Commit 3c7cb6ad authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge remote-tracking branch 'upstream/master' into 30634-protected-pipeline

* upstream/master: (25 commits)
  Remove unneeded asserts and add tests for inactive RequestStore
  Rename the methods to make it fit with current name
  Follow feedback on the merge request
  Make sure it checks against the tag only when it's a tag
  Renamed Gitaly services
  fix transient rspec failure due to Poll.js race condition
  Refactor variables initialization in dropzone_input.js
  cache the cache key...
  avoid #respond_to? in Cache.id_for
  cache DeclarativePolicy.class_for at the class level
  Update 9.3-to-9.4.md
  fix padding on filtered search dropdown. Styles should only apply to li in list
  Cache Note#notable for commits and fix tests
  Add changelog entry
  Update the comments for the new functionality
  Use RequestStoreWrap for Commit#author
  Skip dead jobs queue for web hooks and project services
  Add RequestStoreWrap to cache via RequestStore
  Don't track cached queries in Gitlab::PerformanceBar::PeekQueryTracker
  Add changelog entry
  ...
parents 65e722ee f3e682c0
...@@ -237,7 +237,6 @@ gem 'webpack-rails', '~> 0.9.10' ...@@ -237,7 +237,6 @@ gem 'webpack-rails', '~> 0.9.10'
gem 'rack-proxy', '~> 0.6.0' gem 'rack-proxy', '~> 0.6.0'
gem 'sass-rails', '~> 5.0.6' gem 'sass-rails', '~> 5.0.6'
gem 'coffee-rails', '~> 4.1.0'
gem 'uglifier', '~> 2.7.2' gem 'uglifier', '~> 2.7.2'
gem 'addressable', '~> 2.3.8' gem 'addressable', '~> 2.3.8'
......
...@@ -122,13 +122,6 @@ GEM ...@@ -122,13 +122,6 @@ GEM
coderay (1.1.1) coderay (1.1.1)
coercible (1.0.0) coercible (1.0.0)
descendants_tracker (~> 0.0.1) descendants_tracker (~> 0.0.1)
coffee-rails (4.1.1)
coffee-script (>= 2.2.0)
railties (>= 4.0.0, < 5.1.x)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.10.0)
colorize (0.7.7) colorize (0.7.7)
concurrent-ruby (1.0.5) concurrent-ruby (1.0.5)
concurrent-ruby-ext (1.0.5) concurrent-ruby-ext (1.0.5)
...@@ -938,7 +931,6 @@ DEPENDENCIES ...@@ -938,7 +931,6 @@ DEPENDENCIES
charlock_holmes (~> 0.7.3) charlock_holmes (~> 0.7.3)
chronic (~> 0.10.2) chronic (~> 0.10.2)
chronic_duration (~> 0.10.6) chronic_duration (~> 0.10.6)
coffee-rails (~> 4.1.0)
concurrent-ruby (~> 1.0.5) concurrent-ruby (~> 1.0.5)
connection_pool (~> 2.0) connection_pool (~> 2.0)
creole (~> 0.5.0) creole (~> 0.5.0)
......
...@@ -5,21 +5,28 @@ import './preview_markdown'; ...@@ -5,21 +5,28 @@ import './preview_markdown';
window.DropzoneInput = (function() { window.DropzoneInput = (function() {
function DropzoneInput(form) { function DropzoneInput(form) {
var updateAttachingMessage, $attachingFileMessage, $mdArea, $attachButton, $cancelButton, $retryLink, $uploadingErrorContainer, $uploadingErrorMessage, $uploadProgress, $uploadingProgressContainer, appendToTextArea, btnAlert, child, closeAlertMessage, closeSpinner, divHover, divSpinner, dropzone, $formDropzone, formTextarea, getFilename, handlePaste, iconPaperclip, iconSpinner, insertToTextArea, isImage, maxFileSize, pasteText, uploadsPath, showError, showSpinner, uploadFile, addFileToForm;
Dropzone.autoDiscover = false; Dropzone.autoDiscover = false;
divHover = '<div class="div-dropzone-hover"></div>'; const divHover = '<div class="div-dropzone-hover"></div>';
iconPaperclip = '<i class="fa fa-paperclip div-dropzone-icon"></i>'; const iconPaperclip = '<i class="fa fa-paperclip div-dropzone-icon"></i>';
$attachButton = form.find('.button-attach-file'); const $attachButton = form.find('.button-attach-file');
$attachingFileMessage = form.find('.attaching-file-message'); const $attachingFileMessage = form.find('.attaching-file-message');
$cancelButton = form.find('.button-cancel-uploading-files'); const $cancelButton = form.find('.button-cancel-uploading-files');
$retryLink = form.find('.retry-uploading-link'); const $retryLink = form.find('.retry-uploading-link');
$uploadProgress = form.find('.uploading-progress'); const $uploadProgress = form.find('.uploading-progress');
$uploadingErrorContainer = form.find('.uploading-error-container'); const $uploadingErrorContainer = form.find('.uploading-error-container');
$uploadingErrorMessage = form.find('.uploading-error-message'); const $uploadingErrorMessage = form.find('.uploading-error-message');
$uploadingProgressContainer = form.find('.uploading-progress-container'); const $uploadingProgressContainer = form.find('.uploading-progress-container');
uploadsPath = window.uploads_path || null; const uploadsPath = window.uploads_path || null;
maxFileSize = gon.max_file_size || 10; const maxFileSize = gon.max_file_size || 10;
formTextarea = form.find('.js-gfm-input'); const formTextarea = form.find('.js-gfm-input');
let handlePaste;
let pasteText;
let addFileToForm;
let updateAttachingMessage;
let isImage;
let getFilename;
let uploadFile;
formTextarea.wrap('<div class="div-dropzone"></div>'); formTextarea.wrap('<div class="div-dropzone"></div>');
formTextarea.on('paste', (function(_this) { formTextarea.on('paste', (function(_this) {
return function(event) { return function(event) {
...@@ -28,16 +35,16 @@ window.DropzoneInput = (function() { ...@@ -28,16 +35,16 @@ window.DropzoneInput = (function() {
})(this)); })(this));
// Add dropzone area to the form. // Add dropzone area to the form.
$mdArea = formTextarea.closest('.md-area'); const $mdArea = formTextarea.closest('.md-area');
form.setupMarkdownPreview(); form.setupMarkdownPreview();
$formDropzone = form.find('.div-dropzone'); const $formDropzone = form.find('.div-dropzone');
$formDropzone.parent().addClass('div-dropzone-wrapper'); $formDropzone.parent().addClass('div-dropzone-wrapper');
$formDropzone.append(divHover); $formDropzone.append(divHover);
$formDropzone.find('.div-dropzone-hover').append(iconPaperclip); $formDropzone.find('.div-dropzone-hover').append(iconPaperclip);
if (!uploadsPath) return; if (!uploadsPath) return;
dropzone = $formDropzone.dropzone({ const dropzone = $formDropzone.dropzone({
url: uploadsPath, url: uploadsPath,
dictDefaultMessage: '', dictDefaultMessage: '',
clickable: true, clickable: true,
...@@ -117,7 +124,7 @@ window.DropzoneInput = (function() { ...@@ -117,7 +124,7 @@ window.DropzoneInput = (function() {
} }
}); });
child = $(dropzone[0]).children('textarea'); const child = $(dropzone[0]).children('textarea');
// removeAllFiles(true) stops uploading files (if any) // removeAllFiles(true) stops uploading files (if any)
// and remove them from dropzone files queue. // and remove them from dropzone files queue.
...@@ -214,6 +221,35 @@ window.DropzoneInput = (function() { ...@@ -214,6 +221,35 @@ window.DropzoneInput = (function() {
return value.first(); return value.first();
}; };
const showSpinner = function(e) {
return $uploadingProgressContainer.removeClass('hide');
};
const closeSpinner = function() {
return $uploadingProgressContainer.addClass('hide');
};
const showError = function(message) {
$uploadingErrorContainer.removeClass('hide');
$uploadingErrorMessage.html(message);
};
const closeAlertMessage = function() {
return form.find('.div-dropzone-alert').alert('close');
};
const insertToTextArea = function(filename, url) {
return $(child).val(function(index, val) {
return val.replace(`{{${filename}}}`, url);
});
};
const appendToTextArea = function(url) {
return $(child).val(function(index, val) {
return val + url + "\n";
});
};
uploadFile = function(item, filename) { uploadFile = function(item, filename) {
var formData; var formData;
formData = new FormData(); formData = new FormData();
...@@ -262,35 +298,6 @@ window.DropzoneInput = (function() { ...@@ -262,35 +298,6 @@ window.DropzoneInput = (function() {
messageContainer.text(attachingMessage); messageContainer.text(attachingMessage);
}; };
insertToTextArea = function(filename, url) {
return $(child).val(function(index, val) {
return val.replace(`{{${filename}}}`, url);
});
};
appendToTextArea = function(url) {
return $(child).val(function(index, val) {
return val + url + "\n";
});
};
showSpinner = function(e) {
return $uploadingProgressContainer.removeClass('hide');
};
closeSpinner = function() {
return $uploadingProgressContainer.addClass('hide');
};
showError = function(message) {
$uploadingErrorContainer.removeClass('hide');
$uploadingErrorMessage.html(message);
};
closeAlertMessage = function() {
return form.find('.div-dropzone-alert').alert('close');
};
form.find('.markdown-selector').click(function(e) { form.find('.markdown-selector').click(function(e) {
e.preventDefault(); e.preventDefault();
$(this).closest('.gfm-form').find('.div-dropzone').click(); $(this).closest('.gfm-form').find('.div-dropzone').click();
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
*/ */
export default { export default {
ABORTED: 0,
NO_CONTENT: 204, NO_CONTENT: 204,
OK: 200, OK: 200,
}; };
...@@ -81,6 +81,9 @@ export default class Poll { ...@@ -81,6 +81,9 @@ export default class Poll {
}) })
.catch((error) => { .catch((error) => {
notificationCallback(false); notificationCallback(false);
if (error.status === httpStatusCodes.ABORTED) {
return;
}
errorCallback(error); errorCallback(error);
}); });
} }
......
...@@ -297,18 +297,12 @@ ...@@ -297,18 +297,12 @@
} }
.droplab-dropdown { .droplab-dropdown {
.description {
display: inline-block;
white-space: normal;
margin-left: 5px;
}
.dropdown-toggle > i { .dropdown-toggle > i {
pointer-events: none; pointer-events: none;
} }
li { .dropdown-menu li {
padding: $gl-btn-padding $gl-btn-padding 2px; padding: $gl-btn-padding;
cursor: pointer; cursor: pointer;
> a, > a,
...@@ -344,9 +338,25 @@ ...@@ -344,9 +338,25 @@
visibility: visible; visibility: visible;
} }
&.divider {
margin: 0 8px;
padding: 0;
border-top: $gray-darkest;
}
.icon { .icon {
visibility: hidden; visibility: hidden;
} }
.description {
display: inline-block;
white-space: normal;
margin-left: 5px;
p {
margin-bottom: 0;
}
}
} }
.icon { .icon {
...@@ -354,12 +364,6 @@ ...@@ -354,12 +364,6 @@
vertical-align: top; vertical-align: top;
padding-top: 2px; padding-top: 2px;
} }
.divider {
margin: 0 8px;
padding: 0;
border-top: $gray-darkest;
}
} }
.droplab-dropdown .dropdown-menu, .droplab-dropdown .dropdown-menu,
......
...@@ -275,7 +275,7 @@ ...@@ -275,7 +275,7 @@
} }
.filtered-search-input-dropdown-menu { .filtered-search-input-dropdown-menu {
max-height: 215px; max-height: 225px;
max-width: 280px; max-width: 280px;
overflow: auto; overflow: auto;
...@@ -382,10 +382,6 @@ ...@@ -382,10 +382,6 @@
} }
} }
.dropdown-menu .filter-dropdown-item {
padding: 0;
}
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
.issues-details-filters { .issues-details-filters {
padding: 0 0 10px; padding: 0 0 10px;
...@@ -435,6 +431,7 @@ ...@@ -435,6 +431,7 @@
.fa { .fa {
width: 15px; width: 15px;
line-height: $line-height-base;
} }
.dropdown-label-box { .dropdown-label-box {
......
...@@ -813,8 +813,6 @@ ...@@ -813,8 +813,6 @@
} }
.description { .description {
margin-bottom: 10px;
.text { .text {
margin: 0; margin: 0;
} }
......
class Commit class Commit
extend ActiveModel::Naming extend ActiveModel::Naming
extend Gitlab::Cache::RequestCache
include ActiveModel::Conversion include ActiveModel::Conversion
include Noteable include Noteable
...@@ -169,19 +170,9 @@ class Commit ...@@ -169,19 +170,9 @@ class Commit
end end
def author def author
if RequestStore.active? User.find_by_any_email(author_email.downcase)
key = "commit_author:#{author_email.downcase}"
# nil is a valid value since no author may exist in the system
if RequestStore.store.key?(key)
@author = RequestStore.store[key]
else
@author = find_author_by_any_email
RequestStore.store[key] = @author
end
else
@author ||= find_author_by_any_email
end
end end
request_cache(:author) { author_email.downcase }
def committer def committer
@committer ||= User.find_by_any_email(committer_email.downcase) @committer ||= User.find_by_any_email(committer_email.downcase)
...@@ -322,7 +313,7 @@ class Commit ...@@ -322,7 +313,7 @@ class Commit
def raw_diffs(*args) def raw_diffs(*args)
if Gitlab::GitalyClient.feature_enabled?(:commit_raw_diffs) if Gitlab::GitalyClient.feature_enabled?(:commit_raw_diffs)
Gitlab::GitalyClient::Commit.new(project.repository).diff_from_parent(self, *args) Gitlab::GitalyClient::CommitService.new(project.repository).diff_from_parent(self, *args)
else else
raw.diffs(*args) raw.diffs(*args)
end end
...@@ -331,7 +322,7 @@ class Commit ...@@ -331,7 +322,7 @@ class Commit
def raw_deltas def raw_deltas
@deltas ||= Gitlab::GitalyClient.migrate(:commit_deltas) do |is_enabled| @deltas ||= Gitlab::GitalyClient.migrate(:commit_deltas) do |is_enabled|
if is_enabled if is_enabled
Gitlab::GitalyClient::Commit.new(project.repository).commit_deltas(self) Gitlab::GitalyClient::CommitService.new(project.repository).commit_deltas(self)
else else
raw.deltas raw.deltas
end end
...@@ -368,10 +359,6 @@ class Commit ...@@ -368,10 +359,6 @@ class Commit
end end
end end
def find_author_by_any_email
User.find_by_any_email(author_email.downcase)
end
def repo_changes def repo_changes
changes = { added: [], modified: [], removed: [] } changes = { added: [], modified: [], removed: [] }
......
...@@ -190,7 +190,7 @@ class Note < ActiveRecord::Base ...@@ -190,7 +190,7 @@ class Note < ActiveRecord::Base
# override to return commits, which are not active record # override to return commits, which are not active record
def noteable def noteable
if for_commit? if for_commit?
project.commit(commit_id) @commit ||= project.commit(commit_id)
else else
super super
end end
......
module Ci module Ci
class BuildPolicy < CommitStatusPolicy class BuildPolicy < CommitStatusPolicy
condition(:user_cannot_update) do condition(:user_cannot_update) do
!::Gitlab::UserAccess access = ::Gitlab::UserAccess.new(@user, project: @subject.project)
.new(@user, project: @subject.project)
.can_push_or_merge_to_branch?(@subject.ref) if @subject.tag?
!access.can_create_tag?(@subject.ref)
else
!access.can_push_or_merge_to_branch?(@subject.ref)
end
end end
rule { user_cannot_update }.prevent :update_build rule { user_cannot_update }.prevent :update_build
......
...@@ -55,7 +55,22 @@ ...@@ -55,7 +55,22 @@
%span %span
Members Members
- if current_user && can?(current_user, :admin_group, @group) - if current_user && can?(current_user, :admin_group, @group)
= nav_link(path: %w[groups#projects groups#edit]) do = nav_link(path: %w[groups#projects groups#edit ci_cd#show]) do
= link_to edit_group_path(@group), title: 'Settings' do = link_to edit_group_path(@group), title: 'Settings' do
%span %span
Settings Settings
%ul.sidebar-sub-level-items
= nav_link(path: 'groups#edit') do
= link_to edit_group_path(@group), title: 'General' do
%span
General
= nav_link(path: 'groups#projects') do
= link_to projects_group_path(@group), title: 'Projects' do
%span
Projects
= nav_link(controller: :ci_cd) do
= link_to group_settings_ci_cd_path(@group), title: 'Pipelines' do
%span
Pipelines
...@@ -165,7 +165,7 @@ ...@@ -165,7 +165,7 @@
Snippets Snippets
- if project_nav_tab? :settings - if project_nav_tab? :settings
= nav_link(path: %w[projects#edit members#show integrations#show services#edit repository#show ci_cd#show pages#show]) do = nav_link(path: %w[projects#edit project_members#index integrations#show services#edit repository#show ci_cd#show pages#show]) do
= link_to edit_project_path(@project), title: 'Settings', class: 'shortcuts-tree' do = link_to edit_project_path(@project), title: 'Settings', class: 'shortcuts-tree' do
%span %span
Settings Settings
...@@ -177,8 +177,8 @@ ...@@ -177,8 +177,8 @@
= link_to edit_project_path(@project), title: 'General' do = link_to edit_project_path(@project), title: 'General' do
%span %span
General General
= nav_link(controller: :members) do = nav_link(controller: :project_members) do
= link_to project_settings_members_path(@project), title: 'Members' do = link_to project_project_members_path(@project), title: 'Members' do
%span %span
Members Members
- if can_edit - if can_edit
......
...@@ -2,6 +2,8 @@ class ProjectServiceWorker ...@@ -2,6 +2,8 @@ class ProjectServiceWorker
include Sidekiq::Worker include Sidekiq::Worker
include DedicatedSidekiqQueue include DedicatedSidekiqQueue
sidekiq_options dead: false
def perform(hook_id, data) def perform(hook_id, data)
data = data.with_indifferent_access data = data.with_indifferent_access
Service.find(hook_id).execute(data) Service.find(hook_id).execute(data)
......
...@@ -2,7 +2,7 @@ class WebHookWorker ...@@ -2,7 +2,7 @@ class WebHookWorker
include Sidekiq::Worker include Sidekiq::Worker
include DedicatedSidekiqQueue include DedicatedSidekiqQueue
sidekiq_options retry: 4 sidekiq_options retry: 4, dead: false
def perform(hook_id, data, hook_name) def perform(hook_id, data, hook_name)
hook = WebHook.find(hook_id) hook = WebHook.find(hook_id)
......
---
title: refactor initializations in dropzone_input.js
merge_request: 12768
author: Brandon Everett
---
title: Prevent web hook and project service background jobs from going to the dead
jobs queue
merge_request:
author:
---
title: Remove coffee-rails gem
merge_request:
author: Takuya Noguchi
---
title: Protect manual actions against protected tag too
merge_request: 12908
author:
---
title: fix transient js error in rspec tests
merge_request:
author:
---
title: Add RequestCache which makes caching with RequestStore easier
merge_request: 12920
author:
...@@ -33,7 +33,6 @@ module GettextI18nRailsJs ...@@ -33,7 +33,6 @@ module GettextI18nRailsJs
[ [
".js", ".js",
".jsx", ".jsx",
".coffee",
".vue" ".vue"
].include? ::File.extname(file) ].include? ::File.extname(file)
end end
......
...@@ -157,8 +157,7 @@ configuration file may contain syntax errors. The block name ...@@ -157,8 +157,7 @@ configuration file may contain syntax errors. The block name
file, should be `[[storage]]` instead. file, should be `[[storage]]` instead.
```shell ```shell
cd /home/git/gitaly sudo -u git -H sed -i.pre-9.4 's/\[\[storages\]\]/[[storage]]/' /home/git/gitaly/config.toml
sudo -u git -H editor config.toml
``` ```
#### Compile Gitaly #### Compile Gitaly
......
...@@ -150,7 +150,7 @@ module API ...@@ -150,7 +150,7 @@ module API
# #
# begin # begin
# repository = wiki? ? project.wiki.repository : project.repository # repository = wiki? ? project.wiki.repository : project.repository
# Gitlab::GitalyClient::Notifications.new(repository.raw_repository).post_receive # Gitlab::GitalyClient::NotificationService.new(repository.raw_repository).post_receive
# rescue GRPC::Unavailable => e # rescue GRPC::Unavailable => e
# render_api_error!(e, 500) # render_api_error!(e, 500)
# end # end
......
...@@ -8,7 +8,12 @@ require_dependency 'declarative_policy/step' ...@@ -8,7 +8,12 @@ require_dependency 'declarative_policy/step'
require_dependency 'declarative_policy/base' require_dependency 'declarative_policy/base'
require 'thread'
module DeclarativePolicy module DeclarativePolicy
CLASS_CACHE_MUTEX = Mutex.new
CLASS_CACHE_IVAR = :@__DeclarativePolicy_CLASS_CACHE
class << self class << self
def policy_for(user, subject, opts = {}) def policy_for(user, subject, opts = {})
cache = opts[:cache] || {} cache = opts[:cache] || {}
...@@ -23,7 +28,36 @@ module DeclarativePolicy ...@@ -23,7 +28,36 @@ module DeclarativePolicy
subject = find_delegate(subject) subject = find_delegate(subject)
subject.class.ancestors.each do |klass| class_for_class(subject.class)
end
private
# This method is heavily cached because there are a lot of anonymous
# modules in play in a typical rails app, and #name performs quite
# slowly for anonymous classes and modules.
#
# See https://bugs.ruby-lang.org/issues/11119
#
# if the above bug is resolved, this caching could likely be removed.
def class_for_class(subject_class)
unless subject_class.instance_variable_defined?(CLASS_CACHE_IVAR)
CLASS_CACHE_MUTEX.synchronize do
# re-check in case of a race
break if subject_class.instance_variable_defined?(CLASS_CACHE_IVAR)
policy_class = compute_class_for_class(subject_class)
subject_class.instance_variable_set(CLASS_CACHE_IVAR, policy_class)
end
end
policy_class = subject_class.instance_variable_get(CLASS_CACHE_IVAR)
raise "no policy for #{subject.class.name}" if policy_class.nil?
policy_class
end
def compute_class_for_class(subject_class)
subject_class.ancestors.each do |klass|
next unless klass.name next unless klass.name
begin begin
...@@ -37,12 +71,8 @@ module DeclarativePolicy ...@@ -37,12 +71,8 @@ module DeclarativePolicy
nil nil
end end
end end
raise "no policy for #{subject.class.name}"
end end
private
def find_delegate(subject) def find_delegate(subject)
seen = Set.new seen = Set.new
......
...@@ -21,11 +21,14 @@ module DeclarativePolicy ...@@ -21,11 +21,14 @@ module DeclarativePolicy
private private
def id_for(obj) def id_for(obj)
if obj.respond_to?(:id) && obj.id id =
obj.id.to_s begin
else obj.id
"##{obj.object_id}" rescue NoMethodError
nil
end end
id || "##{obj.object_id}"
end end
end end
end end
......
...@@ -82,6 +82,7 @@ module DeclarativePolicy ...@@ -82,6 +82,7 @@ module DeclarativePolicy
# depending on the scope, we may cache only by the user or only by # depending on the scope, we may cache only by the user or only by
# the subject, resulting in sharing across different policy objects. # the subject, resulting in sharing across different policy objects.
def cache_key def cache_key
@cache_key ||=
case @condition.scope case @condition.scope
when :normal then "/dp/condition/#{@condition.key}/#{user_key},#{subject_key}" when :normal then "/dp/condition/#{@condition.key}/#{user_key},#{subject_key}"
when :user then "/dp/condition/#{@condition.key}/#{user_key}" when :user then "/dp/condition/#{@condition.key}/#{user_key}"
......
module Gitlab
module Cache
# This module provides a simple way to cache values in RequestStore,
# and the cache key would be based on the class name, method name,
# optionally customized instance level values, optionally customized
# method level values, and optional method arguments.
#
# A simple example:
#
# class UserAccess
# extend Gitlab::Cache::RequestCache
#
# request_cache_key do
# [user&.id, project&.id]
# end
#
# request_cache def can_push_to_branch?(ref)
# # ...
# end
# end
#
# This way, the result of `can_push_to_branch?` would be cached in
# `RequestStore.store` based on the cache key. If RequestStore is not
# currently active, then it would be stored in a hash saved in an
# instance variable, so the cache logic would be the same.
# Here's another example using customized method level values:
#
# class Commit
# extend Gitlab::Cache::RequestCache
#
# def author
# User.find_by_any_email(author_email.downcase)
# end
# request_cache(:author) { author_email.downcase }
# end
#
# So that we could have different strategies for different methods
#
module RequestCache
def self.extended(klass)
return if klass < self
extension = Module.new
klass.const_set(:RequestCacheExtension, extension)
klass.prepend(extension)
end
def request_cache_key(&block)
if block_given?
@request_cache_key = block
else
@request_cache_key
end
end
def request_cache(method_name, &method_key_block)
const_get(:RequestCacheExtension).module_eval do
cache_key_method_name = "#{method_name}_cache_key"
define_method(method_name) do |*args|
store =
if RequestStore.active?
RequestStore.store
else
ivar_name = # ! and ? cannot be used as ivar name
"@cache_#{method_name.to_s.tr('!?', "\u2605\u2606")}"
instance_variable_get(ivar_name) ||
instance_variable_set(ivar_name, {})
end
key = __send__(cache_key_method_name, args)
store.fetch(key) { store[key] = super(*args) }
end
define_method(cache_key_method_name) do |args|
klass = self.class
instance_key = instance_exec(&klass.request_cache_key) if
klass.request_cache_key
method_key = instance_exec(&method_key_block) if method_key_block
[klass.name, method_name, *instance_key, *method_key, *args]
.join(':')
end
private cache_key_method_name
end
end
end
end
end
...@@ -29,7 +29,7 @@ module Gitlab ...@@ -29,7 +29,7 @@ module Gitlab
path = path.sub(/\A\/*/, '') path = path.sub(/\A\/*/, '')
path = '/' if path.empty? path = '/' if path.empty?
name = File.basename(path) name = File.basename(path)
entry = Gitlab::GitalyClient::Commit.new(repository).tree_entry(sha, path, MAX_DATA_DISPLAY_SIZE) entry = Gitlab::GitalyClient::CommitService.new(repository).tree_entry(sha, path, MAX_DATA_DISPLAY_SIZE)
return unless entry return unless entry
case entry.type case entry.type
...@@ -87,7 +87,7 @@ module Gitlab ...@@ -87,7 +87,7 @@ module Gitlab
def raw(repository, sha) def raw(repository, sha)
Gitlab::GitalyClient.migrate(:git_blob_raw) do |is_enabled| Gitlab::GitalyClient.migrate(:git_blob_raw) do |is_enabled|
if is_enabled if is_enabled
Gitlab::GitalyClient::Blob.new(repository).get_blob(oid: sha, limit: MAX_DATA_DISPLAY_SIZE) Gitlab::GitalyClient::BlobService.new(repository).get_blob(oid: sha, limit: MAX_DATA_DISPLAY_SIZE)
else else
blob = repository.lookup(sha) blob = repository.lookup(sha)
...@@ -182,7 +182,7 @@ module Gitlab ...@@ -182,7 +182,7 @@ module Gitlab
Gitlab::GitalyClient.migrate(:git_blob_load_all_data) do |is_enabled| Gitlab::GitalyClient.migrate(:git_blob_load_all_data) do |is_enabled|
@data = begin @data = begin
if is_enabled if is_enabled
Gitlab::GitalyClient::Blob.new(repository).get_blob(oid: id, limit: -1).data Gitlab::GitalyClient::BlobService.new(repository).get_blob(oid: id, limit: -1).data
else else
repository.lookup(id).content repository.lookup(id).content
end end
......
...@@ -1106,11 +1106,11 @@ module Gitlab ...@@ -1106,11 +1106,11 @@ module Gitlab
end end
def gitaly_ref_client def gitaly_ref_client
@gitaly_ref_client ||= Gitlab::GitalyClient::Ref.new(self) @gitaly_ref_client ||= Gitlab::GitalyClient::RefService.new(self)
end end
def gitaly_commit_client def gitaly_commit_client
@gitaly_commit_client ||= Gitlab::GitalyClient::Commit.new(self) @gitaly_commit_client ||= Gitlab::GitalyClient::CommitService.new(self)
end end
def gitaly_migrate(method, &block) def gitaly_migrate(method, &block)
......
module Gitlab module Gitlab
module GitalyClient module GitalyClient
class Blob class BlobService
def initialize(repository) def initialize(repository)
@gitaly_repo = repository.gitaly_repository @gitaly_repo = repository.gitaly_repository
end end
......
module Gitlab module Gitlab
module GitalyClient module GitalyClient
class Commit class CommitService
# The ID of empty tree. # The ID of empty tree.
# See http://stackoverflow.com/a/40884093/1856239 and https://github.com/git/git/blob/3ad8b5bf26362ac67c9020bf8c30eee54a84f56d/cache.h#L1011-L1012 # See http://stackoverflow.com/a/40884093/1856239 and https://github.com/git/git/blob/3ad8b5bf26362ac67c9020bf8c30eee54a84f56d/cache.h#L1011-L1012
EMPTY_TREE_ID = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'.freeze EMPTY_TREE_ID = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'.freeze
...@@ -17,20 +17,20 @@ module Gitlab ...@@ -17,20 +17,20 @@ module Gitlab
child_id: child_id child_id: child_id
) )
GitalyClient.call(@repository.storage, :commit, :commit_is_ancestor, request).value GitalyClient.call(@repository.storage, :commit_service, :commit_is_ancestor, request).value
end end
def diff_from_parent(commit, options = {}) def diff_from_parent(commit, options = {})
request_params = commit_diff_request_params(commit, options) request_params = commit_diff_request_params(commit, options)
request_params[:ignore_whitespace_change] = options.fetch(:ignore_whitespace_change, false) request_params[:ignore_whitespace_change] = options.fetch(:ignore_whitespace_change, false)
request = Gitaly::CommitDiffRequest.new(request_params) request = Gitaly::CommitDiffRequest.new(request_params)
response = GitalyClient.call(@repository.storage, :diff, :commit_diff, request) response = GitalyClient.call(@repository.storage, :diff_service, :commit_diff, request)
Gitlab::Git::DiffCollection.new(GitalyClient::DiffStitcher.new(response), options) Gitlab::Git::DiffCollection.new(GitalyClient::DiffStitcher.new(response), options)
end end
def commit_deltas(commit) def commit_deltas(commit)
request = Gitaly::CommitDeltaRequest.new(commit_diff_request_params(commit)) request = Gitaly::CommitDeltaRequest.new(commit_diff_request_params(commit))
response = GitalyClient.call(@repository.storage, :diff, :commit_delta, request) response = GitalyClient.call(@repository.storage, :diff_service, :commit_delta, request)
response.flat_map do |msg| response.flat_map do |msg|
msg.deltas.map { |d| Gitlab::Git::Diff.new(d) } msg.deltas.map { |d| Gitlab::Git::Diff.new(d) }
end end
...@@ -44,7 +44,7 @@ module Gitlab ...@@ -44,7 +44,7 @@ module Gitlab
limit: limit.to_i limit: limit.to_i
) )
response = GitalyClient.call(@repository.storage, :commit, :tree_entry, request) response = GitalyClient.call(@repository.storage, :commit_service, :tree_entry, request)
entry = response.first entry = response.first
return unless entry.oid.present? return unless entry.oid.present?
......
module Gitlab module Gitlab
module GitalyClient module GitalyClient
class Notifications class NotificationService
# 'repository' is a Gitlab::Git::Repository # 'repository' is a Gitlab::Git::Repository
def initialize(repository) def initialize(repository)
@gitaly_repo = repository.gitaly_repository @gitaly_repo = repository.gitaly_repository
...@@ -10,7 +10,7 @@ module Gitlab ...@@ -10,7 +10,7 @@ module Gitlab
def post_receive def post_receive
GitalyClient.call( GitalyClient.call(
@storage, @storage,
:notifications, :notification_service,
:post_receive, :post_receive,
Gitaly::PostReceiveRequest.new(repository: @gitaly_repo) Gitaly::PostReceiveRequest.new(repository: @gitaly_repo)
) )
......
module Gitlab module Gitlab
module GitalyClient module GitalyClient
class Ref class RefService
include Gitlab::EncodingHelper include Gitlab::EncodingHelper
# 'repository' is a Gitlab::Git::Repository # 'repository' is a Gitlab::Git::Repository
...@@ -12,19 +12,19 @@ module Gitlab ...@@ -12,19 +12,19 @@ module Gitlab
def default_branch_name def default_branch_name
request = Gitaly::FindDefaultBranchNameRequest.new(repository: @gitaly_repo) request = Gitaly::FindDefaultBranchNameRequest.new(repository: @gitaly_repo)
response = GitalyClient.call(@storage, :ref, :find_default_branch_name, request) response = GitalyClient.call(@storage, :ref_service, :find_default_branch_name, request)
Gitlab::Git.branch_name(response.name) Gitlab::Git.branch_name(response.name)
end end
def branch_names def branch_names
request = Gitaly::FindAllBranchNamesRequest.new(repository: @gitaly_repo) request = Gitaly::FindAllBranchNamesRequest.new(repository: @gitaly_repo)
response = GitalyClient.call(@storage, :ref, :find_all_branch_names, request) response = GitalyClient.call(@storage, :ref_service, :find_all_branch_names, request)
consume_refs_response(response) { |name| Gitlab::Git.branch_name(name) } consume_refs_response(response) { |name| Gitlab::Git.branch_name(name) }
end end
def tag_names def tag_names
request = Gitaly::FindAllTagNamesRequest.new(repository: @gitaly_repo) request = Gitaly::FindAllTagNamesRequest.new(repository: @gitaly_repo)
response = GitalyClient.call(@storage, :ref, :find_all_tag_names, request) response = GitalyClient.call(@storage, :ref_service, :find_all_tag_names, request)
consume_refs_response(response) { |name| Gitlab::Git.tag_name(name) } consume_refs_response(response) { |name| Gitlab::Git.tag_name(name) }
end end
...@@ -34,7 +34,7 @@ module Gitlab ...@@ -34,7 +34,7 @@ module Gitlab
commit_id: commit_id, commit_id: commit_id,
prefix: ref_prefix prefix: ref_prefix
) )
encode!(GitalyClient.call(@storage, :ref, :find_ref_name, request).name.dup) encode!(GitalyClient.call(@storage, :ref_service, :find_ref_name, request).name.dup)
end end
def count_tag_names def count_tag_names
...@@ -48,7 +48,7 @@ module Gitlab ...@@ -48,7 +48,7 @@ module Gitlab
def local_branches(sort_by: nil) def local_branches(sort_by: nil)
request = Gitaly::FindLocalBranchesRequest.new(repository: @gitaly_repo) request = Gitaly::FindLocalBranchesRequest.new(repository: @gitaly_repo)
request.sort_by = sort_by_param(sort_by) if sort_by request.sort_by = sort_by_param(sort_by) if sort_by
response = GitalyClient.call(@storage, :ref, :find_local_branches, request) response = GitalyClient.call(@storage, :ref_service, :find_local_branches, request)
consume_branches_response(response) consume_branches_response(response)
end end
......
# Inspired by https://github.com/peek/peek-pg/blob/master/lib/peek/views/pg.rb # Inspired by https://github.com/peek/peek-pg/blob/master/lib/peek/views/pg.rb
# PEEK_DB_CLIENT is a constant set in config/initializers/peek.rb
module Gitlab module Gitlab
module PerformanceBar module PerformanceBar
module PeekQueryTracker module PeekQueryTracker
...@@ -23,10 +24,16 @@ module Gitlab ...@@ -23,10 +24,16 @@ module Gitlab
subscribe('sql.active_record') do |_, start, finish, _, data| subscribe('sql.active_record') do |_, start, finish, _, data|
if RequestStore.active? && RequestStore.store[:peek_enabled] if RequestStore.active? && RequestStore.store[:peek_enabled]
# data[:cached] is only available starting from Rails 5.1.0
# https://github.com/rails/rails/blob/v5.1.0/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb#L113
# Before that, data[:name] was set to 'CACHE'
# https://github.com/rails/rails/blob/v4.2.9/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb#L80
unless data.fetch(:cached, data[:name] == 'CACHE')
track_query(data[:sql].strip, data[:binds], start, finish) track_query(data[:sql].strip, data[:binds], start, finish)
end end
end end
end end
end
def track_query(raw_query, bindings, start, finish) def track_query(raw_query, bindings, start, finish)
query = Gitlab::Sherlock::Query.new(raw_query, start, finish) query = Gitlab::Sherlock::Query.new(raw_query, start, finish)
......
module Gitlab module Gitlab
class UserAccess class UserAccess
extend Gitlab::Cache::RequestStoreWrap extend Gitlab::Cache::RequestCache
request_store_wrap_key do request_cache_key do
[user&.id, project&.id] [user&.id, project&.id]
end end
...@@ -34,7 +34,7 @@ module Gitlab ...@@ -34,7 +34,7 @@ module Gitlab
true true
end end
def can_create_tag?(ref) request_cache def can_create_tag?(ref)
return false unless can_access_git? return false unless can_access_git?
if ProtectedTag.protected?(project, ref) if ProtectedTag.protected?(project, ref)
...@@ -44,7 +44,7 @@ module Gitlab ...@@ -44,7 +44,7 @@ module Gitlab
end end
end end
def can_delete_branch?(ref) request_cache def can_delete_branch?(ref)
return false unless can_access_git? return false unless can_access_git?
if ProtectedBranch.protected?(project, ref) if ProtectedBranch.protected?(project, ref)
...@@ -58,7 +58,7 @@ module Gitlab ...@@ -58,7 +58,7 @@ module Gitlab
can_push_to_branch?(ref) || can_merge_to_branch?(ref) can_push_to_branch?(ref) || can_merge_to_branch?(ref)
end end
request_store_wrap def can_push_to_branch?(ref) request_cache def can_push_to_branch?(ref)
return false unless can_access_git? return false unless can_access_git?
if ProtectedBranch.protected?(project, ref) if ProtectedBranch.protected?(project, ref)
...@@ -70,7 +70,7 @@ module Gitlab ...@@ -70,7 +70,7 @@ module Gitlab
end end
end end
request_store_wrap def can_merge_to_branch?(ref) request_cache def can_merge_to_branch?(ref)
return false unless can_access_git? return false unless can_access_git?
if ProtectedBranch.protected?(project, ref) if ProtectedBranch.protected?(project, ref)
......
...@@ -5,7 +5,7 @@ namespace :gettext do ...@@ -5,7 +5,7 @@ namespace :gettext do
# See: https://github.com/grosser/gettext_i18n_rails#customizing-list-of-translatable-files # See: https://github.com/grosser/gettext_i18n_rails#customizing-list-of-translatable-files
def files_to_translate def files_to_translate
folders = %W(app lib config #{locale_path}).join(',') folders = %W(app lib config #{locale_path}).join(',')
exts = %w(rb erb haml slim rhtml js jsx vue coffee handlebars hbs mustache).join(',') exts = %w(rb erb haml slim rhtml js jsx vue handlebars hbs mustache).join(',')
Dir.glob( Dir.glob(
"{#{folders}}/**/*.{#{exts}}" "{#{folders}}/**/*.{#{exts}}"
......
...@@ -4,14 +4,19 @@ FactoryGirl.define do ...@@ -4,14 +4,19 @@ FactoryGirl.define do
factory :commit do factory :commit do
git_commit RepoHelpers.sample_commit git_commit RepoHelpers.sample_commit
project factory: :empty_project project factory: :empty_project
author { build(:author) }
initialize_with do initialize_with do
new(git_commit, project) new(git_commit, project)
end end
after(:build) do |commit|
allow(commit).to receive(:author).and_return build(:author)
end
trait :without_author do trait :without_author do
author nil after(:build) do |commit|
allow(commit).to receive(:author).and_return nil
end
end end
end end
end end
...@@ -54,7 +54,8 @@ feature 'Member autocomplete', :js do ...@@ -54,7 +54,8 @@ feature 'Member autocomplete', :js do
let(:note) { create(:note_on_commit, project: project, commit_id: project.commit.id) } let(:note) { create(:note_on_commit, project: project, commit_id: project.commit.id) }
before do before do
allow_any_instance_of(Commit).to receive(:author).and_return(author) allow(User).to receive(:find_by_any_email)
.with(noteable.author_email.downcase).and_return(author)
visit project_commit_path(project, noteable) visit project_commit_path(project, noteable)
end end
......
...@@ -25,23 +25,28 @@ function mockServiceCall(service, response, shouldFail = false) { ...@@ -25,23 +25,28 @@ function mockServiceCall(service, response, shouldFail = false) {
describe('Poll', () => { describe('Poll', () => {
const service = jasmine.createSpyObj('service', ['fetch']); const service = jasmine.createSpyObj('service', ['fetch']);
const callbacks = jasmine.createSpyObj('callbacks', ['success', 'error']); const callbacks = jasmine.createSpyObj('callbacks', ['success', 'error', 'notification']);
function setup() {
return new Poll({
resource: service,
method: 'fetch',
successCallback: callbacks.success,
errorCallback: callbacks.error,
notificationCallback: callbacks.notification,
}).makeRequest();
}
afterEach(() => { afterEach(() => {
callbacks.success.calls.reset(); callbacks.success.calls.reset();
callbacks.error.calls.reset(); callbacks.error.calls.reset();
callbacks.notification.calls.reset();
service.fetch.calls.reset(); service.fetch.calls.reset();
}); });
it('calls the success callback when no header for interval is provided', (done) => { it('calls the success callback when no header for interval is provided', (done) => {
mockServiceCall(service, { status: 200 }); mockServiceCall(service, { status: 200 });
setup();
new Poll({
resource: service,
method: 'fetch',
successCallback: callbacks.success,
errorCallback: callbacks.error,
}).makeRequest();
waitForAllCallsToFinish(service, 1, () => { waitForAllCallsToFinish(service, 1, () => {
expect(callbacks.success).toHaveBeenCalled(); expect(callbacks.success).toHaveBeenCalled();
...@@ -51,15 +56,9 @@ describe('Poll', () => { ...@@ -51,15 +56,9 @@ describe('Poll', () => {
}); });
}); });
it('calls the error callback whe the http request returns an error', (done) => { it('calls the error callback when the http request returns an error', (done) => {
mockServiceCall(service, { status: 500 }, true); mockServiceCall(service, { status: 500 }, true);
setup();
new Poll({
resource: service,
method: 'fetch',
successCallback: callbacks.success,
errorCallback: callbacks.error,
}).makeRequest();
waitForAllCallsToFinish(service, 1, () => { waitForAllCallsToFinish(service, 1, () => {
expect(callbacks.success).not.toHaveBeenCalled(); expect(callbacks.success).not.toHaveBeenCalled();
...@@ -69,15 +68,22 @@ describe('Poll', () => { ...@@ -69,15 +68,22 @@ describe('Poll', () => {
}); });
}); });
it('skips the error callback when request is aborted', (done) => {
mockServiceCall(service, { status: 0 }, true);
setup();
waitForAllCallsToFinish(service, 1, () => {
expect(callbacks.success).not.toHaveBeenCalled();
expect(callbacks.error).not.toHaveBeenCalled();
expect(callbacks.notification).toHaveBeenCalled();
done();
});
});
it('should call the success callback when the interval header is -1', (done) => { it('should call the success callback when the interval header is -1', (done) => {
mockServiceCall(service, { status: 200, headers: { 'poll-interval': -1 } }); mockServiceCall(service, { status: 200, headers: { 'poll-interval': -1 } });
setup().then(() => {
new Poll({
resource: service,
method: 'fetch',
successCallback: callbacks.success,
errorCallback: callbacks.error,
}).makeRequest().then(() => {
expect(callbacks.success).toHaveBeenCalled(); expect(callbacks.success).toHaveBeenCalled();
expect(callbacks.error).not.toHaveBeenCalled(); expect(callbacks.error).not.toHaveBeenCalled();
......
require 'spec_helper'
describe Gitlab::Cache::RequestCache do
let(:klass) do
Class.new do
extend Gitlab::Cache::RequestCache
attr_accessor :id, :name, :result, :extra
def self.name
'ExpensiveAlgorithm'
end
def initialize(id, name, result, extra = nil)
self.id = id
self.name = name
self.result = result
self.extra = nil
end
request_cache def compute(arg)
result << arg
end
request_cache def repute(arg)
result << arg
end
def dispute(arg)
result << arg
end
request_cache(:dispute) { extra }
end
end
let(:algorithm) { klass.new('id', 'name', []) }
shared_examples 'cache for the same instance' do
it 'does not compute twice for the same argument' do
algorithm.compute(true)
result = algorithm.compute(true)
expect(result).to eq([true])
end
it 'computes twice for the different argument' do
algorithm.compute(true)
result = algorithm.compute(false)
expect(result).to eq([true, false])
end
it 'computes twice for the different class name' do
algorithm.compute(true)
allow(klass).to receive(:name).and_return('CheapAlgo')
result = algorithm.compute(true)
expect(result).to eq([true, true])
end
it 'computes twice for the different method' do
algorithm.compute(true)
result = algorithm.repute(true)
expect(result).to eq([true, true])
end
context 'when request_cache_key is provided' do
before do
klass.request_cache_key do
[id, name]
end
end
it 'computes twice for the different keys, id' do
algorithm.compute(true)
algorithm.id = 'ad'
result = algorithm.compute(true)
expect(result).to eq([true, true])
end
it 'computes twice for the different keys, name' do
algorithm.compute(true)
algorithm.name = 'same'
result = algorithm.compute(true)
expect(result).to eq([true, true])
end
it 'uses extra method cache key if provided' do
algorithm.dispute(true) # miss
algorithm.extra = true
algorithm.dispute(true) # miss
result = algorithm.dispute(true) # hit
expect(result).to eq([true, true])
end
end
end
context 'when RequestStore is active', :request_store do
it_behaves_like 'cache for the same instance'
it 'computes once for different instances when keys are the same' do
algorithm.compute(true)
result = klass.new('id', 'name', algorithm.result).compute(true)
expect(result).to eq([true])
end
it 'computes twice if RequestStore starts over' do
algorithm.compute(true)
RequestStore.end!
RequestStore.clear!
RequestStore.begin!
result = algorithm.compute(true)
expect(result).to eq([true, true])
end
end
context 'when RequestStore is inactive' do
it_behaves_like 'cache for the same instance'
it 'computes twice for different instances even if keys are the same' do
algorithm.compute(true)
result = klass.new('id', 'name', algorithm.result).compute(true)
expect(result).to eq([true, true])
end
end
end
...@@ -45,11 +45,11 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -45,11 +45,11 @@ describe Gitlab::Git::Repository, seed_helper: true do
end end
it 'gets the branch name from GitalyClient' do it 'gets the branch name from GitalyClient' do
expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name) expect_any_instance_of(Gitlab::GitalyClient::RefService).to receive(:default_branch_name)
repository.root_ref repository.root_ref
end end
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::Ref, :default_branch_name do it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :default_branch_name do
subject { repository.root_ref } subject { repository.root_ref }
end end
end end
...@@ -132,11 +132,11 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -132,11 +132,11 @@ describe Gitlab::Git::Repository, seed_helper: true do
it { is_expected.not_to include("branch-from-space") } it { is_expected.not_to include("branch-from-space") }
it 'gets the branch names from GitalyClient' do it 'gets the branch names from GitalyClient' do
expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names) expect_any_instance_of(Gitlab::GitalyClient::RefService).to receive(:branch_names)
subject subject
end end
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::Ref, :branch_names it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :branch_names
end end
describe '#tag_names' do describe '#tag_names' do
...@@ -160,11 +160,11 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -160,11 +160,11 @@ describe Gitlab::Git::Repository, seed_helper: true do
it { is_expected.not_to include("v5.0.0") } it { is_expected.not_to include("v5.0.0") }
it 'gets the tag names from GitalyClient' do it 'gets the tag names from GitalyClient' do
expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names) expect_any_instance_of(Gitlab::GitalyClient::RefService).to receive(:tag_names)
subject subject
end end
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::Ref, :tag_names it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :tag_names
end end
shared_examples 'archive check' do |extenstion| shared_examples 'archive check' do |extenstion|
...@@ -368,7 +368,7 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -368,7 +368,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
context 'when Gitaly commit_count feature is enabled' do context 'when Gitaly commit_count feature is enabled' do
it_behaves_like 'counting commits' it_behaves_like 'counting commits'
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::Commit, :commit_count do it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::CommitService, :commit_count do
subject { repository.commit_count('master') } subject { repository.commit_count('master') }
end end
end end
...@@ -1225,12 +1225,12 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -1225,12 +1225,12 @@ describe Gitlab::Git::Repository, seed_helper: true do
end end
it 'gets the branches from GitalyClient' do it 'gets the branches from GitalyClient' do
expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:local_branches) expect_any_instance_of(Gitlab::GitalyClient::RefService).to receive(:local_branches)
.and_return([]) .and_return([])
@repo.local_branches @repo.local_branches
end end
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::Ref, :local_branches do it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :local_branches do
subject { @repo.local_branches } subject { @repo.local_branches }
end end
end end
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::GitalyClient::Commit do describe Gitlab::GitalyClient::CommitService do
let(:diff_stub) { double('Gitaly::Diff::Stub') } let(:diff_stub) { double('Gitaly::DiffService::Stub') }
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:repository) { project.repository } let(:repository) { project.repository }
let(:repository_message) { repository.gitaly_repository } let(:repository_message) { repository.gitaly_repository }
...@@ -16,7 +16,7 @@ describe Gitlab::GitalyClient::Commit do ...@@ -16,7 +16,7 @@ describe Gitlab::GitalyClient::Commit do
right_commit_id: commit.id right_commit_id: commit.id
) )
expect_any_instance_of(Gitaly::Diff::Stub).to receive(:commit_diff).with(request, kind_of(Hash)) expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_diff).with(request, kind_of(Hash))
described_class.new(repository).diff_from_parent(commit) described_class.new(repository).diff_from_parent(commit)
end end
...@@ -31,7 +31,7 @@ describe Gitlab::GitalyClient::Commit do ...@@ -31,7 +31,7 @@ describe Gitlab::GitalyClient::Commit do
right_commit_id: initial_commit.id right_commit_id: initial_commit.id
) )
expect_any_instance_of(Gitaly::Diff::Stub).to receive(:commit_diff).with(request, kind_of(Hash)) expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_diff).with(request, kind_of(Hash))
described_class.new(repository).diff_from_parent(initial_commit) described_class.new(repository).diff_from_parent(initial_commit)
end end
...@@ -61,7 +61,7 @@ describe Gitlab::GitalyClient::Commit do ...@@ -61,7 +61,7 @@ describe Gitlab::GitalyClient::Commit do
right_commit_id: commit.id right_commit_id: commit.id
) )
expect_any_instance_of(Gitaly::Diff::Stub).to receive(:commit_delta).with(request, kind_of(Hash)).and_return([]) expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_delta).with(request, kind_of(Hash)).and_return([])
described_class.new(repository).commit_deltas(commit) described_class.new(repository).commit_deltas(commit)
end end
...@@ -76,7 +76,7 @@ describe Gitlab::GitalyClient::Commit do ...@@ -76,7 +76,7 @@ describe Gitlab::GitalyClient::Commit do
right_commit_id: initial_commit.id right_commit_id: initial_commit.id
) )
expect_any_instance_of(Gitaly::Diff::Stub).to receive(:commit_delta).with(request, kind_of(Hash)).and_return([]) expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_delta).with(request, kind_of(Hash)).and_return([])
described_class.new(repository).commit_deltas(initial_commit) described_class.new(repository).commit_deltas(initial_commit)
end end
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::GitalyClient::Notifications do describe Gitlab::GitalyClient::NotificationService do
describe '#post_receive' do describe '#post_receive' do
let(:project) { create(:empty_project) } let(:project) { create(:empty_project) }
let(:storage_name) { project.repository_storage } let(:storage_name) { project.repository_storage }
...@@ -8,7 +8,7 @@ describe Gitlab::GitalyClient::Notifications do ...@@ -8,7 +8,7 @@ describe Gitlab::GitalyClient::Notifications do
subject { described_class.new(project.repository) } subject { described_class.new(project.repository) }
it 'sends a post_receive message' do it 'sends a post_receive message' do
expect_any_instance_of(Gitaly::Notifications::Stub) expect_any_instance_of(Gitaly::NotificationService::Stub)
.to receive(:post_receive).with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) .to receive(:post_receive).with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
subject.post_receive subject.post_receive
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::GitalyClient::Ref do describe Gitlab::GitalyClient::RefService do
let(:project) { create(:empty_project) } let(:project) { create(:empty_project) }
let(:storage_name) { project.repository_storage } let(:storage_name) { project.repository_storage }
let(:relative_path) { project.path_with_namespace + '.git' } let(:relative_path) { project.path_with_namespace + '.git' }
...@@ -8,7 +8,7 @@ describe Gitlab::GitalyClient::Ref do ...@@ -8,7 +8,7 @@ describe Gitlab::GitalyClient::Ref do
describe '#branch_names' do describe '#branch_names' do
it 'sends a find_all_branch_names message' do it 'sends a find_all_branch_names message' do
expect_any_instance_of(Gitaly::Ref::Stub) expect_any_instance_of(Gitaly::RefService::Stub)
.to receive(:find_all_branch_names) .to receive(:find_all_branch_names)
.with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
.and_return([]) .and_return([])
...@@ -19,7 +19,7 @@ describe Gitlab::GitalyClient::Ref do ...@@ -19,7 +19,7 @@ describe Gitlab::GitalyClient::Ref do
describe '#tag_names' do describe '#tag_names' do
it 'sends a find_all_tag_names message' do it 'sends a find_all_tag_names message' do
expect_any_instance_of(Gitaly::Ref::Stub) expect_any_instance_of(Gitaly::RefService::Stub)
.to receive(:find_all_tag_names) .to receive(:find_all_tag_names)
.with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
.and_return([]) .and_return([])
...@@ -30,7 +30,7 @@ describe Gitlab::GitalyClient::Ref do ...@@ -30,7 +30,7 @@ describe Gitlab::GitalyClient::Ref do
describe '#default_branch_name' do describe '#default_branch_name' do
it 'sends a find_default_branch_name message' do it 'sends a find_default_branch_name message' do
expect_any_instance_of(Gitaly::Ref::Stub) expect_any_instance_of(Gitaly::RefService::Stub)
.to receive(:find_default_branch_name) .to receive(:find_default_branch_name)
.with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
.and_return(double(name: 'foo')) .and_return(double(name: 'foo'))
...@@ -41,7 +41,7 @@ describe Gitlab::GitalyClient::Ref do ...@@ -41,7 +41,7 @@ describe Gitlab::GitalyClient::Ref do
describe '#local_branches' do describe '#local_branches' do
it 'sends a find_local_branches message' do it 'sends a find_local_branches message' do
expect_any_instance_of(Gitaly::Ref::Stub) expect_any_instance_of(Gitaly::RefService::Stub)
.to receive(:find_local_branches) .to receive(:find_local_branches)
.with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
.and_return([]) .and_return([])
...@@ -50,7 +50,7 @@ describe Gitlab::GitalyClient::Ref do ...@@ -50,7 +50,7 @@ describe Gitlab::GitalyClient::Ref do
end end
it 'parses and sends the sort parameter' do it 'parses and sends the sort parameter' do
expect_any_instance_of(Gitaly::Ref::Stub) expect_any_instance_of(Gitaly::RefService::Stub)
.to receive(:find_local_branches) .to receive(:find_local_branches)
.with(gitaly_request_with_params(sort_by: :UPDATED_DESC), kind_of(Hash)) .with(gitaly_request_with_params(sort_by: :UPDATED_DESC), kind_of(Hash))
.and_return([]) .and_return([])
...@@ -59,7 +59,7 @@ describe Gitlab::GitalyClient::Ref do ...@@ -59,7 +59,7 @@ describe Gitlab::GitalyClient::Ref do
end end
it 'translates known mismatches on sort param values' do it 'translates known mismatches on sort param values' do
expect_any_instance_of(Gitaly::Ref::Stub) expect_any_instance_of(Gitaly::RefService::Stub)
.to receive(:find_local_branches) .to receive(:find_local_branches)
.with(gitaly_request_with_params(sort_by: :NAME), kind_of(Hash)) .with(gitaly_request_with_params(sort_by: :NAME), kind_of(Hash))
.and_return([]) .and_return([])
......
...@@ -16,9 +16,9 @@ describe Gitlab::GitalyClient, lib: true, skip_gitaly_mock: true do ...@@ -16,9 +16,9 @@ describe Gitlab::GitalyClient, lib: true, skip_gitaly_mock: true do
'default' => { 'gitaly_address' => address } 'default' => { 'gitaly_address' => address }
}) })
expect(Gitaly::Commit::Stub).to receive(:new).with(address, any_args) expect(Gitaly::CommitService::Stub).to receive(:new).with(address, any_args)
described_class.stub(:commit, 'default') described_class.stub(:commit_service, 'default')
end end
end end
...@@ -31,9 +31,9 @@ describe Gitlab::GitalyClient, lib: true, skip_gitaly_mock: true do ...@@ -31,9 +31,9 @@ describe Gitlab::GitalyClient, lib: true, skip_gitaly_mock: true do
'default' => { 'gitaly_address' => prefixed_address } 'default' => { 'gitaly_address' => prefixed_address }
}) })
expect(Gitaly::Commit::Stub).to receive(:new).with(address, any_args) expect(Gitaly::CommitService::Stub).to receive(:new).with(address, any_args)
described_class.stub(:commit, 'default') described_class.stub(:commit_service, 'default')
end end
end end
end end
......
...@@ -19,17 +19,15 @@ describe Commit, models: true do ...@@ -19,17 +19,15 @@ describe Commit, models: true do
expect(commit.author).to eq(user) expect(commit.author).to eq(user)
end end
it 'caches the author' do it 'caches the author', :request_store do
allow(RequestStore).to receive(:active?).and_return(true)
user = create(:user, email: commit.author_email) user = create(:user, email: commit.author_email)
expect_any_instance_of(Commit).to receive(:find_author_by_any_email).and_call_original expect(User).to receive(:find_by_any_email).and_call_original
expect(commit.author).to eq(user) expect(commit.author).to eq(user)
key = "commit_author:#{commit.author_email}" key = "Commit:author:#{commit.author_email.downcase}"
expect(RequestStore.store[key]).to eq(user) expect(RequestStore.store[key]).to eq(user)
expect(commit.author).to eq(user) expect(commit.author).to eq(user)
RequestStore.store.clear
end end
end end
......
...@@ -114,14 +114,6 @@ describe Ci::BuildPolicy, :models do ...@@ -114,14 +114,6 @@ describe Ci::BuildPolicy, :models do
end end
end end
context 'when developers can push to the branch' do
let(:branch_policy) { :developers_can_push }
it 'includes ability to update build' do
expect(policy).to be_allowed :update_build
end
end
context 'when developers can push to the branch' do context 'when developers can push to the branch' do
let(:branch_policy) { :developers_can_merge } let(:branch_policy) { :developers_can_merge }
......
...@@ -594,10 +594,10 @@ describe API::Internal do ...@@ -594,10 +594,10 @@ describe API::Internal do
# end # end
# #
# it "calls the Gitaly client with the project's repository" do # it "calls the Gitaly client with the project's repository" do
# expect(Gitlab::GitalyClient::Notifications). # expect(Gitlab::GitalyClient::NotificationService).
# to receive(:new).with(gitlab_git_repository_with(path: project.repository.path)). # to receive(:new).with(gitlab_git_repository_with(path: project.repository.path)).
# and_call_original # and_call_original
# expect_any_instance_of(Gitlab::GitalyClient::Notifications). # expect_any_instance_of(Gitlab::GitalyClient::NotificationService).
# to receive(:post_receive) # to receive(:post_receive)
# #
# post api("/internal/notify_post_receive"), valid_params # post api("/internal/notify_post_receive"), valid_params
...@@ -606,10 +606,10 @@ describe API::Internal do ...@@ -606,10 +606,10 @@ describe API::Internal do
# end # end
# #
# it "calls the Gitaly client with the wiki's repository if it's a wiki" do # it "calls the Gitaly client with the wiki's repository if it's a wiki" do
# expect(Gitlab::GitalyClient::Notifications). # expect(Gitlab::GitalyClient::NotificationService).
# to receive(:new).with(gitlab_git_repository_with(path: project.wiki.repository.path)). # to receive(:new).with(gitlab_git_repository_with(path: project.wiki.repository.path)).
# and_call_original # and_call_original
# expect_any_instance_of(Gitlab::GitalyClient::Notifications). # expect_any_instance_of(Gitlab::GitalyClient::NotificationService).
# to receive(:post_receive) # to receive(:post_receive)
# #
# post api("/internal/notify_post_receive"), valid_wiki_params # post api("/internal/notify_post_receive"), valid_wiki_params
...@@ -618,7 +618,7 @@ describe API::Internal do ...@@ -618,7 +618,7 @@ describe API::Internal do
# end # end
# #
# it "returns 500 if the gitaly call fails" do # it "returns 500 if the gitaly call fails" do
# expect_any_instance_of(Gitlab::GitalyClient::Notifications). # expect_any_instance_of(Gitlab::GitalyClient::NotificationService).
# to receive(:post_receive).and_raise(GRPC::Unavailable) # to receive(:post_receive).and_raise(GRPC::Unavailable)
# #
# post api("/internal/notify_post_receive"), valid_params # post api("/internal/notify_post_receive"), valid_params
...@@ -636,10 +636,10 @@ describe API::Internal do ...@@ -636,10 +636,10 @@ describe API::Internal do
# end # end
# #
# it "calls the Gitaly client with the project's repository" do # it "calls the Gitaly client with the project's repository" do
# expect(Gitlab::GitalyClient::Notifications). # expect(Gitlab::GitalyClient::NotificationService).
# to receive(:new).with(gitlab_git_repository_with(path: project.repository.path)). # to receive(:new).with(gitlab_git_repository_with(path: project.repository.path)).
# and_call_original # and_call_original
# expect_any_instance_of(Gitlab::GitalyClient::Notifications). # expect_any_instance_of(Gitlab::GitalyClient::NotificationService).
# to receive(:post_receive) # to receive(:post_receive)
# #
# post api("/internal/notify_post_receive"), valid_params # post api("/internal/notify_post_receive"), valid_params
...@@ -648,10 +648,10 @@ describe API::Internal do ...@@ -648,10 +648,10 @@ describe API::Internal do
# end # end
# #
# it "calls the Gitaly client with the wiki's repository if it's a wiki" do # it "calls the Gitaly client with the wiki's repository if it's a wiki" do
# expect(Gitlab::GitalyClient::Notifications). # expect(Gitlab::GitalyClient::NotificationService).
# to receive(:new).with(gitlab_git_repository_with(path: project.wiki.repository.path)). # to receive(:new).with(gitlab_git_repository_with(path: project.wiki.repository.path)).
# and_call_original # and_call_original
# expect_any_instance_of(Gitlab::GitalyClient::Notifications). # expect_any_instance_of(Gitlab::GitalyClient::NotificationService).
# to receive(:post_receive) # to receive(:post_receive)
# #
# post api("/internal/notify_post_receive"), valid_wiki_params # post api("/internal/notify_post_receive"), valid_wiki_params
......
...@@ -383,7 +383,7 @@ describe NotificationService, services: true do ...@@ -383,7 +383,7 @@ describe NotificationService, services: true do
before do before do
build_team(note.project) build_team(note.project)
reset_delivered_emails! reset_delivered_emails!
allow_any_instance_of(Commit).to receive(:author).and_return(@u_committer) allow(note.noteable).to receive(:author).and_return(@u_committer)
update_custom_notification(:new_note, @u_guest_custom, resource: project) update_custom_notification(:new_note, @u_guest_custom, resource: project)
update_custom_notification(:new_note, @u_custom_global) update_custom_notification(:new_note, @u_custom_global)
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment