Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
232e0a31f1
commit
8957ace315
57 changed files with 787 additions and 735 deletions
2
Gemfile
2
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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
`,
|
||||
});
|
||||
};
|
||||
|
|
81
app/assets/javascripts/blob/notebook/notebook_viewer.vue
Normal file
81
app/assets/javascripts/blob/notebook/notebook_viewer.vue
Normal file
|
@ -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>
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
53
app/finders/ci/jobs_finder.rb
Normal file
53
app/finders/ci/jobs_finder.rb
Normal file
|
@ -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
|
28
app/finders/ci/pipeline_schedules_finder.rb
Normal file
28
app/finders/ci/pipeline_schedules_finder.rb
Normal file
|
@ -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
|
158
app/finders/ci/pipelines_finder.rb
Normal file
158
app/finders/ci/pipelines_finder.rb
Normal file
|
@ -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
|
45
app/finders/ci/runner_jobs_finder.rb
Normal file
45
app/finders/ci/runner_jobs_finder.rb
Normal file
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Use batch counters instead of approximate counters in usage data
|
||||
merge_request: 27218
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Prevent default overwrite for theme and color ID in user API
|
||||
merge_request: 26792
|
||||
author: Fabio Huser
|
||||
type: fixed
|
5
changelogs/unreleased/update-puma-to-4-3-3.yml
Normal file
5
changelogs/unreleased/update-puma-to-4-3-3.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update Puma to 4.3.3
|
||||
merge_request: 27232
|
||||
author:
|
||||
type: security
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
153
lib/gitlab/cache/import/caching.rb
vendored
Normal file
153
lib/gitlab/cache/import/caching.rb
vendored
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) }
|
|
@ -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) }
|
|
@ -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) { {} }
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
describe RunnerJobsFinder do
|
||||
describe Ci::RunnerJobsFinder do
|
||||
let(:project) { create(:project) }
|
||||
let(:runner) { create(:ci_runner, :instance) }
|
||||
|
108
spec/frontend/blob/notebook/notebook_viever_spec.js
Normal file
108
spec/frontend/blob/notebook/notebook_viever_spec.js
Normal file
|
@ -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.',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1 +0,0 @@
|
|||
<div class="file-content" data-endpoint="/test" id="js-notebook-viewer"></div>
|
|
@ -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.',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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')
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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(
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
Loading…
Reference in a new issue