Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
be3e24ea3c
commit
180cd023a1
|
@ -142,6 +142,7 @@ dependency_scanning:
|
|||
variables:
|
||||
DOCKER_DRIVER: overlay2
|
||||
DOCKER_TLS_CERTDIR: ""
|
||||
DS_EXCLUDED_PATHS: "qa/qa/ee/fixtures/secure_premade_reports,spec,ee/spec"
|
||||
allow_failure: true
|
||||
services:
|
||||
- docker:stable-dind
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.81.0
|
||||
1.83.0
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.12.0
|
||||
1.14.0
|
||||
|
|
|
@ -44,6 +44,7 @@ const Api = {
|
|||
releasePath: '/api/:version/projects/:id/releases/:tag_name',
|
||||
mergeRequestsPipeline: '/api/:version/projects/:id/merge_requests/:merge_request_iid/pipelines',
|
||||
adminStatisticsPath: '/api/:version/application/statistics',
|
||||
pipelineSinglePath: '/api/:version/projects/:id/pipelines/:pipeline_id',
|
||||
|
||||
group(groupId, callback) {
|
||||
const url = Api.buildUrl(Api.groupPath).replace(':id', groupId);
|
||||
|
@ -448,6 +449,14 @@ const Api = {
|
|||
return axios.get(url);
|
||||
},
|
||||
|
||||
pipelineSingle(id, pipelineId) {
|
||||
const url = Api.buildUrl(this.pipelineSinglePath)
|
||||
.replace(':id', encodeURIComponent(id))
|
||||
.replace(':pipeline_id', encodeURIComponent(pipelineId));
|
||||
|
||||
return axios.get(url);
|
||||
},
|
||||
|
||||
buildUrl(url) {
|
||||
return joinPaths(gon.relative_url_root || '', url.replace(':version', gon.api_version));
|
||||
},
|
||||
|
|
|
@ -17,6 +17,25 @@ module Types
|
|||
group.avatar_url(only_path: false)
|
||||
end
|
||||
|
||||
field :share_with_group_lock, GraphQL::BOOLEAN_TYPE, null: true,
|
||||
description: 'Indicates if sharing a project with another group within this group is prevented'
|
||||
|
||||
field :project_creation_level, GraphQL::STRING_TYPE, null: true, method: :project_creation_level_str,
|
||||
description: 'The permission level required to create projects in the group'
|
||||
field :subgroup_creation_level, GraphQL::STRING_TYPE, null: true, method: :subgroup_creation_level_str,
|
||||
description: 'The permission level required to create subgroups within the group'
|
||||
|
||||
field :require_two_factor_authentication, GraphQL::BOOLEAN_TYPE, null: true,
|
||||
description: 'Indicates if all users in this group are required to set up two-factor authentication'
|
||||
field :two_factor_grace_period, GraphQL::INT_TYPE, null: true,
|
||||
description: 'Time before two-factor authentication is enforced'
|
||||
|
||||
field :auto_devops_enabled, GraphQL::BOOLEAN_TYPE, null: true,
|
||||
description: 'Indicates whether Auto DevOps is enabled for all projects within this group'
|
||||
|
||||
field :emails_disabled, GraphQL::BOOLEAN_TYPE, null: true,
|
||||
description: 'Indicates if a group has email notifications disabled'
|
||||
|
||||
field :mentions_disabled, GraphQL::BOOLEAN_TYPE, null: true,
|
||||
description: 'Indicates if a group is disabled from getting mentioned'
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ class Member < ApplicationRecord
|
|||
scope :with_user, -> (user) { where(user: user) }
|
||||
|
||||
scope :with_source_id, ->(source_id) { where(source_id: source_id) }
|
||||
scope :including_source, -> { includes(:source) }
|
||||
|
||||
scope :order_name_asc, -> { left_join_users.reorder(Gitlab::Database.nulls_last_order('users.name', 'ASC')) }
|
||||
scope :order_name_desc, -> { left_join_users.reorder(Gitlab::Database.nulls_last_order('users.name', 'DESC')) }
|
||||
|
|
|
@ -20,17 +20,11 @@ class ErrorTrackingIssueLinkWorker
|
|||
def perform(issue_id)
|
||||
@issue = Issue.find_by_id(issue_id)
|
||||
|
||||
return unless issue && error_tracking && sentry_issue_id
|
||||
return unless valid?
|
||||
|
||||
try_obtain_lease do
|
||||
logger.info("Linking Sentry issue #{sentry_issue_id} to GitLab issue #{issue.id}")
|
||||
|
||||
if integration_id.nil?
|
||||
logger.info("Sentry integration unavailable for #{error_tracking.api_url}")
|
||||
|
||||
break
|
||||
end
|
||||
|
||||
sentry_client.create_issue_link(integration_id, sentry_issue_id, issue)
|
||||
rescue Sentry::Client::Error
|
||||
logger.info("Failed to link Sentry issue #{sentry_issue_id} to GitLab issue #{issue.id}")
|
||||
|
@ -39,6 +33,10 @@ class ErrorTrackingIssueLinkWorker
|
|||
|
||||
private
|
||||
|
||||
def valid?
|
||||
issue && error_tracking && sentry_issue_id
|
||||
end
|
||||
|
||||
def error_tracking
|
||||
strong_memoize(:error_tracking) do
|
||||
issue.project.error_tracking_setting
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Packages published to the package registry via CI/CD with a CI_JOB_TOKEN will
|
||||
display pipeline information on the details page
|
||||
merge_request: 22485
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add users memberships endpoints for admins
|
||||
merge_request: 22518
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Allow setting minimum concurrency for sidekiq-cluster processes
|
||||
merge_request: 23408
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Backfill missing GraphQL API Group type properties
|
||||
merge_request: 23389
|
||||
author: Fabio Huser
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Upgrade to Gitaly v1.83.0
|
||||
merge_request: 23431
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Upgrade pages to 1.14.0
|
||||
merge_request: 23317
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Sync GitLab issues with Sentry plugin integration
|
||||
merge_request: 23355
|
||||
author:
|
||||
type: added
|
|
@ -124,9 +124,18 @@ number of threads that equals the number of queues, plus one spare thread.
|
|||
For example, a process that handles the `process_commit` and `post_receive`
|
||||
queues will use three threads in total.
|
||||
|
||||
## Limiting concurrency
|
||||
## Managing concurrency
|
||||
|
||||
To limit the concurrency of the Sidekiq process:
|
||||
When setting the maximum concurrency, keep in mind this normally should
|
||||
not exceed the number of CPU cores available. The values in the examples
|
||||
below are arbitrary and not particular recommendations.
|
||||
|
||||
Each thread requires a Redis connection, so adding threads may increase Redis
|
||||
latency and potentially cause client timeouts. See the [Sidekiq documentation
|
||||
about Redis](https://github.com/mperham/sidekiq/wiki/Using-Redis) for more
|
||||
details.
|
||||
|
||||
### When running a single Sidekiq process (default)
|
||||
|
||||
1. Edit `/etc/gitlab/gitlab.rb` and add:
|
||||
|
||||
|
@ -140,11 +149,14 @@ To limit the concurrency of the Sidekiq process:
|
|||
sudo gitlab-ctl reconfigure
|
||||
```
|
||||
|
||||
To limit the max concurrency of the Sidekiq cluster processes:
|
||||
This will set the concurrency (number of threads) for the Sidekiq process.
|
||||
|
||||
### When running Sidekiq cluster
|
||||
|
||||
1. Edit `/etc/gitlab/gitlab.rb` and add:
|
||||
|
||||
```ruby
|
||||
sidekiq_cluster['min_concurrency'] = 15
|
||||
sidekiq_cluster['max_concurrency'] = 25
|
||||
```
|
||||
|
||||
|
@ -154,14 +166,21 @@ To limit the max concurrency of the Sidekiq cluster processes:
|
|||
sudo gitlab-ctl reconfigure
|
||||
```
|
||||
|
||||
For each queue group, the concurrency factor will be set to `min(number of queues, N)`.
|
||||
Setting the value to 0 will disable the limit. Keep in mind this normally would
|
||||
not exceed the number of CPU cores available.
|
||||
`min_concurrency` and `max_concurrency` are independent; one can be set without
|
||||
the other. Setting `min_concurrency` to 0 will disable the limit.
|
||||
|
||||
Each thread requires a Redis connection, so adding threads may
|
||||
increase Redis latency and potentially cause client timeouts. See the [Sidekiq
|
||||
documentation about Redis](https://github.com/mperham/sidekiq/wiki/Using-Redis)
|
||||
for more details.
|
||||
For each queue group, let N be one more than the number of queues. The
|
||||
concurrency factor will be set to:
|
||||
|
||||
1. `N`, if it's between `min_concurrency` and `max_concurrency`.
|
||||
1. `max_concurrency`, if `N` exceeds this value.
|
||||
1. `min_concurrency`, if `N` is less than this value.
|
||||
|
||||
If `min_concurrency` is equal to `max_concurrency`, then this value will be used
|
||||
regardless of the number of queues.
|
||||
|
||||
When `min_concurrency` is greater than `max_concurrency`, it is treated as
|
||||
being equal to `max_concurrency`.
|
||||
|
||||
## Modifying the check interval
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
>**Note:** This feature was introduced in GitLab 8.11
|
||||
|
||||
**Valid access levels**
|
||||
## Valid access levels
|
||||
|
||||
The access levels are defined in the `Gitlab::Access` module. Currently, these levels are recognized:
|
||||
|
||||
|
|
|
@ -2434,6 +2434,11 @@ type GrafanaIntegration {
|
|||
}
|
||||
|
||||
type Group {
|
||||
"""
|
||||
Indicates whether Auto DevOps is enabled for all projects within this group
|
||||
"""
|
||||
autoDevopsEnabled: Boolean
|
||||
|
||||
"""
|
||||
Avatar URL of the group
|
||||
"""
|
||||
|
@ -2449,6 +2454,11 @@ type Group {
|
|||
"""
|
||||
descriptionHtml: String
|
||||
|
||||
"""
|
||||
Indicates if a group has email notifications disabled
|
||||
"""
|
||||
emailsDisabled: Boolean
|
||||
|
||||
"""
|
||||
Find a single epic
|
||||
"""
|
||||
|
@ -2623,6 +2633,11 @@ type Group {
|
|||
"""
|
||||
path: String!
|
||||
|
||||
"""
|
||||
The permission level required to create projects in the group
|
||||
"""
|
||||
projectCreationLevel: String
|
||||
|
||||
"""
|
||||
Projects within this namespace
|
||||
"""
|
||||
|
@ -2658,11 +2673,26 @@ type Group {
|
|||
"""
|
||||
requestAccessEnabled: Boolean
|
||||
|
||||
"""
|
||||
Indicates if all users in this group are required to set up two-factor authentication
|
||||
"""
|
||||
requireTwoFactorAuthentication: Boolean
|
||||
|
||||
"""
|
||||
Aggregated storage statistics of the namespace. Only available for root namespaces
|
||||
"""
|
||||
rootStorageStatistics: RootStorageStatistics
|
||||
|
||||
"""
|
||||
Indicates if sharing a project with another group within this group is prevented
|
||||
"""
|
||||
shareWithGroupLock: Boolean
|
||||
|
||||
"""
|
||||
The permission level required to create subgroups within the group
|
||||
"""
|
||||
subgroupCreationLevel: String
|
||||
|
||||
"""
|
||||
Time logged in issues by group members
|
||||
"""
|
||||
|
@ -2698,6 +2728,11 @@ type Group {
|
|||
startDate: Time!
|
||||
): TimelogConnection!
|
||||
|
||||
"""
|
||||
Time before two-factor authentication is enforced
|
||||
"""
|
||||
twoFactorGracePeriod: Int
|
||||
|
||||
"""
|
||||
Permissions for the current user on the resource
|
||||
"""
|
||||
|
|
|
@ -3044,6 +3044,20 @@
|
|||
"name": "Group",
|
||||
"description": null,
|
||||
"fields": [
|
||||
{
|
||||
"name": "autoDevopsEnabled",
|
||||
"description": "Indicates whether Auto DevOps is enabled for all projects within this group",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "avatarUrl",
|
||||
"description": "Avatar URL of the group",
|
||||
|
@ -3086,6 +3100,20 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "emailsDisabled",
|
||||
"description": "Indicates if a group has email notifications disabled",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "epic",
|
||||
"description": "Find a single epic",
|
||||
|
@ -3524,6 +3552,20 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "projectCreationLevel",
|
||||
"description": "The permission level required to create projects in the group",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "projects",
|
||||
"description": "Projects within this namespace",
|
||||
|
@ -3605,6 +3647,20 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "requireTwoFactorAuthentication",
|
||||
"description": "Indicates if all users in this group are required to set up two-factor authentication",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "rootStorageStatistics",
|
||||
"description": "Aggregated storage statistics of the namespace. Only available for root namespaces",
|
||||
|
@ -3619,6 +3675,34 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "shareWithGroupLock",
|
||||
"description": "Indicates if sharing a project with another group within this group is prevented",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "subgroupCreationLevel",
|
||||
"description": "The permission level required to create subgroups within the group",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "timelogs",
|
||||
"description": "Time logged in issues by group members",
|
||||
|
@ -3704,6 +3788,20 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "twoFactorGracePeriod",
|
||||
"description": "Time before two-factor authentication is enforced",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "userPermissions",
|
||||
"description": "Permissions for the current user on the resource",
|
||||
|
|
|
@ -393,6 +393,13 @@ Autogenerated return type of EpicTreeReorder
|
|||
| `userPermissions` | GroupPermissions! | Permissions for the current user on the resource |
|
||||
| `webUrl` | String! | Web URL of the group |
|
||||
| `avatarUrl` | String | Avatar URL of the group |
|
||||
| `shareWithGroupLock` | Boolean | Indicates if sharing a project with another group within this group is prevented |
|
||||
| `projectCreationLevel` | String | The permission level required to create projects in the group |
|
||||
| `subgroupCreationLevel` | String | The permission level required to create subgroups within the group |
|
||||
| `requireTwoFactorAuthentication` | Boolean | Indicates if all users in this group are required to set up two-factor authentication |
|
||||
| `twoFactorGracePeriod` | Int | Time before two-factor authentication is enforced |
|
||||
| `autoDevopsEnabled` | Boolean | Indicates whether Auto DevOps is enabled for all projects within this group |
|
||||
| `emailsDisabled` | Boolean | Indicates if a group has email notifications disabled |
|
||||
| `mentionsDisabled` | Boolean | Indicates if a group is disabled from getting mentioned |
|
||||
| `parent` | Group | Parent group |
|
||||
| `epicsEnabled` | Boolean | Indicates if Epics are enabled for namespace |
|
||||
|
|
|
@ -1405,3 +1405,53 @@ Example response:
|
|||
```
|
||||
|
||||
Please note that `last_activity_at` is deprecated, please use `last_activity_on`.
|
||||
|
||||
## User memberships (admin only)
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/merge_requests/22518) in GitLab 12.8.
|
||||
|
||||
Lists all projects and groups a user is a member of. This endpoint is available for admins only.
|
||||
It returns the `source_id`, `source_name`, `source_type` and `access_level` of a membership.
|
||||
Source can be of type `Namespace` (representing a group) or `Project`. The response represents only direct memberships. Inherited memberships, for example in subgroups, will not be included.
|
||||
Access levels will be represented by an integer value. Read more about the meaning of access level values [here](access_requests.md#valid-access-levels).
|
||||
|
||||
```
|
||||
GET /users/:id/memberships
|
||||
```
|
||||
|
||||
Parameters:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer | yes | The ID of a specified user |
|
||||
| `type` | string | no | Filter memberships by type. Can be either `Project` or `Namespace` |
|
||||
|
||||
Returns:
|
||||
|
||||
- `200 OK` on success.
|
||||
- `404 User Not Found` if user cannot be found.
|
||||
- `403 Forbidden` when not requested by an admin.
|
||||
- `400 Bad Request` when requested type is not supported.
|
||||
|
||||
```bash
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/users/<user_id>/memberships
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"source_id": 1,
|
||||
"source_name": "Project one",
|
||||
"source_type": "Project",
|
||||
"access_level": "20"
|
||||
},
|
||||
{
|
||||
"source_id": 3,
|
||||
"source_name": "Group three",
|
||||
"source_type": "Namespace",
|
||||
"access_level": "20"
|
||||
},
|
||||
]
|
||||
```
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
@ -14,11 +28,22 @@ module Sentry
|
|||
http_put(issue_link_url, params)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def issue_link_api_url(integration_id, sentry_issue_identifier)
|
||||
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_identifier}/integrations/#{integration_id}/"
|
||||
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 plugin_link_api_url(sentry_issue_id)
|
||||
issue_link_url = URI(url)
|
||||
issue_link_url.path = "/api/0/issues/#{sentry_issue_id}/plugins/gitlab/link/"
|
||||
|
||||
issue_link_url
|
||||
end
|
||||
|
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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' }
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
# 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
|
|
@ -1,69 +0,0 @@
|
|||
# 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
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"type": "object",
|
||||
"properties" : {
|
||||
"source_id": { "type": "integer" },
|
||||
"source_name": { "type": "string" },
|
||||
"source_type": { "type": "string" },
|
||||
"access_level": { "type": "integer" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"type": "array",
|
||||
"items": { "$ref": "membership.json" }
|
||||
}
|
|
@ -38,7 +38,7 @@
|
|||
},
|
||||
"firstSeen": "2018-11-06T21:19:55Z",
|
||||
"hasSeen": false,
|
||||
"id": "11",
|
||||
"id": "503504",
|
||||
"isBookmarked": false,
|
||||
"isPublic": false,
|
||||
"isSubscribed": true,
|
||||
|
@ -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",
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"message": "Successfully linked issue.",
|
||||
"link": "https://gitlab.com/test/tanuki-inc/issues/3",
|
||||
"id": 3,
|
||||
"label": "GL-3"
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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(: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(:integration_id) { 44444 }
|
||||
|
||||
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) }
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -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|
|
||||
|
@ -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
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
# 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
|
|
@ -1,86 +0,0 @@
|
|||
# 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
|
|
@ -40,14 +40,17 @@ describe ErrorTrackingIssueLinkWorker do
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples_for 'terminates after one API request' do
|
||||
shared_examples_for 'attempts to create a link via plugin' do
|
||||
it 'takes no action' do
|
||||
expect_next_instance_of(Sentry::Client) do |client|
|
||||
expect(client).to receive(:repos).with('sentry-org').and_return([repo])
|
||||
expect(client)
|
||||
.to receive(:create_issue_link)
|
||||
.with(nil, sentry_issue.sentry_issue_identifier, issue)
|
||||
.and_return(true)
|
||||
end
|
||||
expect_any_instance_of(Sentry::Client).not_to receive(:create_issue_link)
|
||||
|
||||
expect(subject).to be nil
|
||||
expect(subject).to be true
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -78,7 +81,7 @@ describe ErrorTrackingIssueLinkWorker do
|
|||
)
|
||||
end
|
||||
|
||||
it_behaves_like 'terminates after one API request'
|
||||
it_behaves_like 'attempts to create a link via plugin'
|
||||
end
|
||||
|
||||
context 'when Sentry the GitLab integration is for another project' do
|
||||
|
@ -90,7 +93,7 @@ describe ErrorTrackingIssueLinkWorker do
|
|||
)
|
||||
end
|
||||
|
||||
it_behaves_like 'terminates after one API request'
|
||||
it_behaves_like 'attempts to create a link via plugin'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue