Commit 74a89b12 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent b0abae12
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
## 12.6.4
- No changes.
## 12.6.2 ## 12.6.2
### Security (2 changes) ### Security (2 changes)
...@@ -228,6 +232,10 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -228,6 +232,10 @@ Please view this file on the master branch, on stable branches it's out of date.
- Remove IIFEs from jira_connect.js file. !19248 (nuwe1) - Remove IIFEs from jira_connect.js file. !19248 (nuwe1)
## 12.4.8
- No changes.
## 12.4.5 ## 12.4.5
- No changes. - No changes.
......
...@@ -124,9 +124,7 @@ export default { ...@@ -124,9 +124,7 @@ export default {
}, },
}, },
mounted() { mounted() {
if (!this.allValuesEmpty) { this.draw();
this.draw();
}
}, },
methods: { methods: {
draw() { draw() {
...@@ -153,7 +151,14 @@ export default { ...@@ -153,7 +151,14 @@ export default {
this.yScale = d3.scaleLinear().rangeRound([this.vbHeight, 0]); this.yScale = d3.scaleLinear().rangeRound([this.vbHeight, 0]);
this.xScale.domain(this.graphData.map(d => d.name)); this.xScale.domain(this.graphData.map(d => d.name));
this.yScale.domain([0, d3.max(this.graphData.map(d => d.value))]); /*
If we have all-zero graph we want graph to center 0 on axis and not to draw
any kind of ticks on Y axis. Infinity allows us to do that.
See https://gitlab.com/gitlab-org/gitlab/merge_requests/20627#note_251484582
for detailed explanation
*/
this.yScale.domain([0, d3.max(this.graphData.map(d => d.value)) || Infinity]);
// Zoom/Panning Function // Zoom/Panning Function
this.zoom = d3 this.zoom = d3
......
...@@ -39,6 +39,7 @@ class MergeRequestsFinder < IssuableFinder ...@@ -39,6 +39,7 @@ class MergeRequestsFinder < IssuableFinder
def filter_items(_items) def filter_items(_items)
items = by_commit(super) items = by_commit(super)
items = by_deployment(items)
items = by_source_branch(items) items = by_source_branch(items)
items = by_wip(items) items = by_wip(items)
items = by_target_branch(items) items = by_target_branch(items)
...@@ -101,6 +102,17 @@ class MergeRequestsFinder < IssuableFinder ...@@ -101,6 +102,17 @@ class MergeRequestsFinder < IssuableFinder
.or(table[:title].matches('WIP %')) .or(table[:title].matches('WIP %'))
.or(table[:title].matches('[WIP]%')) .or(table[:title].matches('[WIP]%'))
end end
def by_deployment(items)
return items unless deployment_id
items.includes(:deployment_merge_requests)
.where(deployment_merge_requests: { deployment_id: deployment_id })
end
def deployment_id
@deployment_id ||= params[:deployment_id].presence
end
end end
MergeRequestsFinder.prepend_if_ee('EE::MergeRequestsFinder') MergeRequestsFinder.prepend_if_ee('EE::MergeRequestsFinder')
...@@ -11,6 +11,7 @@ class ApplicationSetting < ApplicationRecord ...@@ -11,6 +11,7 @@ class ApplicationSetting < ApplicationRecord
add_authentication_token_field :static_objects_external_storage_auth_token add_authentication_token_field :static_objects_external_storage_auth_token
belongs_to :instance_administration_project, class_name: "Project" belongs_to :instance_administration_project, class_name: "Project"
belongs_to :instance_administrators_group, class_name: "Group"
# Include here so it can override methods from # Include here so it can override methods from
# `add_authentication_token_field` # `add_authentication_token_field`
......
---
title: Add API support for retrieving merge requests deployed in a deployment
merge_request: 21837
author:
type: added
---
title: Save Instance Administrators group ID in DB
merge_request: 22600
author:
type: changed
---
title: Add structured logging for application logs
merge_request: 22379
author:
type: other
---
title: Add API for getting sentry error tracking settings of a project
merge_request: 21788
author: raju249
type: added
---
title: Update d3 to 5.12
merge_request: 20627
author: Praveen Arimbrathodiyil
type: other
---
title: Fix private objects exposure when using Project Import functionality
merge_request:
author:
type: security
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddColumnForInstanceAdministratorsGroup < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :application_settings, :instance_administrators_group_id, :integer
end
end
# frozen_string_literal: true
class AddFkForInstanceAdministratorsGroup < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_foreign_key(
:application_settings,
:namespaces,
column: :instance_administrators_group_id,
on_delete: :nullify
)
end
def down
remove_foreign_key :application_settings, column: :instance_administrators_group_id
end
end
# frozen_string_literal: true
class AddIndexForInstanceAdministratorsGroup < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :application_settings, :instance_administrators_group_id
end
def down
remove_concurrent_index :application_settings, :instance_administrators_group_id
end
end
...@@ -365,9 +365,11 @@ ActiveRecord::Schema.define(version: 2020_01_08_233040) do ...@@ -365,9 +365,11 @@ ActiveRecord::Schema.define(version: 2020_01_08_233040) do
t.text "encrypted_slack_app_verification_token" t.text "encrypted_slack_app_verification_token"
t.string "encrypted_slack_app_verification_token_iv", limit: 255 t.string "encrypted_slack_app_verification_token_iv", limit: 255
t.boolean "updating_name_disabled_for_users", default: false, null: false t.boolean "updating_name_disabled_for_users", default: false, null: false
t.integer "instance_administrators_group_id"
t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id" t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id"
t.index ["file_template_project_id"], name: "index_application_settings_on_file_template_project_id" t.index ["file_template_project_id"], name: "index_application_settings_on_file_template_project_id"
t.index ["instance_administration_project_id"], name: "index_applicationsettings_on_instance_administration_project_id" t.index ["instance_administration_project_id"], name: "index_applicationsettings_on_instance_administration_project_id"
t.index ["instance_administrators_group_id"], name: "index_application_settings_on_instance_administrators_group_id"
t.index ["usage_stats_set_by_user_id"], name: "index_application_settings_on_usage_stats_set_by_user_id" t.index ["usage_stats_set_by_user_id"], name: "index_application_settings_on_usage_stats_set_by_user_id"
end end
...@@ -4414,6 +4416,7 @@ ActiveRecord::Schema.define(version: 2020_01_08_233040) do ...@@ -4414,6 +4416,7 @@ ActiveRecord::Schema.define(version: 2020_01_08_233040) do
add_foreign_key "analytics_repository_file_edits", "projects", on_delete: :cascade add_foreign_key "analytics_repository_file_edits", "projects", on_delete: :cascade
add_foreign_key "analytics_repository_files", "projects", on_delete: :cascade add_foreign_key "analytics_repository_files", "projects", on_delete: :cascade
add_foreign_key "application_settings", "namespaces", column: "custom_project_templates_group_id", on_delete: :nullify add_foreign_key "application_settings", "namespaces", column: "custom_project_templates_group_id", on_delete: :nullify
add_foreign_key "application_settings", "namespaces", column: "instance_administrators_group_id", name: "fk_e8a145f3a7", on_delete: :nullify
add_foreign_key "application_settings", "projects", column: "file_template_project_id", name: "fk_ec757bd087", on_delete: :nullify add_foreign_key "application_settings", "projects", column: "file_template_project_id", name: "fk_ec757bd087", on_delete: :nullify
add_foreign_key "application_settings", "projects", column: "instance_administration_project_id", on_delete: :nullify add_foreign_key "application_settings", "projects", column: "instance_administration_project_id", on_delete: :nullify
add_foreign_key "application_settings", "users", column: "usage_stats_set_by_user_id", name: "fk_964370041d", on_delete: :nullify add_foreign_key "application_settings", "users", column: "usage_stats_set_by_user_id", name: "fk_964370041d", on_delete: :nullify
......
...@@ -29,6 +29,7 @@ The following API resources are available in the project context: ...@@ -29,6 +29,7 @@ The following API resources are available in the project context:
| [Deployments](deployments.md) | `/projects/:id/deployments` | | [Deployments](deployments.md) | `/projects/:id/deployments` |
| [Discussions](discussions.md) (threaded comments) | `/projects/:id/issues/.../discussions`, `/projects/:id/snippets/.../discussions`, `/projects/:id/merge_requests/.../discussions`, `/projects/:id/commits/.../discussions` (also available for groups) | | [Discussions](discussions.md) (threaded comments) | `/projects/:id/issues/.../discussions`, `/projects/:id/snippets/.../discussions`, `/projects/:id/merge_requests/.../discussions`, `/projects/:id/commits/.../discussions` (also available for groups) |
| [Environments](environments.md) | `/projects/:id/environments` | | [Environments](environments.md) | `/projects/:id/environments` |
| [Error Tracking](error_tracking.md) | `/projects/:id/error_tracking/settings` |
| [Events](events.md) | `/projects/:id/events` (also available for users and standalone) | | [Events](events.md) | `/projects/:id/events` (also available for users and standalone) |
| [Issues](issues.md) | `/projects/:id/issues` (also available for groups and standalone) | | [Issues](issues.md) | `/projects/:id/issues` (also available for groups and standalone) |
| [Issues Statistics](issues_statistics.md) | `/projects/:id/issues_statistics` (also available for groups and standalone) | | [Issues Statistics](issues_statistics.md) | `/projects/:id/issues_statistics` (also available for groups and standalone) |
......
...@@ -349,3 +349,19 @@ Example of a response: ...@@ -349,3 +349,19 @@ Example of a response:
"deployable": null "deployable": null
} }
``` ```
## List of merge requests associated with a deployment
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/35739) in GitLab 12.7.
This API retrieves the list of merge requests shipped with a given deployment:
```
GET /projects/:id/deployments/:deployment_id/merge_requests
```
It supports the same parameters as the [Merge Requests API](./merge_requests.md#list-merge-requests) and will return a response using the same format:
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/deployments/42"
```
# Error Tracking settings API
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/34940) in GitLab 12.7.
## Error Tracking project settings
The project settings API allows you to retrieve the Error Tracking settings for a project. Only for project maintainers.
### Get Error Tracking settings
```
GET /projects/:id/error_tracking/settings
```
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
| `id` | integer | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/1/error_tracking/settings
```
Example response:
```json
{
"project_name": "sample sentry project",
"sentry_external_url": "https://sentry.io/myawesomeproject/project",
"api_url": "https://sentry.io/api/0/projects/myawesomeproject/project"
}
```
...@@ -550,7 +550,7 @@ Parameters: ...@@ -550,7 +550,7 @@ Parameters:
}, },
"user" : { "user" : {
"can_merge" : false "can_merge" : false
} },
"assignee": { "assignee": {
"id": 1, "id": 1,
"name": "Administrator", "name": "Administrator",
......
...@@ -127,6 +127,88 @@ importer progresses. Here's what to do: ...@@ -127,6 +127,88 @@ importer progresses. Here's what to do:
logger.info(message: "Import error", error_code: 1, error: "I/O failure") logger.info(message: "Import error", error_code: 1, error: "I/O failure")
``` ```
## Multi-destination Logging
GitLab is transitioning from unstructured/plaintext logs to structured/JSON logs. During this transition period some logs will be recorded in multiple formats through multi-destination logging.
### How to use multi-destination logging
Create a new logger class, inheriting from `MultiDestinationLogger` and add an array of loggers to a `LOGGERS` constant. The loggers should be classes that descend from `Gitlab::Logger`. e.g. the user defined loggers in the following examples, could be inheriting from `Gitlab::Logger` and `Gitlab::JsonLogger`, respectively.
You must specify one of the loggers as the `primary_logger`. The `primary_logger` will be used when information about this multi-destination logger is displayed in the app, e.g. using the `Gitlab::Logger.read_latest` method.
The following example sets one of the defined `LOGGERS` as a `primary_logger`.
```ruby
module Gitlab
class FancyMultiLogger < Gitlab::MultiDestinationLogger
LOGGERS = [UnstructuredLogger, StructuredLogger].freeze
def self.loggers
LOGGERS
end
def primary_logger
UnstructuredLogger
end
end
end
```
You can now call the usual logging methods on this multi-logger, e.g.
```ruby
FancyMultiLogger.info(message: "Information")
```
This message will be logged by each logger registered in `FancyMultiLogger.loggers`.
### Passing a string or hash for logging
When passing a string or hash to a `MultiDestinationLogger`, the log lines could be formatted differently, depending on the kinds of `LOGGERS` set.
e.g. let's partially define the loggers from the previous example:
```ruby
module Gitlab
# Similar to AppTextLogger
class UnstructuredLogger < Gitlab::Logger
...
end
# Similar to AppJsonLogger
class StructuredLogger < Gitlab::JsonLogger
...
end
end
```
Here are some examples of how messages would be handled by both the loggers.
1. When passing a string
```ruby
FancyMultiLogger.info("Information")
# UnstructuredLogger
I, [2020-01-13T12:02:41.566219 #6652] INFO -- : Information
# StructuredLogger
{:severity=>"INFO", :time=>"2020-01-13T11:02:41.559Z", :correlation_id=>"b1701f7ecc4be4bcd4c2d123b214e65a", :message=>"Information"}
```
1. When passing a hash
```ruby
FancyMultiLogger.info({:message=>"This is my message", :project_id=>123})
# UnstructuredLogger
I, [2020-01-13T12:06:09.856766 #8049] INFO -- : {:message=>"This is my message", :project_id=>123}
# StructuredLogger
{:severity=>"INFO", :time=>"2020-01-13T11:06:09.851Z", :correlation_id=>"d7e0886f096db9a8526a4f89da0e45f6", :message=>"This is my message", :project_id=>123}
```
## Exception Handling ## Exception Handling
It often happens that you catch the exception and want to track it. It often happens that you catch the exception and want to track it.
......
...@@ -34,8 +34,8 @@ a pipeline in the [`gitlab-qa`](https://gitlab.com/gitlab-org/gitlab-qa/) projec ...@@ -34,8 +34,8 @@ a pipeline in the [`gitlab-qa`](https://gitlab.com/gitlab-org/gitlab-qa/) projec
by triggering the `package-and-qa` manual action in the `test` stage (not by triggering the `package-and-qa` manual action in the `test` stage (not
available for forks). available for forks).
**This runs end-to-end tests against a custom Omnibus package built from your **This runs end-to-end tests against a custom CE and EE (with an Ultimate license)
merge request's changes.** Omnibus package built from your merge request's changes.**
Manual action that starts end-to-end tests is also available in merge requests Manual action that starts end-to-end tests is also available in merge requests
in [Omnibus GitLab][omnibus-gitlab]. in [Omnibus GitLab][omnibus-gitlab].
......
...@@ -155,7 +155,7 @@ Identity Provider. ...@@ -155,7 +155,7 @@ Identity Provider.
### Requirements ### Requirements
First you need to tell GitLab where to look for group information. For this you First you need to tell GitLab where to look for group information. For this you
need to make sure that your IdP server sends a specific `AttributeStament` along need to make sure that your IdP server sends a specific `AttributeStatement` along
with the regular SAML response. Here is an example: with the regular SAML response. Here is an example:
```xml ```xml
......
...@@ -117,6 +117,7 @@ module API ...@@ -117,6 +117,7 @@ module API
mount ::API::DeployKeys mount ::API::DeployKeys
mount ::API::Deployments mount ::API::Deployments
mount ::API::Environments mount ::API::Environments
mount ::API::ErrorTracking
mount ::API::Events mount ::API::Events
mount ::API::Features mount ::API::Features
mount ::API::Files mount ::API::Files
......
...@@ -127,6 +127,26 @@ module API ...@@ -127,6 +127,26 @@ module API
render_validation_error!(deployment) render_validation_error!(deployment)
end end
end end
helpers Helpers::MergeRequestsHelpers
desc 'Get all merge requests of a deployment' do
detail 'This feature was introduced in GitLab 12.7.'
success Entities::MergeRequestBasic
end
params do
requires :deployment_id, type: Integer, desc: 'The deployment ID'
use :merge_requests_base_params
end
get ':id/deployments/:deployment_id/merge_requests' do
authorize! :read_deployment, user_project
mr_params = declared_params.merge(deployment_id: params[:deployment_id])
merge_requests = MergeRequestsFinder.new(current_user, mr_params).execute
present merge_requests, { with: Entities::MergeRequestBasic, current_user: current_user }
end
end end
end end
end end
# frozen_string_literal: true
module API
module Entities
module ErrorTracking
class ProjectSetting < Grape::Entity
expose :project_name
expose :sentry_external_url
expose :api_url
end
end
end
end
# frozen_string_literal: true
module API
class ErrorTracking < Grape::API
before { authenticate! }
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Get error tracking settings for the project' do
detail 'This feature was introduced in GitLab 12.7.'
success Entities::ErrorTracking::ProjectSetting
end
get ':id/error_tracking/settings' do
authorize! :admin_operations, user_project
setting = user_project.error_tracking_setting
not_found!('Error Tracking Setting') unless setting
present setting, with: Entities::ErrorTracking::ProjectSetting
end
end
end
end
# frozen_string_literal: true
module API
module Helpers
module MergeRequestsHelpers
extend Grape::API::Helpers
include ::API::Helpers::CustomValidators
params :merge_requests_base_params do
optional :state,
type: String,
values: %w[opened closed locked merged all],
default: 'all',
desc: 'Return opened, closed, locked, merged, or all merge requests'
optional :order_by,
type: String,
values: %w[created_at updated_at],
default: 'created_at',
desc: 'Return merge requests ordered by `created_at` or `updated_at` fields.'
optional :sort,
type: String,
values: %w[asc desc],
default: 'desc',
desc: 'Return merge requests sorted in `asc` or `desc` order.'
optional :milestone, type: String, desc: 'Return merge requests for a specific milestone'
optional :labels,
type: Array[String],
coerce_with: Validations::Types::LabelsList.coerce,
desc: 'Comma-separated list of label names'
optional :with_labels_details, type: Boolean, desc: 'Return titles of labels and other details', default: false
optional :created_after, type: DateTime, desc: 'Return merge requests created after the specified time'
optional :created_before, type: DateTime, desc: 'Return merge requests created before the specified time'
optional :updated_after, type: DateTime, desc: 'Return merge requests updated after the specified time'
optional :updated_before, type: DateTime, desc: 'Return merge requests updated before the specified time'
optional :view,
type: String,
values: %w[simple],
desc: 'If simple, returns the `iid`, URL, title, description, and basic state of merge request'
optional :author_id, type: Integer, desc: 'Return merge requests which are authored by the user with the given ID'
optional :assignee_id,
types: [Integer, String],
integer_none_any: true,
desc: 'Return merge requests which are assigned to the user with the given ID'
optional :scope,
type: String,
values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all],
desc: 'Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`'
optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji'
optional :source_branch, type: String, desc: 'Return merge requests with the given source branch'
optional :source_project_id, type: Integer, desc: 'Return merge requests with the given source project id'
optional :target_branch, type: String, desc: 'Return merge requests with the given target branch'
optional :search,
type: String,
desc: 'Search merge requests for text present in the title, description, or any combination of these'
optional :in, type: String, desc: '`title`, `description`, or a string joining them with comma'
optional :wip, type: String, values: %w[yes no], desc: 'Search merge requests for WIP in the title'
end
params :optional_scope_param do
optional :scope,
type: String,
values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all],
default: 'created_by_me',
desc: 'Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`'
end
end
end
end
...@@ -7,6 +7,7 @@ module API ...@@ -7,6 +7,7 @@ module API
before { authenticate_non_get! } before { authenticate_non_get! }
helpers ::Gitlab::IssuableMetadata helpers ::Gitlab::IssuableMetadata
helpers Helpers::MergeRequestsHelpers
# EE::API::MergeRequests would override the following helpers # EE::API::MergeRequests would override the following helpers
helpers do helpers do
...@@ -107,33 +108,7 @@ module API ...@@ -107,33 +108,7 @@ module API
end end
params :merge_requests_params do params :merge_requests_params do
optional :state, type: String, values: %w[opened closed locked merged all], default: 'all', use :merge_requests_base_params
desc: 'Return opened, closed, locked, merged, or all merge requests'
optional :order_by, type: String, values: %w[created_at updated_at], default: 'created_at',
desc: 'Return merge requests ordered by `created_at` or `updated_at` fields.'
optional :sort, type: String, values: %w[asc desc], default: 'desc',
desc: 'Return merge requests sorted in `asc` or `desc` order.'
optional :milestone, type: String, desc: 'Return merge requests for a specific milestone'
optional :labels, type: Array[String], coerce_with: Validations::Types::LabelsList.coerce, desc: 'Comma-separated list of label names'
optional :with_labels_details, type: Boolean, desc: 'Return titles of labels and other details', default: false
optional :created_after, type: DateTime, desc: 'Return merge requests created after the specified time'
optional :created_before, type: DateTime, desc: 'Return merge requests created before the specified time'
optional :updated_after, type: DateTime, desc: 'Return merge requests updated after the specified time'
optional :updated_before, type: DateTime, desc: 'Return merge requests updated before the specified time'
optional :view, type: String, values: %w[simple], desc: 'If simple, returns the `iid`, URL, title, description, and basic state of merge request'
optional :author_id, type: Integer, desc: 'Return merge requests which are authored by the user with the given ID'
optional :assignee_id, types: [Integer, String], integer_none_any: true,
desc: 'Return merge requests which are assigned to the user with the given ID'
optional :scope, type: String, values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all],
desc: 'Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`'
optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji'
optional :source_branch, type: String, desc: 'Return merge requests with the given source branch'
optional :source_project_id, type: Integer, desc: 'Return merge requests with the given source project id'
optional :target_branch, type: String, desc: 'Return merge requests with the given target branch'
optional :search, type: String, desc: 'Search merge requests for text present in the title, description, or any combination of these'
optional :in, type: String, desc: '`title`, `description`, or a string joining them with comma'
optional :wip, type: String, values: %w[yes no], desc: 'Search merge requests for WIP in the title'
use :optional_merge_requests_search_params use :optional_merge_requests_search_params
use :pagination use :pagination
end end
...@@ -145,8 +120,7 @@ module API ...@@ -145,8 +120,7 @@ module API
end end
params do params do
use :merge_requests_params use :merge_requests_params
optional :scope, type: String, values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all], default: 'created_by_me', use :optional_scope_param
desc: 'Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`'
end end
get do get do
authenticate! unless params[:scope] == 'all' authenticate! unless params[:scope] == 'all'
......
# frozen_string_literal: true
module Gitlab
class AppJsonLogger < Gitlab::JsonLogger
def self.file_name_noext
'application_json'
end
end
end
# frozen_string_literal: true # frozen_string_literal: true
module Gitlab module Gitlab
class AppLogger < Gitlab::Logger class AppLogger < Gitlab::MultiDestinationLogger
def self.file_name_noext LOGGERS = [Gitlab::AppTextLogger, Gitlab::AppJsonLogger].freeze
'application'
def self.loggers
LOGGERS
end end
def format_message(severity, timestamp, progname, msg) def self.primary_logger
"#{timestamp.to_s(:long)}: #{msg}\n" Gitlab::AppTextLogger
end end
end end
end end
# frozen_string_literal: true
module Gitlab
class AppTextLogger < Gitlab::Logger
def self.file_name_noext
'application'
end
def format_message(severity, timestamp, progname, msg)
"#{timestamp.to_s(:long)}: #{msg}\n"
end
end
end
# frozen_string_literal: true
module Gitlab
class MultiDestinationLogger < ::Logger
def close
loggers.each(&:close)
end
def self.debug(message)
loggers.each { |logger| logger.build.debug(message) }
end
def self.error(message)
loggers.each { |logger| logger.build.error(message) }
end
def self.warn(message)
loggers.each { |logger| logger.build.warn(message) }
end
def self.info(message)
loggers.each { |logger| logger.build.info(message) }
end
def self.read_latest
primary_logger.read_latest
end
def self.file_name
primary_logger.file_name
end
def self.full_log_path
primary_logger.full_log_path
end
def self.file_name_noext
primary_logger.file_name_noext
end
def self.loggers
raise NotImplementedError
end
def self.primary_logger
raise NotImplementedError
end
end
end
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
"core-js": "^3.2.1", "core-js": "^3.2.1",
"cropper": "^2.3.0", "cropper": "^2.3.0",
"css-loader": "^1.0.0", "css-loader": "^1.0.0",
"d3": "^4.13.0", "d3": "^5.12.0",
"d3-scale": "^1.0.7", "d3-scale": "^1.0.7",
"d3-selection": "^1.2.0", "d3-selection": "^1.2.0",
"dateformat": "^3.0.3", "dateformat": "^3.0.3",
......
...@@ -22,8 +22,15 @@ module QA ...@@ -22,8 +22,15 @@ module QA
expect(page).to have_content(/@#{user.username}(\n| )?Given access/) expect(page).to have_content(/@#{user.username}(\n| )?Given access/)
# Wait for Action Mailer to deliver messages mailhog_items = mailhog_json.dig('items')
mailhog_json = Support::Retrier.retry_until(sleep_interval: 1) do
expect(mailhog_items).to include(an_object_satisfying { |o| /project was granted/ === o.dig('Content', 'Headers', 'Subject', 0) })
end
private
def mailhog_json
Support::Retrier.retry_until(sleep_interval: 1) do
Runtime::Logger.debug(%Q[retrieving "#{QA::Runtime::MailHog.api_messages_url}"]) Runtime::Logger.debug(%Q[retrieving "#{QA::Runtime::MailHog.api_messages_url}"])
mailhog_response = get QA::Runtime::MailHog.api_messages_url mailhog_response = get QA::Runtime::MailHog.api_messages_url
...@@ -33,10 +40,6 @@ module QA ...@@ -33,10 +40,6 @@ module QA
# Expect at least two invitation messages: group and project # Expect at least two invitation messages: group and project
mailhog_data if mailhog_data.dig('total') >= 2 mailhog_data if mailhog_data.dig('total') >= 2
end end
# Check json result from mailhog
mailhog_items = mailhog_json.dig('items')
expect(mailhog_items).to include(an_object_satisfying { |o| /project was granted/ === o.dig('Content', 'Headers', 'Subject', 0) })
end end
end end
end end
......
...@@ -28,11 +28,9 @@ module QA ...@@ -28,11 +28,9 @@ module QA
end end
before do before do
issue = Resource::Issue.fabricate_via_api! do |issue| Resource::Issue.fabricate_via_api! do |issue|
issue.title = issue_title issue.title = issue_title
end end.visit!
issue.visit!
end end
it 'user comments on an issue with an attachment' do it 'user comments on an issue with an attachment' do
......
...@@ -8,11 +8,9 @@ module QA ...@@ -8,11 +8,9 @@ module QA
before do before do
Flow::Login.sign_in Flow::Login.sign_in
issue = Resource::Issue.fabricate_via_api! do |issue| Resource::Issue.fabricate_via_api! do |issue|
issue.title = issue_title issue.title = issue_title
end end.visit!
issue.visit!
end end
it 'user filters comments and activities in an issue' do it 'user filters comments and activities in an issue' do
......
...@@ -166,6 +166,38 @@ describe MergeRequestsFinder do ...@@ -166,6 +166,38 @@ describe MergeRequestsFinder do
expect(scalar_params).to include(:wip, :assignee_id) expect(scalar_params).to include(:wip, :assignee_id)
end end
context 'filter by deployment' do
let_it_be(:project_with_repo) { create(:project, :repository) }
it 'returns the relevant merge requests' do
deployment1 = create(
:deployment,
project: project_with_repo,
sha: project_with_repo.commit.id,
merge_requests: [merge_request1, merge_request2]
)
create(
:deployment,
project: project_with_repo,
sha: project_with_repo.commit.id,
merge_requests: [merge_request3]
)
params = { deployment_id: deployment1.id }
merge_requests = described_class.new(user, params).execute
expect(merge_requests).to contain_exactly(merge_request1, merge_request2)
end
context 'when a deployment does not contain any merge requests' do
it 'returns an empty result' do
params = { deployment_id: create(:deployment, project: project_with_repo, sha: project_with_repo.commit.sha).id }
merge_requests = described_class.new(user, params).execute
expect(merge_requests).to be_empty
end
end
end
end end
context 'assignee filtering' do context 'assignee filtering' do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::AppJsonLogger do
subject { described_class.new('/dev/null') }
let(:hash_message) { { 'message' => 'Message', 'project_id' => '123' } }
let(:string_message) { 'Information' }
it 'logs a hash as a JSON' do
expect(JSON.parse(subject.format_message('INFO', Time.now, nil, hash_message))).to include(hash_message)
end
it 'logs a string as a JSON' do
expect(JSON.parse(subject.format_message('INFO', Time.now, nil, string_message))).to include('message' => string_message)
end
end
...@@ -2,13 +2,21 @@ ...@@ -2,13 +2,21 @@
require 'spec_helper' require 'spec_helper'
describe Gitlab::AppLogger, :request_store do describe Gitlab::AppLogger do
subject { described_class } subject { described_class }
it 'builds a logger once' do it 'builds a Gitlab::Logger object twice' do
expect(::Logger).to receive(:new).and_call_original expect(Gitlab::Logger).to receive(:new)
.exactly(described_class.loggers.size)
.and_call_original
subject.info('hello world') subject.info('Hello World!')
subject.error('hello again') end
it 'logs info to AppLogger and AppJsonLogger' do
expect_any_instance_of(Gitlab::AppTextLogger).to receive(:info).and_call_original
expect_any_instance_of(Gitlab::AppJsonLogger).to receive(:info).and_call_original
subject.info('Hello World!')
end end
end end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::AppTextLogger do
subject { described_class.new('/dev/null') }
let(:hash_message) { { message: 'Message', project_id: 123 } }
let(:string_message) { 'Information' }
it 'logs a hash as string' do
expect(subject.format_message('INFO', Time.now, nil, hash_message )).to include(hash_message.to_s)
end
it 'logs a string unchanged' do
expect(subject.format_message('INFO', Time.now, nil, string_message)).to include(string_message)
end
end
# frozen_string_literal: true
require 'spec_helper'
class FakeLogger
end
class LoggerA < Gitlab::Logger
def self.file_name_noext
'loggerA'
end
end
class LoggerB < Gitlab::JsonLogger
def self.file_name_noext
'loggerB'
end
end
class TestLogger < Gitlab::MultiDestinationLogger
LOGGERS = [LoggerA, LoggerB].freeze
def self.loggers
LOGGERS
end
end
class EmptyLogger < Gitlab::MultiDestinationLogger
def self.loggers
[]
end
end
describe Gitlab::MultiDestinationLogger do
after(:all) do
TestLogger.loggers.each do |logger|
log_file_path = "#{Rails.root}/log/#{logger.file_name}"
File.delete(log_file_path)
end
end
context 'with no primary logger set' do
subject { EmptyLogger }
it 'primary_logger raises an error' do
expect { subject.primary_logger }.to raise_error(NotImplementedError)
end
end
context 'with 2 loggers set' do
subject { TestLogger }
it 'logs info to 2 loggers' do
expect(subject.loggers).to all(receive(:build).and_call_original)
subject.info('Hello World')
end
end
end
...@@ -343,6 +343,48 @@ describe API::Deployments do ...@@ -343,6 +343,48 @@ describe API::Deployments do
end end
end end
describe 'GET /projects/:id/deployments/:deployment_id/merge_requests' do
let(:project) { create(:project, :repository) }
let!(:deployment) { create(:deployment, :success, project: project) }
subject { get api("/projects/#{project.id}/deployments/#{deployment.id}/merge_requests", user) }
context 'when a user is not a member of the deployment project' do
let(:user) { build(:user) }
it 'returns a 404 status code' do
subject
expect(response).to have_gitlab_http_status(404)
end
end
context 'when a user member of the deployment project' do
let_it_be(:project2) { create(:project) }
let!(:merge_request1) { create(:merge_request, source_project: project, target_project: project) }
let!(:merge_request2) { create(:merge_request, source_project: project, target_project: project, state: 'closed') }
let!(:merge_request3) { create(:merge_request, source_project: project2, target_project: project2) }
it 'returns the relevant merge requests linked to a deployment for a project' do
deployment.merge_requests << [merge_request1, merge_request2]
subject
expect(response).to have_gitlab_http_status(200)
expect(json_response.map { |d| d['id'] }).to contain_exactly(merge_request1.id, merge_request2.id)
end
context 'when a deployment is not associated to any existing merge requests' do
it 'returns an empty array' do
subject
expect(response).to have_gitlab_http_status(200)
expect(json_response).to eq([])
end
end
end
end
context 'prevent N + 1 queries' do context 'prevent N + 1 queries' do
context 'when the endpoint returns multiple records' do context 'when the endpoint returns multiple records' do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
......
# frozen_string_literal: true
require 'spec_helper'
describe API::ErrorTracking do
describe "GET /projects/:id/error_tracking/settings" do
let(:user) { create(:user) }
let(:setting) { create(:project_error_tracking_setting) }
let(:project) { setting.project }
def make_request
get api("/projects/#{project.id}/error_tracking/settings", user)
end
context 'when authenticated as maintainer' do
before do
project.add_maintainer(user)
end
it 'returns project settings' do
make_request
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq(
'project_name' => setting.project_name,
'sentry_external_url' => setting.sentry_external_url,
'api_url' => setting.api_url
)
end
end
context 'without a project setting' do
let(:project) { create(:project) }
before do
project.add_maintainer(user)
end
it 'returns 404' do
make_request
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message'])
.to eq('404 Error Tracking Setting Not Found')
end
end
context 'when authenticated as reporter' do
before do
project.add_reporter(user)
end
it 'returns 403' do
make_request
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when authenticated as non-member' do
it 'returns 404' do
make_request
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when unauthenticated' do
let(:user) { nil }
it 'returns 401' do
make_request
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
end
end
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment