Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
be3e24ea3c
commit
180cd023a1
|
@ -142,6 +142,7 @@ dependency_scanning:
|
||||||
variables:
|
variables:
|
||||||
DOCKER_DRIVER: overlay2
|
DOCKER_DRIVER: overlay2
|
||||||
DOCKER_TLS_CERTDIR: ""
|
DOCKER_TLS_CERTDIR: ""
|
||||||
|
DS_EXCLUDED_PATHS: "qa/qa/ee/fixtures/secure_premade_reports,spec,ee/spec"
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
services:
|
services:
|
||||||
- docker:stable-dind
|
- 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',
|
releasePath: '/api/:version/projects/:id/releases/:tag_name',
|
||||||
mergeRequestsPipeline: '/api/:version/projects/:id/merge_requests/:merge_request_iid/pipelines',
|
mergeRequestsPipeline: '/api/:version/projects/:id/merge_requests/:merge_request_iid/pipelines',
|
||||||
adminStatisticsPath: '/api/:version/application/statistics',
|
adminStatisticsPath: '/api/:version/application/statistics',
|
||||||
|
pipelineSinglePath: '/api/:version/projects/:id/pipelines/:pipeline_id',
|
||||||
|
|
||||||
group(groupId, callback) {
|
group(groupId, callback) {
|
||||||
const url = Api.buildUrl(Api.groupPath).replace(':id', groupId);
|
const url = Api.buildUrl(Api.groupPath).replace(':id', groupId);
|
||||||
|
@ -448,6 +449,14 @@ const Api = {
|
||||||
return axios.get(url);
|
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) {
|
buildUrl(url) {
|
||||||
return joinPaths(gon.relative_url_root || '', url.replace(':version', gon.api_version));
|
return joinPaths(gon.relative_url_root || '', url.replace(':version', gon.api_version));
|
||||||
},
|
},
|
||||||
|
|
|
@ -17,6 +17,25 @@ module Types
|
||||||
group.avatar_url(only_path: false)
|
group.avatar_url(only_path: false)
|
||||||
end
|
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,
|
field :mentions_disabled, GraphQL::BOOLEAN_TYPE, null: true,
|
||||||
description: 'Indicates if a group is disabled from getting mentioned'
|
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_user, -> (user) { where(user: user) }
|
||||||
|
|
||||||
scope :with_source_id, ->(source_id) { where(source_id: source_id) }
|
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_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')) }
|
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)
|
def perform(issue_id)
|
||||||
@issue = Issue.find_by_id(issue_id)
|
@issue = Issue.find_by_id(issue_id)
|
||||||
|
|
||||||
return unless issue && error_tracking && sentry_issue_id
|
return unless valid?
|
||||||
|
|
||||||
try_obtain_lease do
|
try_obtain_lease do
|
||||||
logger.info("Linking Sentry issue #{sentry_issue_id} to GitLab issue #{issue.id}")
|
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)
|
sentry_client.create_issue_link(integration_id, sentry_issue_id, issue)
|
||||||
rescue Sentry::Client::Error
|
rescue Sentry::Client::Error
|
||||||
logger.info("Failed to link Sentry issue #{sentry_issue_id} to GitLab issue #{issue.id}")
|
logger.info("Failed to link Sentry issue #{sentry_issue_id} to GitLab issue #{issue.id}")
|
||||||
|
@ -39,6 +33,10 @@ class ErrorTrackingIssueLinkWorker
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def valid?
|
||||||
|
issue && error_tracking && sentry_issue_id
|
||||||
|
end
|
||||||
|
|
||||||
def error_tracking
|
def error_tracking
|
||||||
strong_memoize(:error_tracking) do
|
strong_memoize(:error_tracking) do
|
||||||
issue.project.error_tracking_setting
|
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`
|
For example, a process that handles the `process_commit` and `post_receive`
|
||||||
queues will use three threads in total.
|
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:
|
1. Edit `/etc/gitlab/gitlab.rb` and add:
|
||||||
|
|
||||||
|
@ -140,11 +149,14 @@ To limit the concurrency of the Sidekiq process:
|
||||||
sudo gitlab-ctl reconfigure
|
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:
|
1. Edit `/etc/gitlab/gitlab.rb` and add:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
|
sidekiq_cluster['min_concurrency'] = 15
|
||||||
sidekiq_cluster['max_concurrency'] = 25
|
sidekiq_cluster['max_concurrency'] = 25
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -154,14 +166,21 @@ To limit the max concurrency of the Sidekiq cluster processes:
|
||||||
sudo gitlab-ctl reconfigure
|
sudo gitlab-ctl reconfigure
|
||||||
```
|
```
|
||||||
|
|
||||||
For each queue group, the concurrency factor will be set to `min(number of queues, N)`.
|
`min_concurrency` and `max_concurrency` are independent; one can be set without
|
||||||
Setting the value to 0 will disable the limit. Keep in mind this normally would
|
the other. Setting `min_concurrency` to 0 will disable the limit.
|
||||||
not exceed the number of CPU cores available.
|
|
||||||
|
|
||||||
Each thread requires a Redis connection, so adding threads may
|
For each queue group, let N be one more than the number of queues. The
|
||||||
increase Redis latency and potentially cause client timeouts. See the [Sidekiq
|
concurrency factor will be set to:
|
||||||
documentation about Redis](https://github.com/mperham/sidekiq/wiki/Using-Redis)
|
|
||||||
for more details.
|
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
|
## Modifying the check interval
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
>**Note:** This feature was introduced in GitLab 8.11
|
>**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:
|
The access levels are defined in the `Gitlab::Access` module. Currently, these levels are recognized:
|
||||||
|
|
||||||
|
|
|
@ -2434,6 +2434,11 @@ type GrafanaIntegration {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Group {
|
type Group {
|
||||||
|
"""
|
||||||
|
Indicates whether Auto DevOps is enabled for all projects within this group
|
||||||
|
"""
|
||||||
|
autoDevopsEnabled: Boolean
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Avatar URL of the group
|
Avatar URL of the group
|
||||||
"""
|
"""
|
||||||
|
@ -2449,6 +2454,11 @@ type Group {
|
||||||
"""
|
"""
|
||||||
descriptionHtml: String
|
descriptionHtml: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
Indicates if a group has email notifications disabled
|
||||||
|
"""
|
||||||
|
emailsDisabled: Boolean
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Find a single epic
|
Find a single epic
|
||||||
"""
|
"""
|
||||||
|
@ -2623,6 +2633,11 @@ type Group {
|
||||||
"""
|
"""
|
||||||
path: String!
|
path: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
The permission level required to create projects in the group
|
||||||
|
"""
|
||||||
|
projectCreationLevel: String
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Projects within this namespace
|
Projects within this namespace
|
||||||
"""
|
"""
|
||||||
|
@ -2658,11 +2673,26 @@ type Group {
|
||||||
"""
|
"""
|
||||||
requestAccessEnabled: Boolean
|
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
|
Aggregated storage statistics of the namespace. Only available for root namespaces
|
||||||
"""
|
"""
|
||||||
rootStorageStatistics: RootStorageStatistics
|
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
|
Time logged in issues by group members
|
||||||
"""
|
"""
|
||||||
|
@ -2698,6 +2728,11 @@ type Group {
|
||||||
startDate: Time!
|
startDate: Time!
|
||||||
): TimelogConnection!
|
): TimelogConnection!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Time before two-factor authentication is enforced
|
||||||
|
"""
|
||||||
|
twoFactorGracePeriod: Int
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Permissions for the current user on the resource
|
Permissions for the current user on the resource
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -3044,6 +3044,20 @@
|
||||||
"name": "Group",
|
"name": "Group",
|
||||||
"description": null,
|
"description": null,
|
||||||
"fields": [
|
"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",
|
"name": "avatarUrl",
|
||||||
"description": "Avatar URL of the group",
|
"description": "Avatar URL of the group",
|
||||||
|
@ -3086,6 +3100,20 @@
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"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",
|
"name": "epic",
|
||||||
"description": "Find a single epic",
|
"description": "Find a single epic",
|
||||||
|
@ -3524,6 +3552,20 @@
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"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",
|
"name": "projects",
|
||||||
"description": "Projects within this namespace",
|
"description": "Projects within this namespace",
|
||||||
|
@ -3605,6 +3647,20 @@
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"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",
|
"name": "rootStorageStatistics",
|
||||||
"description": "Aggregated storage statistics of the namespace. Only available for root namespaces",
|
"description": "Aggregated storage statistics of the namespace. Only available for root namespaces",
|
||||||
|
@ -3619,6 +3675,34 @@
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"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",
|
"name": "timelogs",
|
||||||
"description": "Time logged in issues by group members",
|
"description": "Time logged in issues by group members",
|
||||||
|
@ -3704,6 +3788,20 @@
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"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",
|
"name": "userPermissions",
|
||||||
"description": "Permissions for the current user on the resource",
|
"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 |
|
| `userPermissions` | GroupPermissions! | Permissions for the current user on the resource |
|
||||||
| `webUrl` | String! | Web URL of the group |
|
| `webUrl` | String! | Web URL of the group |
|
||||||
| `avatarUrl` | String | Avatar 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 |
|
| `mentionsDisabled` | Boolean | Indicates if a group is disabled from getting mentioned |
|
||||||
| `parent` | Group | Parent group |
|
| `parent` | Group | Parent group |
|
||||||
| `epicsEnabled` | Boolean | Indicates if Epics are enabled for namespace |
|
| `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`.
|
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 API
|
||||||
module Entities
|
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
|
class BlameRangeCommit < Grape::Entity
|
||||||
expose :id
|
expose :id
|
||||||
expose :parent_ids
|
expose :parent_ids
|
||||||
|
|
|
@ -533,6 +533,32 @@ module API
|
||||||
end
|
end
|
||||||
# rubocop: enable CodeReuse/ActiveRecord
|
# 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
|
params do
|
||||||
requires :user_id, type: Integer, desc: 'The ID of the user'
|
requires :user_id, type: Integer, desc: 'The ID of the user'
|
||||||
end
|
end
|
||||||
|
|
|
@ -54,6 +54,12 @@ module Sentry
|
||||||
end
|
end
|
||||||
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
|
def http_request
|
||||||
response = handle_request_exceptions do
|
response = handle_request_exceptions do
|
||||||
yield
|
yield
|
||||||
|
|
|
@ -3,8 +3,22 @@
|
||||||
module Sentry
|
module Sentry
|
||||||
class Client
|
class Client
|
||||||
module IssueLink
|
module IssueLink
|
||||||
def create_issue_link(integration_id, sentry_issue_identifier, issue)
|
# Creates a link in Sentry corresponding to the provided
|
||||||
issue_link_url = issue_link_api_url(integration_id, sentry_issue_identifier)
|
# 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 = {
|
params = {
|
||||||
project: issue.project.id,
|
project: issue.project.id,
|
||||||
|
@ -14,11 +28,22 @@ module Sentry
|
||||||
http_put(issue_link_url, params)
|
http_put(issue_link_url, params)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
def global_integration_link_api_url(integration_id, sentry_issue_id)
|
||||||
|
|
||||||
def issue_link_api_url(integration_id, sentry_issue_identifier)
|
|
||||||
issue_link_url = URI(url)
|
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
|
issue_link_url
|
||||||
end
|
end
|
||||||
|
|
|
@ -13026,6 +13026,12 @@ msgstr ""
|
||||||
msgid "PackageRegistry|There was a problem fetching the details for this package."
|
msgid "PackageRegistry|There was a problem fetching the details for this package."
|
||||||
msgstr ""
|
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"
|
msgid "PackageRegistry|Unable to load package"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -66,14 +66,23 @@ module QA
|
||||||
metadata[:type] = :feature
|
metadata[:type] = :feature
|
||||||
end
|
end
|
||||||
|
|
||||||
config.before do
|
config.before(:suite) do
|
||||||
unless browser.rspec_configured
|
unless browser.rspec_configured
|
||||||
browser.rspec_configured = true
|
browser.rspec_configured = true
|
||||||
|
|
||||||
##
|
##
|
||||||
# Perform before hooks, which are different for CE and EE
|
# 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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
factory :project_error_tracking_setting, class: 'ErrorTracking::ProjectErrorTrackingSetting' do
|
factory :project_error_tracking_setting, class: 'ErrorTracking::ProjectErrorTrackingSetting' do
|
||||||
project
|
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 }
|
enabled { true }
|
||||||
token { 'access_token_123' }
|
token { 'access_token_123' }
|
||||||
project_name { 'Sentry Project' }
|
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",
|
"firstSeen": "2018-11-06T21:19:55Z",
|
||||||
"hasSeen": false,
|
"hasSeen": false,
|
||||||
"id": "11",
|
"id": "503504",
|
||||||
"isBookmarked": false,
|
"isBookmarked": false,
|
||||||
"isPublic": false,
|
"isPublic": false,
|
||||||
"isSubscribed": true,
|
"isSubscribed": true,
|
||||||
|
@ -72,64 +72,232 @@
|
||||||
"shortId": "PUMP-STATION-1",
|
"shortId": "PUMP-STATION-1",
|
||||||
"stats": {
|
"stats": {
|
||||||
"24h": [
|
"24h": [
|
||||||
[1541451600.0, 557],
|
[
|
||||||
[1541455200.0, 473],
|
1541451600.0,
|
||||||
[1541458800.0, 914],
|
557
|
||||||
[1541462400.0, 991],
|
],
|
||||||
[1541466000.0, 925],
|
[
|
||||||
[1541469600.0, 881],
|
1541455200.0,
|
||||||
[1541473200.0, 182],
|
473
|
||||||
[1541476800.0, 490],
|
],
|
||||||
[1541480400.0, 820],
|
[
|
||||||
[1541484000.0, 322],
|
1541458800.0,
|
||||||
[1541487600.0, 836],
|
914
|
||||||
[1541491200.0, 565],
|
],
|
||||||
[1541494800.0, 758],
|
[
|
||||||
[1541498400.0, 880],
|
1541462400.0,
|
||||||
[1541502000.0, 677],
|
991
|
||||||
[1541505600.0, 381],
|
],
|
||||||
[1541509200.0, 814],
|
[
|
||||||
[1541512800.0, 329],
|
1541466000.0,
|
||||||
[1541516400.0, 446],
|
925
|
||||||
[1541520000.0, 731],
|
],
|
||||||
[1541523600.0, 111],
|
[
|
||||||
[1541527200.0, 926],
|
1541469600.0,
|
||||||
[1541530800.0, 772],
|
881
|
||||||
[1541534400.0, 400],
|
],
|
||||||
[1541538000.0, 943]
|
[
|
||||||
|
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": [
|
"30d": [
|
||||||
[1538870400.0, 565],
|
[
|
||||||
[1538956800.0, 12862],
|
1538870400.0,
|
||||||
[1539043200.0, 15617],
|
565
|
||||||
[1539129600.0, 10809],
|
],
|
||||||
[1539216000.0, 15065],
|
[
|
||||||
[1539302400.0, 12927],
|
1538956800.0,
|
||||||
[1539388800.0, 12994],
|
12862
|
||||||
[1539475200.0, 13139],
|
],
|
||||||
[1539561600.0, 11838],
|
[
|
||||||
[1539648000.0, 12088],
|
1539043200.0,
|
||||||
[1539734400.0, 12338],
|
15617
|
||||||
[1539820800.0, 12768],
|
],
|
||||||
[1539907200.0, 12816],
|
[
|
||||||
[1539993600.0, 15356],
|
1539129600.0,
|
||||||
[1540080000.0, 10910],
|
10809
|
||||||
[1540166400.0, 12306],
|
],
|
||||||
[1540252800.0, 12912],
|
[
|
||||||
[1540339200.0, 14700],
|
1539216000.0,
|
||||||
[1540425600.0, 11890],
|
15065
|
||||||
[1540512000.0, 11684],
|
],
|
||||||
[1540598400.0, 13510],
|
[
|
||||||
[1540684800.0, 12625],
|
1539302400.0,
|
||||||
[1540771200.0, 12811],
|
12927
|
||||||
[1540857600.0, 13180],
|
],
|
||||||
[1540944000.0, 14651],
|
[
|
||||||
[1541030400.0, 14161],
|
1539388800.0,
|
||||||
[1541116800.0, 12612],
|
12994
|
||||||
[1541203200.0, 14316],
|
],
|
||||||
[1541289600.0, 14742],
|
[
|
||||||
[1541376000.0, 12505],
|
1539475200.0,
|
||||||
[1541462400.0, 14180]
|
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",
|
"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 { expect(described_class).to require_graphql_authorizations(:read_group) }
|
||||||
|
|
||||||
it 'has the expected fields' do
|
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)
|
is_expected.to include_graphql_fields(*expected_fields)
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,18 +5,18 @@ require 'spec_helper'
|
||||||
describe Sentry::Client::IssueLink do
|
describe Sentry::Client::IssueLink do
|
||||||
include SentryClientHelpers
|
include SentryClientHelpers
|
||||||
|
|
||||||
let(:error_tracking_setting) { create(:project_error_tracking_setting, api_url: sentry_url) }
|
let_it_be(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' }
|
||||||
let(: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(:client) { error_tracking_setting.sentry_client }
|
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
|
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(: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_response) { issue_link_sample_response }
|
||||||
let!(:sentry_api_request) { stub_sentry_request(sentry_issue_link_url, :put, body: sentry_api_response, status: 201) }
|
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
|
it_behaves_like 'maps Sentry exceptions', :put
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@ describe Sentry::Client::Issue do
|
||||||
let(:token) { 'test-token' }
|
let(:token) { 'test-token' }
|
||||||
let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0' }
|
let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0' }
|
||||||
let(:client) { Sentry::Client.new(sentry_url, token) }
|
let(:client) { Sentry::Client.new(sentry_url, token) }
|
||||||
let(:issue_id) { 11 }
|
let(:issue_id) { 503504 }
|
||||||
|
|
||||||
describe '#list_issues' do
|
describe '#list_issues' do
|
||||||
shared_examples 'issues have correct return type' do |klass|
|
shared_examples 'issues have correct return type' do |klass|
|
||||||
|
@ -243,7 +243,7 @@ describe Sentry::Client::Issue do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'has a correct external URL' do
|
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
|
end
|
||||||
|
|
||||||
it 'issue has a correct external base url' do
|
it 'issue has a correct external base url' do
|
||||||
|
|
|
@ -2100,6 +2100,83 @@ describe API::Users do
|
||||||
end
|
end
|
||||||
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
|
context "user activities", :clean_gitlab_redis_shared_state do
|
||||||
let!(:old_active_user) { create(:user, last_activity_on: Time.utc(2000, 1, 1)) }
|
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) }
|
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
|
||||||
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
|
it 'takes no action' do
|
||||||
expect_next_instance_of(Sentry::Client) do |client|
|
expect_next_instance_of(Sentry::Client) do |client|
|
||||||
expect(client).to receive(:repos).with('sentry-org').and_return([repo])
|
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
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -78,7 +81,7 @@ describe ErrorTrackingIssueLinkWorker do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'terminates after one API request'
|
it_behaves_like 'attempts to create a link via plugin'
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when Sentry the GitLab integration is for another project' do
|
context 'when Sentry the GitLab integration is for another project' do
|
||||||
|
@ -90,7 +93,7 @@ describe ErrorTrackingIssueLinkWorker do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'terminates after one API request'
|
it_behaves_like 'attempts to create a link via plugin'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue