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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-03-14 03:09:30 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-03-14 03:09:30 +0300
commit8957ace3159e5369a700a77614493ed6a8a98f93 (patch)
tree98ff5be0caa30cfebb4e0cd0ae2ceaf21ce92eb4
parent232e0a31f1e5d5b3a788dfc3dba8f8d41df36bf9 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/blob/notebook/index.js86
-rw-r--r--app/assets/javascripts/blob/notebook/notebook_viewer.vue81
-rw-r--r--app/controllers/admin/jobs_controller.rb4
-rw-r--r--app/controllers/projects/jobs_controller.rb4
-rw-r--r--app/controllers/projects/pipeline_schedules_controller.rb4
-rw-r--r--app/controllers/projects/pipelines_controller.rb4
-rw-r--r--app/finders/ci/jobs_finder.rb53
-rw-r--r--app/finders/ci/pipeline_schedules_finder.rb28
-rw-r--r--app/finders/ci/pipelines_finder.rb158
-rw-r--r--app/finders/ci/runner_jobs_finder.rb45
-rw-r--r--app/finders/jobs_finder.rb51
-rw-r--r--app/finders/pipeline_schedules_finder.rb26
-rw-r--r--app/finders/pipelines_finder.rb156
-rw-r--r--app/finders/runner_jobs_finder.rb43
-rw-r--r--app/graphql/resolvers/concerns/resolves_pipelines.rb2
-rw-r--r--app/workers/all_queues.yml2
-rw-r--r--app/workers/project_update_repository_storage_worker.rb2
-rw-r--r--changelogs/unreleased/197918-add-package-type-param-to-group-packages-api.yml5
-rw-r--r--changelogs/unreleased/210332-approximate-counters-are-not-working-on-gitlab-com.yml5
-rw-r--r--changelogs/unreleased/fix-prevent-user-theme-color-api-overwrite.yml5
-rw-r--r--changelogs/unreleased/update-puma-to-4-3-3.yml5
-rw-r--r--doc/api/packages.md1
-rw-r--r--lib/api/pipeline_schedules.rb2
-rw-r--r--lib/api/pipelines.rb4
-rw-r--r--lib/api/runners.rb4
-rw-r--r--lib/api/users.rb4
-rw-r--r--lib/gitlab/cache/import/caching.rb153
-rw-r--r--lib/gitlab/github_import.rb2
-rw-r--r--lib/gitlab/github_import/caching.rb151
-rw-r--r--lib/gitlab/github_import/issuable_finder.rb4
-rw-r--r--lib/gitlab/github_import/label_finder.rb4
-rw-r--r--lib/gitlab/github_import/milestone_finder.rb4
-rw-r--r--lib/gitlab/github_import/page_counter.rb4
-rw-r--r--lib/gitlab/github_import/parallel_scheduling.rb6
-rw-r--r--lib/gitlab/github_import/user_finder.rb10
-rw-r--r--lib/gitlab/usage_data.rb17
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/features/projects/blobs/blob_show_spec.rb42
-rw-r--r--spec/finders/ci/jobs_finder_spec.rb (renamed from spec/finders/jobs_finder_spec.rb)2
-rw-r--r--spec/finders/ci/pipeline_schedules_finder_spec.rb (renamed from spec/finders/pipeline_schedules_finder_spec.rb)2
-rw-r--r--spec/finders/ci/pipelines_finder_spec.rb (renamed from spec/finders/pipelines_finder_spec.rb)2
-rw-r--r--spec/finders/ci/runner_jobs_finder_spec.rb (renamed from spec/finders/runner_jobs_finder_spec.rb)2
-rw-r--r--spec/frontend/blob/notebook/notebook_viever_spec.js108
-rw-r--r--spec/frontend/fixtures/static/notebook_viewer.html1
-rw-r--r--spec/javascripts/blob/notebook/index_spec.js130
-rw-r--r--spec/lib/gitlab/cache/import/caching_spec.rb (renamed from spec/lib/gitlab/github_import/caching_spec.rb)2
-rw-r--r--spec/lib/gitlab/github_import/issuable_finder_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/label_finder_spec.rb4
-rw-r--r--spec/lib/gitlab/github_import/milestone_finder_spec.rb4
-rw-r--r--spec/lib/gitlab/github_import/page_counter_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/parallel_scheduling_spec.rb4
-rw-r--r--spec/lib/gitlab/github_import/user_finder_spec.rb22
-rw-r--r--spec/lib/gitlab/github_import_spec.rb2
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb23
-rw-r--r--spec/requests/api/users_spec.rb20
57 files changed, 787 insertions, 735 deletions
diff --git a/Gemfile b/Gemfile
index d48d209356e..1e01266155e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -171,7 +171,7 @@ group :unicorn do
end
group :puma do
- gem 'gitlab-puma', '~> 4.3.1.gitlab.2', require: false
+ gem 'gitlab-puma', '~> 4.3.3.gitlab.2', require: false
gem 'gitlab-puma_worker_killer', '~> 0.1.1.gitlab.1', require: false
gem 'rack-timeout', require: false
end
diff --git a/Gemfile.lock b/Gemfile.lock
index ef8bffdc592..6da9a9a1740 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -391,7 +391,7 @@ GEM
gitlab-mail_room (0.0.3)
gitlab-markup (1.7.0)
gitlab-net-dns (0.9.1)
- gitlab-puma (4.3.1.gitlab.2)
+ gitlab-puma (4.3.3.gitlab.2)
nio4r (~> 2.0)
gitlab-puma_worker_killer (0.1.1.gitlab.1)
get_process_mem (~> 0.2)
@@ -1237,7 +1237,7 @@ DEPENDENCIES
gitlab-mail_room (~> 0.0.3)
gitlab-markup (~> 1.7.0)
gitlab-net-dns (~> 0.9.1)
- gitlab-puma (~> 4.3.1.gitlab.2)
+ gitlab-puma (~> 4.3.3.gitlab.2)
gitlab-puma_worker_killer (~> 0.1.1.gitlab.1)
gitlab-sidekiq-fetcher (= 0.5.2)
gitlab-styles (~> 3.1.0)
diff --git a/app/assets/javascripts/blob/notebook/index.js b/app/assets/javascripts/blob/notebook/index.js
index 35634d63e4a..a8c94b6263e 100644
--- a/app/assets/javascripts/blob/notebook/index.js
+++ b/app/assets/javascripts/blob/notebook/index.js
@@ -1,87 +1,17 @@
-/* eslint-disable no-new */
import Vue from 'vue';
-import axios from '../../lib/utils/axios_utils';
-import notebookLab from '../../notebook/index.vue';
+import NotebookViewer from './notebook_viewer.vue';
export default () => {
const el = document.getElementById('js-notebook-viewer');
- new Vue({
+ return new Vue({
el,
- components: {
- notebookLab,
+ render(createElement) {
+ return createElement(NotebookViewer, {
+ props: {
+ endpoint: el.dataset.endpoint,
+ },
+ });
},
- data() {
- return {
- error: false,
- loadError: false,
- loading: true,
- json: {},
- };
- },
- mounted() {
- if (gon.katex_css_url) {
- const katexStyles = document.createElement('link');
- katexStyles.setAttribute('rel', 'stylesheet');
- katexStyles.setAttribute('href', gon.katex_css_url);
- document.head.appendChild(katexStyles);
- }
-
- if (gon.katex_js_url) {
- const katexScript = document.createElement('script');
- katexScript.addEventListener('load', () => {
- this.loadFile();
- });
- katexScript.setAttribute('src', gon.katex_js_url);
- document.head.appendChild(katexScript);
- } else {
- this.loadFile();
- }
- },
- methods: {
- loadFile() {
- axios
- .get(el.dataset.endpoint)
- .then(res => res.data)
- .then(data => {
- this.json = data;
- this.loading = false;
- })
- .catch(e => {
- if (e.status !== 200) {
- this.loadError = true;
- }
-
- this.error = true;
- });
- },
- },
- template: `
- <div class="container-fluid md prepend-top-default append-bottom-default">
- <div
- class="text-center loading"
- v-if="loading && !error">
- <i
- class="fa fa-spinner fa-spin"
- aria-hidden="true"
- aria-label="iPython notebook loading">
- </i>
- </div>
- <notebook-lab
- v-if="!loading && !error"
- :notebook="json"
- code-css-class="code white" />
- <p
- class="text-center"
- v-if="error">
- <span v-if="loadError">
- An error occurred while loading the file. Please try again later.
- </span>
- <span v-else>
- An error occurred while parsing the file.
- </span>
- </p>
- </div>
- `,
});
};
diff --git a/app/assets/javascripts/blob/notebook/notebook_viewer.vue b/app/assets/javascripts/blob/notebook/notebook_viewer.vue
new file mode 100644
index 00000000000..401fe9beb62
--- /dev/null
+++ b/app/assets/javascripts/blob/notebook/notebook_viewer.vue
@@ -0,0 +1,81 @@
+<script>
+import axios from '~/lib/utils/axios_utils';
+import notebookLab from '~/notebook/index.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
+
+export default {
+ components: {
+ notebookLab,
+ GlLoadingIcon,
+ },
+ props: {
+ endpoint: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ error: false,
+ loadError: false,
+ loading: true,
+ json: {},
+ };
+ },
+ mounted() {
+ if (gon.katex_css_url) {
+ const katexStyles = document.createElement('link');
+ katexStyles.setAttribute('rel', 'stylesheet');
+ katexStyles.setAttribute('href', gon.katex_css_url);
+ document.head.appendChild(katexStyles);
+ }
+
+ if (gon.katex_js_url) {
+ const katexScript = document.createElement('script');
+ katexScript.addEventListener('load', () => {
+ this.loadFile();
+ });
+ katexScript.setAttribute('src', gon.katex_js_url);
+ document.head.appendChild(katexScript);
+ } else {
+ this.loadFile();
+ }
+ },
+ methods: {
+ loadFile() {
+ axios
+ .get(this.endpoint)
+ .then(res => res.data)
+ .then(data => {
+ this.json = data;
+ this.loading = false;
+ })
+ .catch(e => {
+ if (e.status !== 200) {
+ this.loadError = true;
+ }
+ this.error = true;
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <div
+ class="js-notebook-viewer-mounted container-fluid md prepend-top-default append-bottom-default"
+ >
+ <div v-if="loading && !error" class="text-center loading">
+ <gl-loading-icon class="mt-5" size="lg" />
+ </div>
+ <notebook-lab v-if="!loading && !error" :notebook="json" code-css-class="code white" />
+ <p v-if="error" class="text-center">
+ <span v-if="loadError" ref="loadErrorMessage">{{
+ __('An error occurred while loading the file. Please try again later.')
+ }}</span>
+ <span v-else ref="parsingErrorMessage">{{
+ __('An error occurred while parsing the file.')
+ }}</span>
+ </p>
+ </div>
+</template>
diff --git a/app/controllers/admin/jobs_controller.rb b/app/controllers/admin/jobs_controller.rb
index 892f6dc657c..a3a18a115e9 100644
--- a/app/controllers/admin/jobs_controller.rb
+++ b/app/controllers/admin/jobs_controller.rb
@@ -3,10 +3,10 @@
class Admin::JobsController < Admin::ApplicationController
def index
# We need all builds for tabs counters
- @all_builds = JobsFinder.new(current_user: current_user).execute
+ @all_builds = Ci::JobsFinder.new(current_user: current_user).execute
@scope = params[:scope]
- @builds = JobsFinder.new(current_user: current_user, params: params).execute
+ @builds = Ci::JobsFinder.new(current_user: current_user, params: params).execute
@builds = @builds.eager_load_everything
@builds = @builds.page(params[:page]).per(30)
end
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index cb473d6ee96..e0457925b34 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -19,10 +19,10 @@ class Projects::JobsController < Projects::ApplicationController
def index
# We need all builds for tabs counters
- @all_builds = JobsFinder.new(current_user: current_user, project: @project).execute
+ @all_builds = Ci::JobsFinder.new(current_user: current_user, project: @project).execute
@scope = params[:scope]
- @builds = JobsFinder.new(current_user: current_user, project: @project, params: params).execute
+ @builds = Ci::JobsFinder.new(current_user: current_user, project: @project, params: params).execute
@builds = @builds.eager_load_everything
@builds = @builds.page(params[:page]).per(30).without_count
end
diff --git a/app/controllers/projects/pipeline_schedules_controller.rb b/app/controllers/projects/pipeline_schedules_controller.rb
index ead839e8441..e7e8a900060 100644
--- a/app/controllers/projects/pipeline_schedules_controller.rb
+++ b/app/controllers/projects/pipeline_schedules_controller.rb
@@ -13,8 +13,8 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController
# rubocop: disable CodeReuse/ActiveRecord
def index
@scope = params[:scope]
- @all_schedules = PipelineSchedulesFinder.new(@project).execute
- @schedules = PipelineSchedulesFinder.new(@project).execute(scope: params[:scope])
+ @all_schedules = Ci::PipelineSchedulesFinder.new(@project).execute
+ @schedules = Ci::PipelineSchedulesFinder.new(@project).execute(scope: params[:scope])
.includes(:last_pipeline)
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index 6d902e099d9..11b6e5eb66f 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -22,7 +22,7 @@ class Projects::PipelinesController < Projects::ApplicationController
def index
@scope = params[:scope]
- @pipelines = PipelinesFinder
+ @pipelines = Ci::PipelinesFinder
.new(project, current_user, scope: @scope)
.execute
.page(params[:page])
@@ -251,7 +251,7 @@ class Projects::PipelinesController < Projects::ApplicationController
end
def limited_pipelines_count(project, scope = nil)
- finder = PipelinesFinder.new(project, current_user, scope: scope)
+ finder = Ci::PipelinesFinder.new(project, current_user, scope: scope)
view_context.limited_counter_with_delimiter(finder.execute)
end
diff --git a/app/finders/ci/jobs_finder.rb b/app/finders/ci/jobs_finder.rb
new file mode 100644
index 00000000000..2169bf8c53e
--- /dev/null
+++ b/app/finders/ci/jobs_finder.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+module Ci
+ class JobsFinder
+ include Gitlab::Allowable
+
+ def initialize(current_user:, project: nil, params: {})
+ @current_user = current_user
+ @project = project
+ @params = params
+ end
+
+ def execute
+ builds = init_collection.order_id_desc
+ filter_by_scope(builds)
+ rescue Gitlab::Access::AccessDeniedError
+ Ci::Build.none
+ end
+
+ private
+
+ attr_reader :current_user, :project, :params
+
+ def init_collection
+ project ? project_builds : all_builds
+ end
+
+ def all_builds
+ raise Gitlab::Access::AccessDeniedError unless current_user&.admin?
+
+ Ci::Build.all
+ end
+
+ def project_builds
+ raise Gitlab::Access::AccessDeniedError unless can?(current_user, :read_build, project)
+
+ project.builds.relevant
+ end
+
+ def filter_by_scope(builds)
+ case params[:scope]
+ when 'pending'
+ builds.pending.reverse_order
+ when 'running'
+ builds.running.reverse_order
+ when 'finished'
+ builds.finished
+ else
+ builds
+ end
+ end
+ end
+end
diff --git a/app/finders/ci/pipeline_schedules_finder.rb b/app/finders/ci/pipeline_schedules_finder.rb
new file mode 100644
index 00000000000..2544c8c3254
--- /dev/null
+++ b/app/finders/ci/pipeline_schedules_finder.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Ci
+ class PipelineSchedulesFinder
+ attr_reader :project, :pipeline_schedules
+
+ def initialize(project)
+ @project = project
+ @pipeline_schedules = project.pipeline_schedules
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def execute(scope: nil)
+ scoped_schedules =
+ case scope
+ when 'active'
+ pipeline_schedules.active
+ when 'inactive'
+ pipeline_schedules.inactive
+ else
+ pipeline_schedules
+ end
+
+ scoped_schedules.order(id: :desc)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+end
diff --git a/app/finders/ci/pipelines_finder.rb b/app/finders/ci/pipelines_finder.rb
new file mode 100644
index 00000000000..9e71e92b456
--- /dev/null
+++ b/app/finders/ci/pipelines_finder.rb
@@ -0,0 +1,158 @@
+# frozen_string_literal: true
+
+module Ci
+ class PipelinesFinder
+ attr_reader :project, :pipelines, :params, :current_user
+
+ ALLOWED_INDEXED_COLUMNS = %w[id status ref updated_at user_id].freeze
+
+ def initialize(project, current_user, params = {})
+ @project = project
+ @current_user = current_user
+ @pipelines = project.all_pipelines
+ @params = params
+ end
+
+ def execute
+ unless Ability.allowed?(current_user, :read_pipeline, project)
+ return Ci::Pipeline.none
+ end
+
+ items = pipelines.no_child
+ items = by_scope(items)
+ items = by_status(items)
+ items = by_ref(items)
+ items = by_sha(items)
+ items = by_name(items)
+ items = by_username(items)
+ items = by_yaml_errors(items)
+ items = by_updated_at(items)
+ sort_items(items)
+ end
+
+ private
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def ids_for_ref(refs)
+ pipelines.where(ref: refs).group(:ref).select('max(id)')
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def from_ids(ids)
+ pipelines.unscoped.where(project_id: project.id, id: ids)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def branches
+ project.repository.branch_names
+ end
+
+ def tags
+ project.repository.tag_names
+ end
+
+ def by_scope(items)
+ case params[:scope]
+ when 'running'
+ items.running
+ when 'pending'
+ items.pending
+ when 'finished'
+ items.finished
+ when 'branches'
+ from_ids(ids_for_ref(branches))
+ when 'tags'
+ from_ids(ids_for_ref(tags))
+ else
+ items
+ end
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def by_status(items)
+ return items unless HasStatus::AVAILABLE_STATUSES.include?(params[:status])
+
+ items.where(status: params[:status])
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def by_ref(items)
+ if params[:ref].present?
+ items.where(ref: params[:ref])
+ else
+ items
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def by_sha(items)
+ if params[:sha].present?
+ items.where(sha: params[:sha])
+ else
+ items
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def by_name(items)
+ if params[:name].present?
+ items.joins(:user).where(users: { name: params[:name] })
+ else
+ items
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def by_username(items)
+ if params[:username].present?
+ items.joins(:user).where(users: { username: params[:username] })
+ else
+ items
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def by_yaml_errors(items)
+ case Gitlab::Utils.to_boolean(params[:yaml_errors])
+ when true
+ items.where("yaml_errors IS NOT NULL")
+ when false
+ items.where("yaml_errors IS NULL")
+ else
+ items
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def by_updated_at(items)
+ items = items.updated_before(params[:updated_before]) if params[:updated_before].present?
+ items = items.updated_after(params[:updated_after]) if params[:updated_after].present?
+
+ items
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def sort_items(items)
+ order_by = if ALLOWED_INDEXED_COLUMNS.include?(params[:order_by])
+ params[:order_by]
+ else
+ :id
+ end
+
+ sort = if params[:sort] =~ /\A(ASC|DESC)\z/i
+ params[:sort]
+ else
+ :desc
+ end
+
+ items.order(order_by => sort)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+end
diff --git a/app/finders/ci/runner_jobs_finder.rb b/app/finders/ci/runner_jobs_finder.rb
new file mode 100644
index 00000000000..ffcdb407e7e
--- /dev/null
+++ b/app/finders/ci/runner_jobs_finder.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module Ci
+ class RunnerJobsFinder
+ attr_reader :runner, :params
+
+ ALLOWED_INDEXED_COLUMNS = %w[id].freeze
+
+ def initialize(runner, params = {})
+ @runner = runner
+ @params = params
+ end
+
+ def execute
+ items = @runner.builds
+ items = by_status(items)
+ sort_items(items)
+ end
+
+ private
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def by_status(items)
+ return items unless HasStatus::AVAILABLE_STATUSES.include?(params[:status])
+
+ items.where(status: params[:status])
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def sort_items(items)
+ return items unless ALLOWED_INDEXED_COLUMNS.include?(params[:order_by])
+
+ order_by = params[:order_by]
+ sort = if /\A(ASC|DESC)\z/i.match?(params[:sort])
+ params[:sort]
+ else
+ :desc
+ end
+
+ items.order(order_by => sort)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+end
diff --git a/app/finders/jobs_finder.rb b/app/finders/jobs_finder.rb
deleted file mode 100644
index bac18e69618..00000000000
--- a/app/finders/jobs_finder.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-# frozen_string_literal: true
-
-class JobsFinder
- include Gitlab::Allowable
-
- def initialize(current_user:, project: nil, params: {})
- @current_user = current_user
- @project = project
- @params = params
- end
-
- def execute
- builds = init_collection.order_id_desc
- filter_by_scope(builds)
- rescue Gitlab::Access::AccessDeniedError
- Ci::Build.none
- end
-
- private
-
- attr_reader :current_user, :project, :params
-
- def init_collection
- project ? project_builds : all_builds
- end
-
- def all_builds
- raise Gitlab::Access::AccessDeniedError unless current_user&.admin?
-
- Ci::Build.all
- end
-
- def project_builds
- raise Gitlab::Access::AccessDeniedError unless can?(current_user, :read_build, project)
-
- project.builds.relevant
- end
-
- def filter_by_scope(builds)
- case params[:scope]
- when 'pending'
- builds.pending.reverse_order
- when 'running'
- builds.running.reverse_order
- when 'finished'
- builds.finished
- else
- builds
- end
- end
-end
diff --git a/app/finders/pipeline_schedules_finder.rb b/app/finders/pipeline_schedules_finder.rb
deleted file mode 100644
index 3beee608268..00000000000
--- a/app/finders/pipeline_schedules_finder.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# frozen_string_literal: true
-
-class PipelineSchedulesFinder
- attr_reader :project, :pipeline_schedules
-
- def initialize(project)
- @project = project
- @pipeline_schedules = project.pipeline_schedules
- end
-
- # rubocop: disable CodeReuse/ActiveRecord
- def execute(scope: nil)
- scoped_schedules =
- case scope
- when 'active'
- pipeline_schedules.active
- when 'inactive'
- pipeline_schedules.inactive
- else
- pipeline_schedules
- end
-
- scoped_schedules.order(id: :desc)
- end
- # rubocop: enable CodeReuse/ActiveRecord
-end
diff --git a/app/finders/pipelines_finder.rb b/app/finders/pipelines_finder.rb
deleted file mode 100644
index 0599daab564..00000000000
--- a/app/finders/pipelines_finder.rb
+++ /dev/null
@@ -1,156 +0,0 @@
-# frozen_string_literal: true
-
-class PipelinesFinder
- attr_reader :project, :pipelines, :params, :current_user
-
- ALLOWED_INDEXED_COLUMNS = %w[id status ref updated_at user_id].freeze
-
- def initialize(project, current_user, params = {})
- @project = project
- @current_user = current_user
- @pipelines = project.all_pipelines
- @params = params
- end
-
- def execute
- unless Ability.allowed?(current_user, :read_pipeline, project)
- return Ci::Pipeline.none
- end
-
- items = pipelines.no_child
- items = by_scope(items)
- items = by_status(items)
- items = by_ref(items)
- items = by_sha(items)
- items = by_name(items)
- items = by_username(items)
- items = by_yaml_errors(items)
- items = by_updated_at(items)
- sort_items(items)
- end
-
- private
-
- # rubocop: disable CodeReuse/ActiveRecord
- def ids_for_ref(refs)
- pipelines.where(ref: refs).group(:ref).select('max(id)')
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- # rubocop: disable CodeReuse/ActiveRecord
- def from_ids(ids)
- pipelines.unscoped.where(project_id: project.id, id: ids)
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- def branches
- project.repository.branch_names
- end
-
- def tags
- project.repository.tag_names
- end
-
- def by_scope(items)
- case params[:scope]
- when 'running'
- items.running
- when 'pending'
- items.pending
- when 'finished'
- items.finished
- when 'branches'
- from_ids(ids_for_ref(branches))
- when 'tags'
- from_ids(ids_for_ref(tags))
- else
- items
- end
- end
-
- # rubocop: disable CodeReuse/ActiveRecord
- def by_status(items)
- return items unless HasStatus::AVAILABLE_STATUSES.include?(params[:status])
-
- items.where(status: params[:status])
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- # rubocop: disable CodeReuse/ActiveRecord
- def by_ref(items)
- if params[:ref].present?
- items.where(ref: params[:ref])
- else
- items
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- # rubocop: disable CodeReuse/ActiveRecord
- def by_sha(items)
- if params[:sha].present?
- items.where(sha: params[:sha])
- else
- items
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- # rubocop: disable CodeReuse/ActiveRecord
- def by_name(items)
- if params[:name].present?
- items.joins(:user).where(users: { name: params[:name] })
- else
- items
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- # rubocop: disable CodeReuse/ActiveRecord
- def by_username(items)
- if params[:username].present?
- items.joins(:user).where(users: { username: params[:username] })
- else
- items
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- # rubocop: disable CodeReuse/ActiveRecord
- def by_yaml_errors(items)
- case Gitlab::Utils.to_boolean(params[:yaml_errors])
- when true
- items.where("yaml_errors IS NOT NULL")
- when false
- items.where("yaml_errors IS NULL")
- else
- items
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- def by_updated_at(items)
- items = items.updated_before(params[:updated_before]) if params[:updated_before].present?
- items = items.updated_after(params[:updated_after]) if params[:updated_after].present?
-
- items
- end
-
- # rubocop: disable CodeReuse/ActiveRecord
- def sort_items(items)
- order_by = if ALLOWED_INDEXED_COLUMNS.include?(params[:order_by])
- params[:order_by]
- else
- :id
- end
-
- sort = if params[:sort] =~ /\A(ASC|DESC)\z/i
- params[:sort]
- else
- :desc
- end
-
- items.order(order_by => sort)
- end
- # rubocop: enable CodeReuse/ActiveRecord
-end
diff --git a/app/finders/runner_jobs_finder.rb b/app/finders/runner_jobs_finder.rb
deleted file mode 100644
index ef90817416a..00000000000
--- a/app/finders/runner_jobs_finder.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-# frozen_string_literal: true
-
-class RunnerJobsFinder
- attr_reader :runner, :params
-
- ALLOWED_INDEXED_COLUMNS = %w[id].freeze
-
- def initialize(runner, params = {})
- @runner = runner
- @params = params
- end
-
- def execute
- items = @runner.builds
- items = by_status(items)
- sort_items(items)
- end
-
- private
-
- # rubocop: disable CodeReuse/ActiveRecord
- def by_status(items)
- return items unless HasStatus::AVAILABLE_STATUSES.include?(params[:status])
-
- items.where(status: params[:status])
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- # rubocop: disable CodeReuse/ActiveRecord
- def sort_items(items)
- return items unless ALLOWED_INDEXED_COLUMNS.include?(params[:order_by])
-
- order_by = params[:order_by]
- sort = if /\A(ASC|DESC)\z/i.match?(params[:sort])
- params[:sort]
- else
- :desc
- end
-
- items.order(order_by => sort)
- end
- # rubocop: enable CodeReuse/ActiveRecord
-end
diff --git a/app/graphql/resolvers/concerns/resolves_pipelines.rb b/app/graphql/resolvers/concerns/resolves_pipelines.rb
index a6f82cc8505..46d9e174deb 100644
--- a/app/graphql/resolvers/concerns/resolves_pipelines.rb
+++ b/app/graphql/resolvers/concerns/resolves_pipelines.rb
@@ -30,6 +30,6 @@ module ResolvesPipelines
end
def resolve_pipelines(project, params = {})
- PipelinesFinder.new(project, context[:current_user], params).execute
+ Ci::PipelinesFinder.new(project, context[:current_user], params).execute
end
end
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 49dcc441780..9b9b1417e22 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -1166,7 +1166,7 @@
:weight: 1
:idempotent:
- :name: project_update_repository_storage
- :feature_category: :source_code_management
+ :feature_category: :gitaly
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
diff --git a/app/workers/project_update_repository_storage_worker.rb b/app/workers/project_update_repository_storage_worker.rb
index ea2b9e799c0..ecee33e6421 100644
--- a/app/workers/project_update_repository_storage_worker.rb
+++ b/app/workers/project_update_repository_storage_worker.rb
@@ -3,7 +3,7 @@
class ProjectUpdateRepositoryStorageWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
- feature_category :source_code_management
+ feature_category :gitaly
def perform(project_id, new_repository_storage_key)
project = Project.find(project_id)
diff --git a/changelogs/unreleased/197918-add-package-type-param-to-group-packages-api.yml b/changelogs/unreleased/197918-add-package-type-param-to-group-packages-api.yml
new file mode 100644
index 00000000000..ff9eb13bea6
--- /dev/null
+++ b/changelogs/unreleased/197918-add-package-type-param-to-group-packages-api.yml
@@ -0,0 +1,5 @@
+---
+title: Add package_type as a filter option to the group packages list API endpoint
+merge_request: 26833
+author:
+type: added
diff --git a/changelogs/unreleased/210332-approximate-counters-are-not-working-on-gitlab-com.yml b/changelogs/unreleased/210332-approximate-counters-are-not-working-on-gitlab-com.yml
new file mode 100644
index 00000000000..e7b614173ec
--- /dev/null
+++ b/changelogs/unreleased/210332-approximate-counters-are-not-working-on-gitlab-com.yml
@@ -0,0 +1,5 @@
+---
+title: Use batch counters instead of approximate counters in usage data
+merge_request: 27218
+author:
+type: performance
diff --git a/changelogs/unreleased/fix-prevent-user-theme-color-api-overwrite.yml b/changelogs/unreleased/fix-prevent-user-theme-color-api-overwrite.yml
new file mode 100644
index 00000000000..9fd3cb83869
--- /dev/null
+++ b/changelogs/unreleased/fix-prevent-user-theme-color-api-overwrite.yml
@@ -0,0 +1,5 @@
+---
+title: Prevent default overwrite for theme and color ID in user API
+merge_request: 26792
+author: Fabio Huser
+type: fixed
diff --git a/changelogs/unreleased/update-puma-to-4-3-3.yml b/changelogs/unreleased/update-puma-to-4-3-3.yml
new file mode 100644
index 00000000000..f01fa09a417
--- /dev/null
+++ b/changelogs/unreleased/update-puma-to-4-3-3.yml
@@ -0,0 +1,5 @@
+---
+title: Update Puma to 4.3.3
+merge_request: 27232
+author:
+type: security
diff --git a/doc/api/packages.md b/doc/api/packages.md
index b1b6969769c..a29e1f99fd3 100644
--- a/doc/api/packages.md
+++ b/doc/api/packages.md
@@ -67,6 +67,7 @@ GET /groups/:id/packages
| `exclude_subgroups` | boolean | false | If the param is included as true, packages from projects from subgroups are not listed. Default is `false`. |
| `order_by`| string | no | The field to use as order. One of `created_at` (default), `name`, `version`, `type`, or `project_path`. |
| `sort` | string | no | The direction of the order, either `asc` (default) for ascending order or `desc` for descending order. |
+| `package_type` | string | no | Filter the returned packages by type. One of `conan`, `maven`, `npm` or `nuget`. (_Introduced in GitLab 12.9_) |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/:id/packages?exclude_subgroups=true
diff --git a/lib/api/pipeline_schedules.rb b/lib/api/pipeline_schedules.rb
index 445a37a70c0..edc99590cdb 100644
--- a/lib/api/pipeline_schedules.rb
+++ b/lib/api/pipeline_schedules.rb
@@ -22,7 +22,7 @@ module API
get ':id/pipeline_schedules' do
authorize! :read_pipeline_schedule, user_project
- schedules = PipelineSchedulesFinder.new(user_project).execute(scope: params[:scope])
+ schedules = Ci::PipelineSchedulesFinder.new(user_project).execute(scope: params[:scope])
.preload([:owner, :last_pipeline])
present paginate(schedules), with: Entities::PipelineSchedule
end
diff --git a/lib/api/pipelines.rb b/lib/api/pipelines.rb
index 66a183173af..06f8920b37c 100644
--- a/lib/api/pipelines.rb
+++ b/lib/api/pipelines.rb
@@ -27,7 +27,7 @@ module API
optional :username, type: String, desc: 'The username of the user who triggered pipelines'
optional :updated_before, type: DateTime, desc: 'Return pipelines updated before the specified datetime. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ'
optional :updated_after, type: DateTime, desc: 'Return pipelines updated after the specified datetime. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ'
- optional :order_by, type: String, values: PipelinesFinder::ALLOWED_INDEXED_COLUMNS, default: 'id',
+ optional :order_by, type: String, values: Ci::PipelinesFinder::ALLOWED_INDEXED_COLUMNS, default: 'id',
desc: 'Order pipelines'
optional :sort, type: String, values: %w[asc desc], default: 'desc',
desc: 'Sort pipelines'
@@ -36,7 +36,7 @@ module API
authorize! :read_pipeline, user_project
authorize! :read_build, user_project
- pipelines = PipelinesFinder.new(user_project, current_user, params).execute
+ pipelines = Ci::PipelinesFinder.new(user_project, current_user, params).execute
present paginate(pipelines), with: Entities::PipelineBasic
end
diff --git a/lib/api/runners.rb b/lib/api/runners.rb
index c2d371b6867..eba1b5499d0 100644
--- a/lib/api/runners.rb
+++ b/lib/api/runners.rb
@@ -115,7 +115,7 @@ module API
params do
requires :id, type: Integer, desc: 'The ID of the runner'
optional :status, type: String, desc: 'Status of the job', values: Ci::Build::AVAILABLE_STATUSES
- optional :order_by, type: String, desc: 'Order by `id` or not', values: RunnerJobsFinder::ALLOWED_INDEXED_COLUMNS
+ optional :order_by, type: String, desc: 'Order by `id` or not', values: Ci::RunnerJobsFinder::ALLOWED_INDEXED_COLUMNS
optional :sort, type: String, values: %w[asc desc], default: 'desc', desc: 'Sort by asc (ascending) or desc (descending)'
use :pagination
end
@@ -123,7 +123,7 @@ module API
runner = get_runner(params[:id])
authenticate_list_runners_jobs!(runner)
- jobs = RunnerJobsFinder.new(runner, params).execute
+ jobs = Ci::RunnerJobsFinder.new(runner, params).execute
present paginate(jobs), with: Entities::JobBasicWithProject
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index cc13c8aab9e..1ca222b4ed5 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -52,8 +52,8 @@ module API
optional :external, type: Boolean, desc: 'Flag indicating the user is an external user'
# TODO: remove rubocop disable - https://gitlab.com/gitlab-org/gitlab/issues/14960
optional :avatar, type: File, desc: 'Avatar image for user' # rubocop:disable Scalability/FileUploads
- optional :theme_id, type: Integer, default: 1, desc: 'The GitLab theme for the user'
- optional :color_scheme_id, type: Integer, default: 1, desc: 'The color scheme for the file viewer'
+ optional :theme_id, type: Integer, desc: 'The GitLab theme for the user'
+ optional :color_scheme_id, type: Integer, desc: 'The color scheme for the file viewer'
optional :private_profile, type: Boolean, desc: 'Flag indicating the user has a private profile'
all_or_none_of :extern_uid, :provider
diff --git a/lib/gitlab/cache/import/caching.rb b/lib/gitlab/cache/import/caching.rb
new file mode 100644
index 00000000000..ead94761ae7
--- /dev/null
+++ b/lib/gitlab/cache/import/caching.rb
@@ -0,0 +1,153 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Cache
+ module Import
+ module Caching
+ # The default timeout of the cache keys.
+ TIMEOUT = 24.hours.to_i
+
+ WRITE_IF_GREATER_SCRIPT = <<-EOF.strip_heredoc.freeze
+ local key, value, ttl = KEYS[1], tonumber(ARGV[1]), ARGV[2]
+ local existing = tonumber(redis.call("get", key))
+
+ if existing == nil or value > existing then
+ redis.call("set", key, value)
+ redis.call("expire", key, ttl)
+ return true
+ else
+ return false
+ end
+ EOF
+
+ # Reads a cache key.
+ #
+ # If the key exists and has a non-empty value its TTL is refreshed
+ # automatically.
+ #
+ # raw_key - The cache key to read.
+ # timeout - The new timeout of the key if the key is to be refreshed.
+ def self.read(raw_key, timeout: TIMEOUT)
+ key = cache_key_for(raw_key)
+ value = Redis::Cache.with { |redis| redis.get(key) }
+
+ if value.present?
+ # We refresh the expiration time so frequently used keys stick
+ # around, removing the need for querying the database as much as
+ # possible.
+ #
+ # A key may be empty when we looked up a GitHub user (for example) but
+ # did not find a matching GitLab user. In that case we _don't_ want to
+ # refresh the TTL so we automatically pick up the right data when said
+ # user were to register themselves on the GitLab instance.
+ Redis::Cache.with { |redis| redis.expire(key, timeout) }
+ end
+
+ value
+ end
+
+ # Reads an integer from the cache, or returns nil if no value was found.
+ #
+ # See Caching.read for more information.
+ def self.read_integer(raw_key, timeout: TIMEOUT)
+ value = read(raw_key, timeout: timeout)
+
+ value.to_i if value.present?
+ end
+
+ # Sets a cache key to the given value.
+ #
+ # key - The cache key to write.
+ # value - The value to set.
+ # timeout - The time after which the cache key should expire.
+ def self.write(raw_key, value, timeout: TIMEOUT)
+ key = cache_key_for(raw_key)
+
+ Redis::Cache.with do |redis|
+ redis.set(key, value, ex: timeout)
+ end
+
+ value
+ end
+
+ # Adds a value to a set.
+ #
+ # raw_key - The key of the set to add the value to.
+ # value - The value to add to the set.
+ # timeout - The new timeout of the key.
+ def self.set_add(raw_key, value, timeout: TIMEOUT)
+ key = cache_key_for(raw_key)
+
+ Redis::Cache.with do |redis|
+ redis.multi do |m|
+ m.sadd(key, value)
+ m.expire(key, timeout)
+ end
+ end
+ end
+
+ # Returns true if the given value is present in the set.
+ #
+ # raw_key - The key of the set to check.
+ # value - The value to check for.
+ def self.set_includes?(raw_key, value)
+ key = cache_key_for(raw_key)
+
+ Redis::Cache.with do |redis|
+ redis.sismember(key, value)
+ end
+ end
+
+ # Sets multiple keys to a given value.
+ #
+ # mapping - A Hash mapping the cache keys to their values.
+ # timeout - The time after which the cache key should expire.
+ def self.write_multiple(mapping, timeout: TIMEOUT)
+ Redis::Cache.with do |redis|
+ redis.multi do |multi|
+ mapping.each do |raw_key, value|
+ multi.set(cache_key_for(raw_key), value, ex: timeout)
+ end
+ end
+ end
+ end
+
+ # Sets the expiration time of a key.
+ #
+ # raw_key - The key for which to change the timeout.
+ # timeout - The new timeout.
+ def self.expire(raw_key, timeout)
+ key = cache_key_for(raw_key)
+
+ Redis::Cache.with do |redis|
+ redis.expire(key, timeout)
+ end
+ end
+
+ # Sets a key to the given integer but only if the existing value is
+ # smaller than the given value.
+ #
+ # This method uses a Lua script to ensure the read and write are atomic.
+ #
+ # raw_key - The key to set.
+ # value - The new value for the key.
+ # timeout - The key timeout in seconds.
+ #
+ # Returns true when the key was overwritten, false otherwise.
+ def self.write_if_greater(raw_key, value, timeout: TIMEOUT)
+ key = cache_key_for(raw_key)
+ val = Redis::Cache.with do |redis|
+ redis
+ .eval(WRITE_IF_GREATER_SCRIPT, keys: [key], argv: [value, timeout])
+ end
+
+ val ? true : false
+ end
+
+ def self.cache_key_for(raw_key)
+ "#{Redis::Cache::CACHE_NAMESPACE}:#{raw_key}"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import.rb b/lib/gitlab/github_import.rb
index 14a6d6443ec..9a7c406d981 100644
--- a/lib/gitlab/github_import.rb
+++ b/lib/gitlab/github_import.rb
@@ -16,7 +16,7 @@ module Gitlab
def self.ghost_user_id
key = 'github-import/ghost-user-id'
- Caching.read_integer(key) || Caching.write(key, User.select(:id).ghost.id)
+ Gitlab::Cache::Import::Caching.read_integer(key) || Gitlab::Cache::Import::Caching.write(key, User.select(:id).ghost.id)
end
end
end
diff --git a/lib/gitlab/github_import/caching.rb b/lib/gitlab/github_import/caching.rb
deleted file mode 100644
index b08f133794f..00000000000
--- a/lib/gitlab/github_import/caching.rb
+++ /dev/null
@@ -1,151 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module GithubImport
- module Caching
- # The default timeout of the cache keys.
- TIMEOUT = 24.hours.to_i
-
- WRITE_IF_GREATER_SCRIPT = <<-EOF.strip_heredoc.freeze
- local key, value, ttl = KEYS[1], tonumber(ARGV[1]), ARGV[2]
- local existing = tonumber(redis.call("get", key))
-
- if existing == nil or value > existing then
- redis.call("set", key, value)
- redis.call("expire", key, ttl)
- return true
- else
- return false
- end
- EOF
-
- # Reads a cache key.
- #
- # If the key exists and has a non-empty value its TTL is refreshed
- # automatically.
- #
- # raw_key - The cache key to read.
- # timeout - The new timeout of the key if the key is to be refreshed.
- def self.read(raw_key, timeout: TIMEOUT)
- key = cache_key_for(raw_key)
- value = Redis::Cache.with { |redis| redis.get(key) }
-
- if value.present?
- # We refresh the expiration time so frequently used keys stick
- # around, removing the need for querying the database as much as
- # possible.
- #
- # A key may be empty when we looked up a GitHub user (for example) but
- # did not find a matching GitLab user. In that case we _don't_ want to
- # refresh the TTL so we automatically pick up the right data when said
- # user were to register themselves on the GitLab instance.
- Redis::Cache.with { |redis| redis.expire(key, timeout) }
- end
-
- value
- end
-
- # Reads an integer from the cache, or returns nil if no value was found.
- #
- # See Caching.read for more information.
- def self.read_integer(raw_key, timeout: TIMEOUT)
- value = read(raw_key, timeout: timeout)
-
- value.to_i if value.present?
- end
-
- # Sets a cache key to the given value.
- #
- # key - The cache key to write.
- # value - The value to set.
- # timeout - The time after which the cache key should expire.
- def self.write(raw_key, value, timeout: TIMEOUT)
- key = cache_key_for(raw_key)
-
- Redis::Cache.with do |redis|
- redis.set(key, value, ex: timeout)
- end
-
- value
- end
-
- # Adds a value to a set.
- #
- # raw_key - The key of the set to add the value to.
- # value - The value to add to the set.
- # timeout - The new timeout of the key.
- def self.set_add(raw_key, value, timeout: TIMEOUT)
- key = cache_key_for(raw_key)
-
- Redis::Cache.with do |redis|
- redis.multi do |m|
- m.sadd(key, value)
- m.expire(key, timeout)
- end
- end
- end
-
- # Returns true if the given value is present in the set.
- #
- # raw_key - The key of the set to check.
- # value - The value to check for.
- def self.set_includes?(raw_key, value)
- key = cache_key_for(raw_key)
-
- Redis::Cache.with do |redis|
- redis.sismember(key, value)
- end
- end
-
- # Sets multiple keys to a given value.
- #
- # mapping - A Hash mapping the cache keys to their values.
- # timeout - The time after which the cache key should expire.
- def self.write_multiple(mapping, timeout: TIMEOUT)
- Redis::Cache.with do |redis|
- redis.multi do |multi|
- mapping.each do |raw_key, value|
- multi.set(cache_key_for(raw_key), value, ex: timeout)
- end
- end
- end
- end
-
- # Sets the expiration time of a key.
- #
- # raw_key - The key for which to change the timeout.
- # timeout - The new timeout.
- def self.expire(raw_key, timeout)
- key = cache_key_for(raw_key)
-
- Redis::Cache.with do |redis|
- redis.expire(key, timeout)
- end
- end
-
- # Sets a key to the given integer but only if the existing value is
- # smaller than the given value.
- #
- # This method uses a Lua script to ensure the read and write are atomic.
- #
- # raw_key - The key to set.
- # value - The new value for the key.
- # timeout - The key timeout in seconds.
- #
- # Returns true when the key was overwritten, false otherwise.
- def self.write_if_greater(raw_key, value, timeout: TIMEOUT)
- key = cache_key_for(raw_key)
- val = Redis::Cache.with do |redis|
- redis
- .eval(WRITE_IF_GREATER_SCRIPT, keys: [key], argv: [value, timeout])
- end
-
- val ? true : false
- end
-
- def self.cache_key_for(raw_key)
- "#{Redis::Cache::CACHE_NAMESPACE}:#{raw_key}"
- end
- end
- end
-end
diff --git a/lib/gitlab/github_import/issuable_finder.rb b/lib/gitlab/github_import/issuable_finder.rb
index c81603a1aa9..136531505ea 100644
--- a/lib/gitlab/github_import/issuable_finder.rb
+++ b/lib/gitlab/github_import/issuable_finder.rb
@@ -23,7 +23,7 @@ module Gitlab
#
# This method will return `nil` if no ID could be found.
def database_id
- val = Caching.read(cache_key)
+ val = Gitlab::Cache::Import::Caching.read(cache_key)
val.to_i if val.present?
end
@@ -32,7 +32,7 @@ module Gitlab
#
# database_id - The ID of the corresponding database row.
def cache_database_id(database_id)
- Caching.write(cache_key, database_id)
+ Gitlab::Cache::Import::Caching.write(cache_key, database_id)
end
private
diff --git a/lib/gitlab/github_import/label_finder.rb b/lib/gitlab/github_import/label_finder.rb
index cad39e48e43..39e669dbba4 100644
--- a/lib/gitlab/github_import/label_finder.rb
+++ b/lib/gitlab/github_import/label_finder.rb
@@ -15,7 +15,7 @@ module Gitlab
# Returns the label ID for the given name.
def id_for(name)
- Caching.read_integer(cache_key_for(name))
+ Gitlab::Cache::Import::Caching.read_integer(cache_key_for(name))
end
# rubocop: disable CodeReuse/ActiveRecord
@@ -27,7 +27,7 @@ module Gitlab
hash[cache_key_for(name)] = id
end
- Caching.write_multiple(mapping)
+ Gitlab::Cache::Import::Caching.write_multiple(mapping)
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/gitlab/github_import/milestone_finder.rb b/lib/gitlab/github_import/milestone_finder.rb
index a157a1e1ff5..d9290e36ea1 100644
--- a/lib/gitlab/github_import/milestone_finder.rb
+++ b/lib/gitlab/github_import/milestone_finder.rb
@@ -18,7 +18,7 @@ module Gitlab
def id_for(issuable)
return unless issuable.milestone_number
- Caching.read_integer(cache_key_for(issuable.milestone_number))
+ Gitlab::Cache::Import::Caching.read_integer(cache_key_for(issuable.milestone_number))
end
# rubocop: disable CodeReuse/ActiveRecord
@@ -30,7 +30,7 @@ module Gitlab
hash[cache_key_for(iid)] = id
end
- Caching.write_multiple(mapping)
+ Gitlab::Cache::Import::Caching.write_multiple(mapping)
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/gitlab/github_import/page_counter.rb b/lib/gitlab/github_import/page_counter.rb
index a3e7b3c1afc..3b4fd42ba2a 100644
--- a/lib/gitlab/github_import/page_counter.rb
+++ b/lib/gitlab/github_import/page_counter.rb
@@ -19,12 +19,12 @@ module Gitlab
#
# Returns true if the page number was overwritten, false otherwise.
def set(page)
- Caching.write_if_greater(cache_key, page)
+ Gitlab::Cache::Import::Caching.write_if_greater(cache_key, page)
end
# Returns the current value from the cache.
def current
- Caching.read_integer(cache_key) || 1
+ Gitlab::Cache::Import::Caching.read_integer(cache_key) || 1
end
end
end
diff --git a/lib/gitlab/github_import/parallel_scheduling.rb b/lib/gitlab/github_import/parallel_scheduling.rb
index 849a66d47ed..cabc615ea11 100644
--- a/lib/gitlab/github_import/parallel_scheduling.rb
+++ b/lib/gitlab/github_import/parallel_scheduling.rb
@@ -42,7 +42,7 @@ module Gitlab
# still scheduling duplicates while. Since all work has already been
# completed those jobs will just cycle through any remaining pages while
# not scheduling anything.
- Caching.expire(already_imported_cache_key, 15.minutes.to_i)
+ Gitlab::Cache::Import::Caching.expire(already_imported_cache_key, 15.minutes.to_i)
retval
end
@@ -112,14 +112,14 @@ module Gitlab
def already_imported?(object)
id = id_for_already_imported_cache(object)
- Caching.set_includes?(already_imported_cache_key, id)
+ Gitlab::Cache::Import::Caching.set_includes?(already_imported_cache_key, id)
end
# Marks the given object as "already imported".
def mark_as_imported(object)
id = id_for_already_imported_cache(object)
- Caching.set_add(already_imported_cache_key, id)
+ Gitlab::Cache::Import::Caching.set_add(already_imported_cache_key, id)
end
# Returns the ID to use for the cache used for checking if an object has
diff --git a/lib/gitlab/github_import/user_finder.rb b/lib/gitlab/github_import/user_finder.rb
index 51a532437bd..9da986ae921 100644
--- a/lib/gitlab/github_import/user_finder.rb
+++ b/lib/gitlab/github_import/user_finder.rb
@@ -102,11 +102,11 @@ module Gitlab
def email_for_github_username(username)
cache_key = EMAIL_FOR_USERNAME_CACHE_KEY % username
- email = Caching.read(cache_key)
+ email = Gitlab::Cache::Import::Caching.read(cache_key)
unless email
user = client.user(username)
- email = Caching.write(cache_key, user.email) if user
+ email = Gitlab::Cache::Import::Caching.write(cache_key, user.email) if user
end
email
@@ -125,7 +125,7 @@ module Gitlab
def id_for_github_id(id)
gitlab_id = query_id_for_github_id(id) || nil
- Caching.write(ID_CACHE_KEY % id, gitlab_id)
+ Gitlab::Cache::Import::Caching.write(ID_CACHE_KEY % id, gitlab_id)
end
# Queries and caches the GitLab user ID for a GitHub email, if one was
@@ -133,7 +133,7 @@ module Gitlab
def id_for_github_email(email)
gitlab_id = query_id_for_github_email(email) || nil
- Caching.write(ID_FOR_EMAIL_CACHE_KEY % email, gitlab_id)
+ Gitlab::Cache::Import::Caching.write(ID_FOR_EMAIL_CACHE_KEY % email, gitlab_id)
end
# rubocop: disable CodeReuse/ActiveRecord
@@ -155,7 +155,7 @@ module Gitlab
# 1. A boolean indicating if the key was present or not.
# 2. The ID as an Integer, or nil in case no ID could be found.
def read_id_from_cache(key)
- value = Caching.read(key)
+ value = Gitlab::Cache::Import::Caching.read(key)
exists = !value.nil?
number = value.to_i
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index 35c69099b01..c69c49831fa 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -2,7 +2,6 @@
module Gitlab
class UsageData
- APPROXIMATE_COUNT_MODELS = [Label, MergeRequest, Note, Todo].freeze
BATCH_SIZE = 100
class << self
@@ -107,10 +106,12 @@ module Gitlab
suggestions: count(Suggestion),
todos: count(Todo),
uploads: count(Upload),
- web_hooks: count(WebHook)
+ web_hooks: count(WebHook),
+ labels: count(Label),
+ merge_requests: count(MergeRequest),
+ notes: count(Note)
}.merge(
services_usage,
- approximate_counts,
usage_counters,
user_preferences_usage,
ingress_modsecurity_usage
@@ -251,16 +252,6 @@ module Gitlab
fallback
end
- def approximate_counts
- approx_counts = Gitlab::Database::Count.approximate_counts(APPROXIMATE_COUNT_MODELS)
-
- APPROXIMATE_COUNT_MODELS.each_with_object({}) do |model, result|
- key = model.name.underscore.pluralize.to_sym
-
- result[key] = approx_counts[model] || -1
- end
- end
-
def installation_type
if Rails.env.production?
Gitlab::INSTALLATION_TYPE
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 1bbb8be6af5..a431612bfe7 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1948,6 +1948,9 @@ msgstr ""
msgid "An error occurred while parsing recent searches"
msgstr ""
+msgid "An error occurred while parsing the file."
+msgstr ""
+
msgid "An error occurred while removing epics."
msgstr ""
diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb
index 0ff3e45c956..9fc70412975 100644
--- a/spec/features/projects/blobs/blob_show_spec.rb
+++ b/spec/features/projects/blobs/blob_show_spec.rb
@@ -308,6 +308,48 @@ describe 'File blob', :js do
end
end
+ context 'Jupiter Notebook file' do
+ before do
+ project.add_maintainer(project.creator)
+
+ Files::CreateService.new(
+ project,
+ project.creator,
+ start_branch: 'master',
+ branch_name: 'master',
+ commit_message: "Add Jupiter Notebook",
+ file_path: 'files/basic.ipynb',
+ file_content: project.repository.blob_at('add-ipython-files', 'files/ipython/basic.ipynb').data
+ ).execute
+
+ visit_blob('files/basic.ipynb')
+
+ wait_for_requests
+ end
+
+ it 'displays the blob' do
+ aggregate_failures do
+ # shows rendered notebook
+ expect(page).to have_selector('.js-notebook-viewer-mounted')
+
+ # does show a viewer switcher
+ expect(page).to have_selector('.js-blob-viewer-switcher')
+
+ # show a disabled copy button
+ expect(page).to have_selector('.js-copy-blob-source-btn.disabled')
+
+ # shows a raw button
+ expect(page).to have_link('Open raw')
+
+ # shows a download button
+ expect(page).to have_link('Download')
+
+ # shows the rendered notebook
+ expect(page).to have_content('test')
+ end
+ end
+ end
+
context 'ISO file (stored in LFS)' do
context 'when LFS is enabled on the project' do
before do
diff --git a/spec/finders/jobs_finder_spec.rb b/spec/finders/ci/jobs_finder_spec.rb
index 01f9ec03c79..7083e8fbf43 100644
--- a/spec/finders/jobs_finder_spec.rb
+++ b/spec/finders/ci/jobs_finder_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe JobsFinder, '#execute' do
+describe Ci::JobsFinder, '#execute' do
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:user, :admin) }
let_it_be(:project) { create(:project, :private, public_builds: false) }
diff --git a/spec/finders/pipeline_schedules_finder_spec.rb b/spec/finders/ci/pipeline_schedules_finder_spec.rb
index 8d0bde15e03..5b5154ce834 100644
--- a/spec/finders/pipeline_schedules_finder_spec.rb
+++ b/spec/finders/ci/pipeline_schedules_finder_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PipelineSchedulesFinder do
+describe Ci::PipelineSchedulesFinder do
let(:project) { create(:project) }
let!(:active_schedule) { create(:ci_pipeline_schedule, project: project) }
diff --git a/spec/finders/pipelines_finder_spec.rb b/spec/finders/ci/pipelines_finder_spec.rb
index 1dbf9491118..6528093731e 100644
--- a/spec/finders/pipelines_finder_spec.rb
+++ b/spec/finders/ci/pipelines_finder_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PipelinesFinder do
+describe Ci::PipelinesFinder do
let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
let(:params) { {} }
diff --git a/spec/finders/runner_jobs_finder_spec.rb b/spec/finders/ci/runner_jobs_finder_spec.rb
index c11f9182036..a3245119291 100644
--- a/spec/finders/runner_jobs_finder_spec.rb
+++ b/spec/finders/ci/runner_jobs_finder_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe RunnerJobsFinder do
+describe Ci::RunnerJobsFinder do
let(:project) { create(:project) }
let(:runner) { create(:ci_runner, :instance) }
diff --git a/spec/frontend/blob/notebook/notebook_viever_spec.js b/spec/frontend/blob/notebook/notebook_viever_spec.js
new file mode 100644
index 00000000000..535d2bd544a
--- /dev/null
+++ b/spec/frontend/blob/notebook/notebook_viever_spec.js
@@ -0,0 +1,108 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlLoadingIcon } from '@gitlab/ui';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import component from '~/blob/notebook/notebook_viewer.vue';
+import NotebookLab from '~/notebook/index.vue';
+import waitForPromises from 'helpers/wait_for_promises';
+
+describe('iPython notebook renderer', () => {
+ let wrapper;
+ let mock;
+
+ const endpoint = 'test';
+ const mockNotebook = {
+ cells: [
+ {
+ cell_type: 'markdown',
+ source: ['# test'],
+ },
+ {
+ cell_type: 'code',
+ execution_count: 1,
+ source: ['def test(str)', ' return str'],
+ outputs: [],
+ },
+ ],
+ };
+
+ const mountComponent = () => {
+ wrapper = shallowMount(component, { propsData: { endpoint } });
+ };
+
+ const findLoading = () => wrapper.find(GlLoadingIcon);
+ const findNotebookLab = () => wrapper.find(NotebookLab);
+ const findLoadErrorMessage = () => wrapper.find({ ref: 'loadErrorMessage' });
+ const findParseErrorMessage = () => wrapper.find({ ref: 'parsingErrorMessage' });
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ mock.restore();
+ });
+
+ it('shows loading icon', () => {
+ mock.onGet(endpoint).reply(() => new Promise(() => {}));
+ mountComponent({ loadFile: jest.fn() });
+ expect(findLoading().exists()).toBe(true);
+ });
+
+ describe('successful response', () => {
+ beforeEach(() => {
+ mock.onGet(endpoint).reply(200, mockNotebook);
+ mountComponent();
+ return waitForPromises();
+ });
+
+ it('does not show loading icon', () => {
+ expect(findLoading().exists()).toBe(false);
+ });
+
+ it('renders the notebook', () => {
+ expect(findNotebookLab().exists()).toBe(true);
+ });
+ });
+
+ describe('error in JSON response', () => {
+ beforeEach(() => {
+ mock.onGet(endpoint).reply(() =>
+ // eslint-disable-next-line prefer-promise-reject-errors
+ Promise.reject({ status: 200 }),
+ );
+
+ mountComponent();
+ return waitForPromises();
+ });
+
+ it('does not show loading icon', () => {
+ expect(findLoading().exists()).toBe(false);
+ });
+
+ it('shows error message', () => {
+ expect(findParseErrorMessage().text()).toEqual('An error occurred while parsing the file.');
+ });
+ });
+
+ describe('error getting file', () => {
+ beforeEach(() => {
+ mock.onGet(endpoint).reply(500, '');
+
+ mountComponent();
+ return waitForPromises();
+ });
+
+ it('does not show loading icon', () => {
+ expect(findLoading().exists()).toBe(false);
+ });
+
+ it('shows error message', () => {
+ expect(findLoadErrorMessage().text()).toEqual(
+ 'An error occurred while loading the file. Please try again later.',
+ );
+ });
+ });
+});
diff --git a/spec/frontend/fixtures/static/notebook_viewer.html b/spec/frontend/fixtures/static/notebook_viewer.html
deleted file mode 100644
index 4bbb7bf1094..00000000000
--- a/spec/frontend/fixtures/static/notebook_viewer.html
+++ /dev/null
@@ -1 +0,0 @@
-<div class="file-content" data-endpoint="/test" id="js-notebook-viewer"></div>
diff --git a/spec/javascripts/blob/notebook/index_spec.js b/spec/javascripts/blob/notebook/index_spec.js
deleted file mode 100644
index db6ca5bd22d..00000000000
--- a/spec/javascripts/blob/notebook/index_spec.js
+++ /dev/null
@@ -1,130 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-import axios from '~/lib/utils/axios_utils';
-import renderNotebook from '~/blob/notebook';
-
-describe('iPython notebook renderer', () => {
- preloadFixtures('static/notebook_viewer.html');
-
- beforeEach(() => {
- loadFixtures('static/notebook_viewer.html');
- });
-
- it('shows loading icon', () => {
- renderNotebook();
-
- expect(document.querySelector('.loading')).not.toBeNull();
- });
-
- describe('successful response', () => {
- let mock;
-
- beforeEach(done => {
- mock = new MockAdapter(axios);
- mock.onGet('/test').reply(200, {
- cells: [
- {
- cell_type: 'markdown',
- source: ['# test'],
- },
- {
- cell_type: 'code',
- execution_count: 1,
- source: ['def test(str)', ' return str'],
- outputs: [],
- },
- ],
- });
-
- renderNotebook();
-
- setTimeout(() => {
- done();
- });
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- it('does not show loading icon', () => {
- expect(document.querySelector('.loading')).toBeNull();
- });
-
- it('renders the notebook', () => {
- expect(document.querySelector('.md')).not.toBeNull();
- });
-
- it('renders the markdown cell', () => {
- expect(document.querySelector('h1')).not.toBeNull();
-
- expect(document.querySelector('h1').textContent.trim()).toBe('test');
- });
-
- it('highlights code', () => {
- expect(document.querySelector('.token')).not.toBeNull();
-
- expect(document.querySelector('.language-python')).not.toBeNull();
- });
- });
-
- describe('error in JSON response', () => {
- let mock;
-
- beforeEach(done => {
- mock = new MockAdapter(axios);
- mock.onGet('/test').reply(() =>
- // eslint-disable-next-line prefer-promise-reject-errors
- Promise.reject({ status: 200, data: '{ "cells": [{"cell_type": "markdown"} }' }),
- );
-
- renderNotebook();
-
- setTimeout(() => {
- done();
- });
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- it('does not show loading icon', () => {
- expect(document.querySelector('.loading')).toBeNull();
- });
-
- it('shows error message', () => {
- expect(document.querySelector('.md').textContent.trim()).toBe(
- 'An error occurred while parsing the file.',
- );
- });
- });
-
- describe('error getting file', () => {
- let mock;
-
- beforeEach(done => {
- mock = new MockAdapter(axios);
- mock.onGet('/test').reply(500, '');
-
- renderNotebook();
-
- setTimeout(() => {
- done();
- });
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- it('does not show loading icon', () => {
- expect(document.querySelector('.loading')).toBeNull();
- });
-
- it('shows error message', () => {
- expect(document.querySelector('.md').textContent.trim()).toBe(
- 'An error occurred while loading the file. Please try again later.',
- );
- });
- });
-});
diff --git a/spec/lib/gitlab/github_import/caching_spec.rb b/spec/lib/gitlab/cache/import/caching_spec.rb
index 18c3e382532..e4aec0f4dec 100644
--- a/spec/lib/gitlab/github_import/caching_spec.rb
+++ b/spec/lib/gitlab/cache/import/caching_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Gitlab::GithubImport::Caching, :clean_gitlab_redis_cache do
+describe Gitlab::Cache::Import::Caching, :clean_gitlab_redis_cache do
describe '.read' do
it 'reads a value from the cache' do
described_class.write('foo', 'bar')
diff --git a/spec/lib/gitlab/github_import/issuable_finder_spec.rb b/spec/lib/gitlab/github_import/issuable_finder_spec.rb
index b8a6feb6c73..55add863d43 100644
--- a/spec/lib/gitlab/github_import/issuable_finder_spec.rb
+++ b/spec/lib/gitlab/github_import/issuable_finder_spec.rb
@@ -30,7 +30,7 @@ describe Gitlab::GithubImport::IssuableFinder, :clean_gitlab_redis_cache do
describe '#cache_database_id' do
it 'caches the ID of a database row' do
- expect(Gitlab::GithubImport::Caching)
+ expect(Gitlab::Cache::Import::Caching)
.to receive(:write)
.with('github-import/issuable-finder/4/MergeRequest/1', 10)
diff --git a/spec/lib/gitlab/github_import/label_finder_spec.rb b/spec/lib/gitlab/github_import/label_finder_spec.rb
index 039ae27ad57..bb946a15a2d 100644
--- a/spec/lib/gitlab/github_import/label_finder_spec.rb
+++ b/spec/lib/gitlab/github_import/label_finder_spec.rb
@@ -21,7 +21,7 @@ describe Gitlab::GithubImport::LabelFinder, :clean_gitlab_redis_cache do
it 'returns nil for an empty cache key' do
key = finder.cache_key_for(bug.name)
- Gitlab::GithubImport::Caching.write(key, '')
+ Gitlab::Cache::Import::Caching.write(key, '')
expect(finder.id_for(bug.name)).to be_nil
end
@@ -40,7 +40,7 @@ describe Gitlab::GithubImport::LabelFinder, :clean_gitlab_redis_cache do
describe '#build_cache' do
it 'builds the cache of all project labels' do
- expect(Gitlab::GithubImport::Caching)
+ expect(Gitlab::Cache::Import::Caching)
.to receive(:write_multiple)
.with(
{
diff --git a/spec/lib/gitlab/github_import/milestone_finder_spec.rb b/spec/lib/gitlab/github_import/milestone_finder_spec.rb
index 407e2e67ec9..ecb533b7e39 100644
--- a/spec/lib/gitlab/github_import/milestone_finder_spec.rb
+++ b/spec/lib/gitlab/github_import/milestone_finder_spec.rb
@@ -22,7 +22,7 @@ describe Gitlab::GithubImport::MilestoneFinder, :clean_gitlab_redis_cache do
it 'returns nil for an empty cache key' do
key = finder.cache_key_for(milestone.iid)
- Gitlab::GithubImport::Caching.write(key, '')
+ Gitlab::Cache::Import::Caching.write(key, '')
expect(finder.id_for(issuable)).to be_nil
end
@@ -41,7 +41,7 @@ describe Gitlab::GithubImport::MilestoneFinder, :clean_gitlab_redis_cache do
describe '#build_cache' do
it 'builds the cache of all project milestones' do
- expect(Gitlab::GithubImport::Caching)
+ expect(Gitlab::Cache::Import::Caching)
.to receive(:write_multiple)
.with("github-import/milestone-finder/#{project.id}/1" => milestone.id)
.and_call_original
diff --git a/spec/lib/gitlab/github_import/page_counter_spec.rb b/spec/lib/gitlab/github_import/page_counter_spec.rb
index 87f3ce45fd3..95125c9c22f 100644
--- a/spec/lib/gitlab/github_import/page_counter_spec.rb
+++ b/spec/lib/gitlab/github_import/page_counter_spec.rb
@@ -12,7 +12,7 @@ describe Gitlab::GithubImport::PageCounter, :clean_gitlab_redis_cache do
end
it 'sets the initial page number to the cached value when one is present' do
- Gitlab::GithubImport::Caching.write(counter.cache_key, 2)
+ Gitlab::Cache::Import::Caching.write(counter.cache_key, 2)
expect(described_class.new(project, :issues).current).to eq(2)
end
diff --git a/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb b/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb
index f4d107e3dce..a6ae99b395c 100644
--- a/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb
+++ b/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb
@@ -57,7 +57,7 @@ describe Gitlab::GithubImport::ParallelScheduling do
expect(importer).to receive(:parallel_import)
- expect(Gitlab::GithubImport::Caching)
+ expect(Gitlab::Cache::Import::Caching)
.to receive(:expire)
.with(importer.already_imported_cache_key, a_kind_of(Numeric))
@@ -287,7 +287,7 @@ describe Gitlab::GithubImport::ParallelScheduling do
.with(object)
.and_return(object.id)
- expect(Gitlab::GithubImport::Caching)
+ expect(Gitlab::Cache::Import::Caching)
.to receive(:set_add)
.with(importer.already_imported_cache_key, object.id)
.and_call_original
diff --git a/spec/lib/gitlab/github_import/user_finder_spec.rb b/spec/lib/gitlab/github_import/user_finder_spec.rb
index 74b5c1c52cd..8764ebef32b 100644
--- a/spec/lib/gitlab/github_import/user_finder_spec.rb
+++ b/spec/lib/gitlab/github_import/user_finder_spec.rb
@@ -162,7 +162,7 @@ describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
context 'when an Email address is cached' do
it 'reads the Email address from the cache' do
- expect(Gitlab::GithubImport::Caching)
+ expect(Gitlab::Cache::Import::Caching)
.to receive(:read)
.and_return(email)
@@ -182,7 +182,7 @@ describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
it 'caches the Email address when an Email address is available' do
expect(client).to receive(:user).with('kittens').and_return(user)
- expect(Gitlab::GithubImport::Caching)
+ expect(Gitlab::Cache::Import::Caching)
.to receive(:write)
.with(an_instance_of(String), email)
@@ -195,7 +195,7 @@ describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
.with('kittens')
.and_return(nil)
- expect(Gitlab::GithubImport::Caching)
+ expect(Gitlab::Cache::Import::Caching)
.not_to receive(:write)
expect(finder.email_for_github_username('kittens')).to be_nil
@@ -207,7 +207,7 @@ describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
let(:id) { 4 }
it 'reads a user ID from the cache' do
- Gitlab::GithubImport::Caching
+ Gitlab::Cache::Import::Caching
.write(described_class::ID_CACHE_KEY % id, 4)
expect(finder.cached_id_for_github_id(id)).to eq([true, 4])
@@ -222,7 +222,7 @@ describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
let(:email) { 'kittens@example.com' }
it 'reads a user ID from the cache' do
- Gitlab::GithubImport::Caching
+ Gitlab::Cache::Import::Caching
.write(described_class::ID_FOR_EMAIL_CACHE_KEY % email, 4)
expect(finder.cached_id_for_github_email(email)).to eq([true, 4])
@@ -241,7 +241,7 @@ describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
.with(id)
.and_return(42)
- expect(Gitlab::GithubImport::Caching)
+ expect(Gitlab::Cache::Import::Caching)
.to receive(:write)
.with(described_class::ID_CACHE_KEY % id, 42)
@@ -253,7 +253,7 @@ describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
.with(id)
.and_return(nil)
- expect(Gitlab::GithubImport::Caching)
+ expect(Gitlab::Cache::Import::Caching)
.to receive(:write)
.with(described_class::ID_CACHE_KEY % id, nil)
@@ -269,7 +269,7 @@ describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
.with(email)
.and_return(42)
- expect(Gitlab::GithubImport::Caching)
+ expect(Gitlab::Cache::Import::Caching)
.to receive(:write)
.with(described_class::ID_FOR_EMAIL_CACHE_KEY % email, 42)
@@ -281,7 +281,7 @@ describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
.with(email)
.and_return(nil)
- expect(Gitlab::GithubImport::Caching)
+ expect(Gitlab::Cache::Import::Caching)
.to receive(:write)
.with(described_class::ID_FOR_EMAIL_CACHE_KEY % email, nil)
@@ -317,13 +317,13 @@ describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
describe '#read_id_from_cache' do
it 'reads an ID from the cache' do
- Gitlab::GithubImport::Caching.write('foo', 10)
+ Gitlab::Cache::Import::Caching.write('foo', 10)
expect(finder.read_id_from_cache('foo')).to eq([true, 10])
end
it 'reads a cache key with an empty value' do
- Gitlab::GithubImport::Caching.write('foo', nil)
+ Gitlab::Cache::Import::Caching.write('foo', nil)
expect(finder.read_id_from_cache('foo')).to eq([true, nil])
end
diff --git a/spec/lib/gitlab/github_import_spec.rb b/spec/lib/gitlab/github_import_spec.rb
index c3ddac01c87..290d66243aa 100644
--- a/spec/lib/gitlab/github_import_spec.rb
+++ b/spec/lib/gitlab/github_import_spec.rb
@@ -35,7 +35,7 @@ describe Gitlab::GithubImport do
end
it 'caches the ghost user ID' do
- expect(Gitlab::GithubImport::Caching)
+ expect(Gitlab::Cache::Import::Caching)
.to receive(:write)
.once
.and_call_original
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 7bc9e1a9a32..fed33c89726 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -387,29 +387,6 @@ describe Gitlab::UsageData do
expect(described_class.count(relation, fallback: 15, batch: false)).to eq(15)
end
end
-
- describe '#approximate_counts' do
- it 'gets approximate counts for selected models', :aggregate_failures do
- create(:label)
-
- expect(Gitlab::Database::Count).to receive(:approximate_counts)
- .with(described_class::APPROXIMATE_COUNT_MODELS).once.and_call_original
-
- counts = described_class.approximate_counts.values
-
- expect(counts.count).to eq(described_class::APPROXIMATE_COUNT_MODELS.count)
- expect(counts.any? { |count| count < 0 }).to be_falsey
- end
-
- it 'returns default values if counts can not be retrieved', :aggregate_failures do
- described_class::APPROXIMATE_COUNT_MODELS.map do |model|
- model.name.underscore.pluralize.to_sym
- end
-
- expect(Gitlab::Database::Count).to receive(:approximate_counts).and_return({})
- expect(described_class.approximate_counts.values.uniq).to eq([-1])
- end
- end
end
end
end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 7d63a031666..6d1b76a9aea 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -832,6 +832,13 @@ describe API::Users, :do_not_mock_admin_mode do
expect(user.reload.private_profile).to eq(false)
end
+ it "does have default values for theme and color-scheme ID" do
+ put api("/users/#{user.id}", admin), params: {}
+
+ expect(user.reload.theme_id).to eq(Gitlab::Themes.default.id)
+ expect(user.reload.color_scheme_id).to eq(Gitlab::ColorSchemes.default.id)
+ end
+
it "updates private profile" do
put api("/users/#{user.id}", admin), params: { private_profile: true }
@@ -857,6 +864,19 @@ describe API::Users, :do_not_mock_admin_mode do
expect(user.reload.private_profile).to eq(true)
end
+ it "does not modify theme or color-scheme ID when field is not provided" do
+ theme = Gitlab::Themes.each.find { |t| t.id != Gitlab::Themes.default.id }
+ scheme = Gitlab::ColorSchemes.each.find { |t| t.id != Gitlab::ColorSchemes.default.id }
+
+ user.update(theme_id: theme.id, color_scheme_id: scheme.id)
+
+ put api("/users/#{user.id}", admin), params: {}
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(user.reload.theme_id).to eq(theme.id)
+ expect(user.reload.color_scheme_id).to eq(scheme.id)
+ end
+
it "does not update admin status" do
put api("/users/#{admin_user.id}", admin), params: { can_create_group: false }