Skip to content
Snippets Groups Projects
Commit 180cd023 authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent be3e24ea
No related branches found
No related tags found
No related merge requests found
Showing
with 452 additions and 285 deletions
Loading
Loading
@@ -2,6 +2,15 @@
 
module API
module Entities
class Membership < Grape::Entity
expose :source_id
expose :source_name do |member|
member.source.name
end
expose :source_type
expose :access_level
end
class BlameRangeCommit < Grape::Entity
expose :id
expose :parent_ids
Loading
Loading
Loading
Loading
@@ -533,6 +533,32 @@ module API
end
# rubocop: enable CodeReuse/ActiveRecord
 
desc 'Get memberships' do
success Entities::Membership
end
params do
requires :user_id, type: Integer, desc: 'The ID of the user'
optional :type, type: String, values: %w[Project Namespace]
use :pagination
end
get ":user_id/memberships" do
authenticated_as_admin!
user = find_user_by_id(params)
members = case params[:type]
when 'Project'
user.project_members
when 'Namespace'
user.group_members
else
user.members
end
members = members.including_source
present paginate(members), with: Entities::Membership
end
params do
requires :user_id, type: Integer, desc: 'The ID of the user'
end
Loading
Loading
Loading
Loading
@@ -54,6 +54,12 @@ module Sentry
end
end
 
def http_post(url, params = {})
http_request do
Gitlab::HTTP.post(url, **request_params.merge(body: params.to_json))
end
end
def http_request
response = handle_request_exceptions do
yield
Loading
Loading
Loading
Loading
@@ -3,8 +3,22 @@
module Sentry
class Client
module IssueLink
def create_issue_link(integration_id, sentry_issue_identifier, issue)
issue_link_url = issue_link_api_url(integration_id, sentry_issue_identifier)
# Creates a link in Sentry corresponding to the provided
# Sentry issue and GitLab issue
# @param integration_id [Integer, nil] Representing a global
# GitLab integration in Sentry. Nil for plugins.
# @param sentry_issue_id [Integer] Id for an issue from Sentry
# @param issue [Issue] Issue for which the link should be created
def create_issue_link(integration_id, sentry_issue_id, issue)
return create_plugin_link(sentry_issue_id, issue) unless integration_id
create_global_integration_link(integration_id, sentry_issue_id, issue)
end
private
def create_global_integration_link(integration_id, sentry_issue_id, issue)
issue_link_url = global_integration_link_api_url(integration_id, sentry_issue_id)
 
params = {
project: issue.project.id,
Loading
Loading
@@ -14,11 +28,22 @@ module Sentry
http_put(issue_link_url, params)
end
 
private
def global_integration_link_api_url(integration_id, sentry_issue_id)
issue_link_url = URI(url)
issue_link_url.path = "/api/0/groups/#{sentry_issue_id}/integrations/#{integration_id}/"
issue_link_url
end
def create_plugin_link(sentry_issue_id, issue)
issue_link_url = plugin_link_api_url(sentry_issue_id)
http_post(issue_link_url, issue_id: issue.iid)
end
 
def issue_link_api_url(integration_id, sentry_issue_identifier)
def plugin_link_api_url(sentry_issue_id)
issue_link_url = URI(url)
issue_link_url.path = "/api/0/groups/#{sentry_issue_identifier}/integrations/#{integration_id}/"
issue_link_url.path = "/api/0/issues/#{sentry_issue_id}/plugins/gitlab/link/"
 
issue_link_url
end
Loading
Loading
Loading
Loading
@@ -13026,6 +13026,12 @@ msgstr ""
msgid "PackageRegistry|There was a problem fetching the details for this package."
msgstr ""
 
msgid "PackageRegistry|There was an error fetching the pipeline information."
msgstr ""
msgid "PackageRegistry|Unable to fetch pipeline information"
msgstr ""
msgid "PackageRegistry|Unable to load package"
msgstr ""
 
Loading
Loading
Loading
Loading
@@ -66,14 +66,23 @@ module QA
metadata[:type] = :feature
end
 
config.before do
config.before(:suite) do
unless browser.rspec_configured
browser.rspec_configured = true
 
##
# Perform before hooks, which are different for CE and EE
#
Runtime::Release.perform_before_hooks
begin
Runtime::Release.perform_before_hooks
rescue
saved = Capybara::Screenshot.screenshot_and_save_page
QA::Runtime::Logger.error("Screenshot: #{saved[:image]}") if saved&.key?(:image)
QA::Runtime::Logger.error("HTML capture: #{saved[:html]}") if saved&.key?(:html)
raise
end
end
end
end
Loading
Loading
Loading
Loading
@@ -3,7 +3,7 @@
FactoryBot.define do
factory :project_error_tracking_setting, class: 'ErrorTracking::ProjectErrorTrackingSetting' do
project
api_url { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' }
api_url { 'https://gitlab.com/api/0/projects/sentry-org/sentry-project' }
enabled { true }
token { 'access_token_123' }
project_name { 'Sentry Project' }
Loading
Loading
# frozen_string_literal: true
require 'spec_helper'
describe 'View error details page', :js, :use_clean_rails_memory_store_caching, :sidekiq_inline do
include_context 'sentry error tracking context feature'
context 'with current user as project owner' do
before do
sign_in(project.owner)
visit details_project_error_tracking_index_path(project, issue_id: issue_id)
end
it_behaves_like 'error tracking show page'
end
context 'with current user as project guest' do
let_it_be(:user) { create(:user) }
before do
project.add_guest(user)
sign_in(user)
visit details_project_error_tracking_index_path(project, issue_id: issue_id)
end
it 'renders not found' do
expect(page).to have_content('Page Not Found')
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe 'View error details page', :js, :use_clean_rails_memory_store_caching, :sidekiq_inline do
include_context 'sentry error tracking context feature'
let_it_be(:issues_response_body) { fixture_file('sentry/issues_sample_response.json') }
let_it_be(:issues_response) { JSON.parse(issues_response_body) }
let(:issues_api_url) { "#{sentry_api_urls.issues_url}?limit=20&query=is:unresolved" }
before do
stub_request(:get, issues_api_url).with(
headers: { 'Authorization' => 'Bearer access_token_123' }
).to_return(status: 200, body: issues_response_body, headers: { 'Content-Type' => 'application/json' })
end
context 'with current user as project owner' do
before do
sign_in(project.owner)
visit project_error_tracking_index_path(project)
end
it_behaves_like 'error tracking index page'
end
# A bug caused the detail link to be broken for all users but the project owner
context 'with current user as project maintainer' do
let_it_be(:user) { create(:user) }
before do
project.add_maintainer(user)
sign_in(user)
visit project_error_tracking_index_path(project)
end
it_behaves_like 'error tracking index page'
end
context 'with error tracking settings disabled' do
before do
project_error_tracking_settings.update(enabled: false)
sign_in(project.owner)
visit project_error_tracking_index_path(project)
end
it 'renders call to action' do
expect(page).to have_content('Enable error tracking')
end
end
context 'with current user as project guest' do
let_it_be(:user) { create(:user) }
before do
project.add_guest(user)
sign_in(user)
visit project_error_tracking_index_path(project)
end
it 'renders not found' do
expect(page).to have_content('Page Not Found')
end
end
end
{
"type": "object",
"properties" : {
"source_id": { "type": "integer" },
"source_name": { "type": "string" },
"source_type": { "type": "string" },
"access_level": { "type": "integer" }
},
"additionalProperties": false
}
{
"type": "array",
"items": { "$ref": "membership.json" }
}
Loading
Loading
@@ -38,7 +38,7 @@
},
"firstSeen": "2018-11-06T21:19:55Z",
"hasSeen": false,
"id": "11",
"id": "503504",
"isBookmarked": false,
"isPublic": false,
"isSubscribed": true,
Loading
Loading
@@ -72,64 +72,232 @@
"shortId": "PUMP-STATION-1",
"stats": {
"24h": [
[1541451600.0, 557],
[1541455200.0, 473],
[1541458800.0, 914],
[1541462400.0, 991],
[1541466000.0, 925],
[1541469600.0, 881],
[1541473200.0, 182],
[1541476800.0, 490],
[1541480400.0, 820],
[1541484000.0, 322],
[1541487600.0, 836],
[1541491200.0, 565],
[1541494800.0, 758],
[1541498400.0, 880],
[1541502000.0, 677],
[1541505600.0, 381],
[1541509200.0, 814],
[1541512800.0, 329],
[1541516400.0, 446],
[1541520000.0, 731],
[1541523600.0, 111],
[1541527200.0, 926],
[1541530800.0, 772],
[1541534400.0, 400],
[1541538000.0, 943]
[
1541451600.0,
557
],
[
1541455200.0,
473
],
[
1541458800.0,
914
],
[
1541462400.0,
991
],
[
1541466000.0,
925
],
[
1541469600.0,
881
],
[
1541473200.0,
182
],
[
1541476800.0,
490
],
[
1541480400.0,
820
],
[
1541484000.0,
322
],
[
1541487600.0,
836
],
[
1541491200.0,
565
],
[
1541494800.0,
758
],
[
1541498400.0,
880
],
[
1541502000.0,
677
],
[
1541505600.0,
381
],
[
1541509200.0,
814
],
[
1541512800.0,
329
],
[
1541516400.0,
446
],
[
1541520000.0,
731
],
[
1541523600.0,
111
],
[
1541527200.0,
926
],
[
1541530800.0,
772
],
[
1541534400.0,
400
],
[
1541538000.0,
943
]
],
"30d": [
[1538870400.0, 565],
[1538956800.0, 12862],
[1539043200.0, 15617],
[1539129600.0, 10809],
[1539216000.0, 15065],
[1539302400.0, 12927],
[1539388800.0, 12994],
[1539475200.0, 13139],
[1539561600.0, 11838],
[1539648000.0, 12088],
[1539734400.0, 12338],
[1539820800.0, 12768],
[1539907200.0, 12816],
[1539993600.0, 15356],
[1540080000.0, 10910],
[1540166400.0, 12306],
[1540252800.0, 12912],
[1540339200.0, 14700],
[1540425600.0, 11890],
[1540512000.0, 11684],
[1540598400.0, 13510],
[1540684800.0, 12625],
[1540771200.0, 12811],
[1540857600.0, 13180],
[1540944000.0, 14651],
[1541030400.0, 14161],
[1541116800.0, 12612],
[1541203200.0, 14316],
[1541289600.0, 14742],
[1541376000.0, 12505],
[1541462400.0, 14180]
[
1538870400.0,
565
],
[
1538956800.0,
12862
],
[
1539043200.0,
15617
],
[
1539129600.0,
10809
],
[
1539216000.0,
15065
],
[
1539302400.0,
12927
],
[
1539388800.0,
12994
],
[
1539475200.0,
13139
],
[
1539561600.0,
11838
],
[
1539648000.0,
12088
],
[
1539734400.0,
12338
],
[
1539820800.0,
12768
],
[
1539907200.0,
12816
],
[
1539993600.0,
15356
],
[
1540080000.0,
10910
],
[
1540166400.0,
12306
],
[
1540252800.0,
12912
],
[
1540339200.0,
14700
],
[
1540425600.0,
11890
],
[
1540512000.0,
11684
],
[
1540598400.0,
13510
],
[
1540684800.0,
12625
],
[
1540771200.0,
12811
],
[
1540857600.0,
13180
],
[
1540944000.0,
14651
],
[
1541030400.0,
14161
],
[
1541116800.0,
12612
],
[
1541203200.0,
14316
],
[
1541289600.0,
14742
],
[
1541376000.0,
12505
],
[
1541462400.0,
14180
]
]
},
"status": "unresolved",
Loading
Loading
{
"message": "Successfully linked issue.",
"link": "https://gitlab.com/test/tanuki-inc/issues/3",
"id": 3,
"label": "GL-3"
}
Loading
Loading
@@ -10,7 +10,14 @@ describe GitlabSchema.types['Group'] do
it { expect(described_class).to require_graphql_authorizations(:read_group) }
 
it 'has the expected fields' do
expected_fields = %w[web_url avatar_url mentions_disabled parent]
expected_fields = %w[
id name path full_name full_path description description_html visibility
lfs_enabled request_access_enabled projects root_storage_statistics
web_url avatar_url share_with_group_lock project_creation_level
subgroup_creation_level require_two_factor_authentication
two_factor_grace_period auto_devops_enabled emails_disabled
mentions_disabled parent
]
 
is_expected.to include_graphql_fields(*expected_fields)
end
Loading
Loading
Loading
Loading
@@ -5,18 +5,18 @@ require 'spec_helper'
describe Sentry::Client::IssueLink do
include SentryClientHelpers
 
let(:error_tracking_setting) { create(:project_error_tracking_setting, api_url: sentry_url) }
let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' }
let(:client) { error_tracking_setting.sentry_client }
let_it_be(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' }
let_it_be(:error_tracking_setting) { create(:project_error_tracking_setting, api_url: sentry_url) }
let_it_be(:issue) { create(:issue, project: error_tracking_setting.project) }
 
let(:issue_link_sample_response) { JSON.parse(fixture_file('sentry/issue_link_sample_response.json')) }
let(:client) { error_tracking_setting.sentry_client }
let(:sentry_issue_id) { 11111111 }
 
describe '#create_issue_link' do
let(:sentry_issue_link_url) { "https://sentrytest.gitlab.com/api/0/groups/#{sentry_issue_id}/integrations/#{integration_id}/" }
let(:integration_id) { 44444 }
let(:sentry_issue_id) { 11111111 }
let(:issue) { create(:issue, project: error_tracking_setting.project) }
 
let(:sentry_issue_link_url) { "https://sentrytest.gitlab.com/api/0/groups/#{sentry_issue_id}/integrations/#{integration_id}/" }
let(:issue_link_sample_response) { JSON.parse(fixture_file('sentry/global_integration_link_sample_response.json')) }
let(:sentry_api_response) { issue_link_sample_response }
let!(:sentry_api_request) { stub_sentry_request(sentry_issue_link_url, :put, body: sentry_api_response, status: 201) }
 
Loading
Loading
@@ -37,5 +37,29 @@ describe Sentry::Client::IssueLink do
 
it_behaves_like 'maps Sentry exceptions', :put
end
context 'when integration_id is not provided' do
let(:sentry_issue_link_url) { "https://sentrytest.gitlab.com/api/0/issues/#{sentry_issue_id}/plugins/gitlab/link/" }
let(:integration_id) { nil }
let(:issue_link_sample_response) { JSON.parse(fixture_file('sentry/plugin_link_sample_response.json')) }
let!(:sentry_api_request) { stub_sentry_request(sentry_issue_link_url, :post, body: sentry_api_response) }
it_behaves_like 'calls sentry api'
it { is_expected.to be_present }
context 'redirects' do
let(:sentry_api_url) { sentry_issue_link_url }
it_behaves_like 'no Sentry redirects', :post
end
context 'when exception is raised' do
let(:sentry_request_url) { sentry_issue_link_url }
it_behaves_like 'maps Sentry exceptions', :post
end
end
end
end
Loading
Loading
@@ -8,7 +8,7 @@ describe Sentry::Client::Issue do
let(:token) { 'test-token' }
let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0' }
let(:client) { Sentry::Client.new(sentry_url, token) }
let(:issue_id) { 11 }
let(:issue_id) { 503504 }
 
describe '#list_issues' do
shared_examples 'issues have correct return type' do |klass|
Loading
Loading
@@ -243,7 +243,7 @@ describe Sentry::Client::Issue do
end
 
it 'has a correct external URL' do
expect(subject.external_url).to eq('https://sentrytest.gitlab.com/api/0/issues/11')
expect(subject.external_url).to eq('https://sentrytest.gitlab.com/api/0/issues/503504')
end
 
it 'issue has a correct external base url' do
Loading
Loading
Loading
Loading
@@ -2100,6 +2100,83 @@ describe API::Users do
end
end
 
describe "GET /users/:id/memberships" do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:group) { create(:group) }
let(:requesting_user) { create(:user) }
before_all do
project.add_guest(user)
group.add_guest(user)
end
it "responses with 403" do
get api("/users/#{user.id}/memberships", requesting_user)
expect(response).to have_gitlab_http_status(:forbidden)
end
context 'requested by admin user' do
let(:requesting_user) { create(:user, :admin) }
it "responses successfully" do
get api("/users/#{user.id}/memberships", requesting_user)
aggregate_failures 'expect successful response including groups and projects' do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/memberships')
expect(response).to include_pagination_headers
expect(json_response).to contain_exactly(
a_hash_including('source_type' => 'Project'),
a_hash_including('source_type' => 'Namespace')
)
end
end
it 'does not submit N+1 DB queries' do
# Avoid setup queries
get api("/users/#{user.id}/memberships", requesting_user)
control = ActiveRecord::QueryRecorder.new do
get api("/users/#{user.id}/memberships", requesting_user)
end
create_list(:project, 5).map { |project| project.add_guest(user) }
expect do
get api("/users/#{user.id}/memberships", requesting_user)
end.not_to exceed_query_limit(control)
end
context 'with type filter' do
it "only returns project memberships" do
get api("/users/#{user.id}/memberships?type=Project", requesting_user)
aggregate_failures do
expect(json_response).to contain_exactly(a_hash_including('source_type' => 'Project'))
expect(json_response).not_to include(a_hash_including('source_type' => 'Namespace'))
end
end
it "only returns group memberships" do
get api("/users/#{user.id}/memberships?type=Namespace", requesting_user)
aggregate_failures do
expect(json_response).to contain_exactly(a_hash_including('source_type' => 'Namespace'))
expect(json_response).not_to include(a_hash_including('source_type' => 'Project'))
end
end
it "recognizes unsupported types" do
get api("/users/#{user.id}/memberships?type=foo", requesting_user)
expect(response).to have_gitlab_http_status(:bad_request)
end
end
end
end
context "user activities", :clean_gitlab_redis_shared_state do
let!(:old_active_user) { create(:user, last_activity_on: Time.utc(2000, 1, 1)) }
let!(:newly_active_user) { create(:user, last_activity_on: 2.days.ago.midday) }
Loading
Loading
# frozen_string_literal: true
shared_context 'sentry error tracking context feature' do
include ReactiveCachingHelpers
let_it_be(:project) { create(:project) }
let_it_be(:project_error_tracking_settings) { create(:project_error_tracking_setting, project: project) }
let_it_be(:issue_response_body) { fixture_file('sentry/issue_sample_response.json') }
let_it_be(:issue_response) { JSON.parse(issue_response_body) }
let_it_be(:event_response_body) { fixture_file('sentry/issue_latest_event_sample_response.json') }
let_it_be(:event_response) { JSON.parse(event_response_body) }
let(:sentry_api_urls) { Sentry::ApiUrls.new(project_error_tracking_settings.api_url) }
let(:issue_id) { issue_response['id'] }
before do
stub_request(:get, sentry_api_urls.issue_url(issue_id)).with(
headers: { 'Authorization' => 'Bearer access_token_123' }
).to_return(status: 200, body: issue_response_body, headers: { 'Content-Type' => 'application/json' })
stub_request(:get, sentry_api_urls.issue_latest_event_url(issue_id)).with(
headers: { 'Authorization' => 'Bearer access_token_123' }
).to_return(status: 200, body: event_response_body, headers: { 'Content-Type' => 'application/json' })
end
end
# frozen_string_literal: true
shared_examples 'error tracking index page' do
it 'renders the error index page' do
within('div.js-title-container') do
expect(page).to have_content(project.namespace.name)
expect(page).to have_content(project.name)
end
within('div.error-list') do
expect(page).to have_content('Error')
expect(page).to have_content('Events')
expect(page).to have_content('Users')
expect(page).to have_content('Last Seen')
end
end
it 'renders the error index data' do
Timecop.freeze(2020, 01, 01, 12, 0, 0) do
within('div.error-list') do
expect(page).to have_content(issues_response[0]['title'])
expect(page).to have_content(issues_response[0]['count'].to_s)
expect(page).to have_content(issues_response[0]['last_seen'])
expect(page).to have_content('1 year ago')
end
end
end
context 'when error is clicked' do
before do
click_on issues_response[0]['title']
end
it 'loads the error page' do
expect(page).to have_content('Error details')
end
end
end
shared_examples 'expanded stack trace context' do |selected_line: nil, expected_line: 1|
it 'expands the stack trace context' do
within('div.stacktrace') do
find("div.file-holder:nth-child(#{selected_line}) svg.ic-chevron-right").click if selected_line
expanded_line = find("div.file-holder:nth-child(#{expected_line})")
expect(expanded_line).to have_css('svg.ic-chevron-down')
event_response['entries'][0]['data']['values'][0]['stacktrace']['frames'][-expected_line]['context'].each do |context|
expect(page).to have_content(context[0])
end
end
end
end
shared_examples 'error tracking show page' do
it 'renders the error details' do
release_short_version = issue_response['firstRelease']['shortVersion']
Timecop.freeze(2020, 01, 01, 12, 0, 0) do
expect(page).to have_content('1 month ago by raven.scripts.runner in main')
expect(page).to have_content(issue_response['metadata']['title'])
expect(page).to have_content('level: error')
expect(page).to have_content('Error details')
expect(page).to have_content('GitLab Issue: https://gitlab.com/gitlab-org/gitlab/issues/1')
expect(page).to have_content("Sentry event: https://sentrytest.gitlab.com/sentry-org/sentry-project/issues/#{issue_id}")
expect(page).to have_content("First seen: 1 year ago (2018-11-06 9:19:55PM UTC) Release: #{release_short_version}")
expect(page).to have_content('Events: 1')
expect(page).to have_content('Users: 0')
end
end
it 'renders the stack trace heading' do
expect(page).to have_content('Stack trace')
end
it 'renders the stack trace' do
event_response['entries'][0]['data']['values'][0]['stacktrace']['frames'].each do |frame|
expect(frame['filename']).not_to be_nil
expect(page).to have_content(frame['filename'])
end
end
# The first line is expanded by default if no line is selected
it_behaves_like 'expanded stack trace context', selected_line: nil, expected_line: 1
it_behaves_like 'expanded stack trace context', selected_line: 8, expected_line: 8
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment