Commit 4376167a authored by Reuben Pereira's avatar Reuben Pereira Committed by Douglas Barbosa Alexandre

Add ProjectMetricsDashboardSetting model and table

This new table will be used to store the external_dashboard_url which
allows users to add a link to their external dashboards (ex Grafana)
to the Metrics dashboard.
parent bb6908cd
...@@ -56,6 +56,8 @@ module Projects ...@@ -56,6 +56,8 @@ module Projects
# overridden in EE # overridden in EE
def permitted_project_params def permitted_project_params
{ {
metrics_setting_attributes: [:external_dashboard_url],
error_tracking_setting_attributes: [ error_tracking_setting_attributes: [
:enabled, :enabled,
:api_host, :api_host,
......
...@@ -188,6 +188,7 @@ class Project < ApplicationRecord ...@@ -188,6 +188,7 @@ class Project < ApplicationRecord
has_one :import_export_upload, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_one :import_export_upload, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_one :project_repository, inverse_of: :project has_one :project_repository, inverse_of: :project
has_one :error_tracking_setting, inverse_of: :project, class_name: 'ErrorTracking::ProjectErrorTrackingSetting' has_one :error_tracking_setting, inverse_of: :project, class_name: 'ErrorTracking::ProjectErrorTrackingSetting'
has_one :metrics_setting, inverse_of: :project, class_name: 'ProjectMetricsSetting'
# Merge Requests for target project should be removed with it # Merge Requests for target project should be removed with it
has_many :merge_requests, foreign_key: 'target_project_id', inverse_of: :target_project has_many :merge_requests, foreign_key: 'target_project_id', inverse_of: :target_project
...@@ -297,6 +298,7 @@ class Project < ApplicationRecord ...@@ -297,6 +298,7 @@ class Project < ApplicationRecord
reject_if: ->(attrs) { attrs[:id].blank? && attrs[:url].blank? } reject_if: ->(attrs) { attrs[:id].blank? && attrs[:url].blank? }
accepts_nested_attributes_for :error_tracking_setting, update_only: true accepts_nested_attributes_for :error_tracking_setting, update_only: true
accepts_nested_attributes_for :metrics_setting, update_only: true, allow_destroy: true
delegate :name, to: :owner, allow_nil: true, prefix: true delegate :name, to: :owner, allow_nil: true, prefix: true
delegate :members, to: :team, prefix: true delegate :members, to: :team, prefix: true
......
# frozen_string_literal: true
class ProjectMetricsSetting < ApplicationRecord
belongs_to :project
validates :external_dashboard_url,
length: { maximum: 255 },
addressable_url: { enforce_sanitization: true, ascii_only: true }
end
...@@ -12,7 +12,16 @@ module Projects ...@@ -12,7 +12,16 @@ module Projects
private private
def project_update_params def project_update_params
error_tracking_params error_tracking_params.merge(metrics_setting_params)
end
def metrics_setting_params
attribs = params[:metrics_setting_attributes]
return {} unless attribs
destroy = attribs[:external_dashboard_url].blank?
{ metrics_setting_attributes: attribs.merge(_destroy: destroy) }
end end
def error_tracking_params def error_tracking_params
......
---
title: Add backend support for a External Dashboard URL setting
merge_request: 27550
author:
type: added
...@@ -14,6 +14,8 @@ en: ...@@ -14,6 +14,8 @@ en:
token: "Auth Token" token: "Auth Token"
project: "Project" project: "Project"
api_url: "Sentry API URL" api_url: "Sentry API URL"
project/metrics_setting:
external_dashboard_url: "External dashboard URL"
errors: errors:
messages: messages:
label_already_exists_at_group_level: "already exists at group level for %{group}. Please choose another one." label_already_exists_at_group_level: "already exists at group level for %{group}. Please choose another one."
......
# frozen_string_literal: true
class CreateProjectMetricsSettings < ActiveRecord::Migration[5.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
create_table :project_metrics_settings, id: :int, primary_key: :project_id, default: nil do |t|
t.string :external_dashboard_url, null: false
t.foreign_key :projects, column: :project_id, on_delete: :cascade
end
end
end
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20190408163745) do ActiveRecord::Schema.define(version: 20190422082247) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -1681,6 +1681,10 @@ ActiveRecord::Schema.define(version: 20190408163745) do ...@@ -1681,6 +1681,10 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_project_import_data_on_project_id", using: :btree t.index ["project_id"], name: "index_project_import_data_on_project_id", using: :btree
end end
create_table "project_metrics_settings", primary_key: "project_id", id: :integer, default: nil, force: :cascade do |t|
t.string "external_dashboard_url", null: false
end
create_table "project_mirror_data", id: :serial, force: :cascade do |t| create_table "project_mirror_data", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false t.integer "project_id", null: false
t.string "status" t.string "status"
...@@ -2529,6 +2533,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do ...@@ -2529,6 +2533,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
add_foreign_key "project_features", "projects", name: "fk_18513d9b92", on_delete: :cascade add_foreign_key "project_features", "projects", name: "fk_18513d9b92", on_delete: :cascade
add_foreign_key "project_group_links", "projects", name: "fk_daa8cee94c", on_delete: :cascade add_foreign_key "project_group_links", "projects", name: "fk_daa8cee94c", on_delete: :cascade
add_foreign_key "project_import_data", "projects", name: "fk_ffb9ee3a10", on_delete: :cascade add_foreign_key "project_import_data", "projects", name: "fk_ffb9ee3a10", on_delete: :cascade
add_foreign_key "project_metrics_settings", "projects", on_delete: :cascade
add_foreign_key "project_mirror_data", "projects", on_delete: :cascade add_foreign_key "project_mirror_data", "projects", on_delete: :cascade
add_foreign_key "project_repositories", "projects", on_delete: :cascade add_foreign_key "project_repositories", "projects", on_delete: :cascade
add_foreign_key "project_repositories", "shards", on_delete: :restrict add_foreign_key "project_repositories", "shards", on_delete: :restrict
......
...@@ -75,6 +75,7 @@ project_tree: ...@@ -75,6 +75,7 @@ project_tree:
- :project_badges - :project_badges
- :ci_cd_settings - :ci_cd_settings
- :error_tracking_setting - :error_tracking_setting
- :metrics_setting
# Only include the following attributes for the models specified. # Only include the following attributes for the models specified.
included_attributes: included_attributes:
......
...@@ -25,7 +25,8 @@ module Gitlab ...@@ -25,7 +25,8 @@ module Gitlab
metrics: 'MergeRequest::Metrics', metrics: 'MergeRequest::Metrics',
ci_cd_settings: 'ProjectCiCdSetting', ci_cd_settings: 'ProjectCiCdSetting',
error_tracking_setting: 'ErrorTracking::ProjectErrorTrackingSetting', error_tracking_setting: 'ErrorTracking::ProjectErrorTrackingSetting',
links: 'Releases::Link' }.freeze links: 'Releases::Link',
metrics_setting: 'ProjectMetricsSetting' }.freeze
USER_REFERENCES = %w[author_id assignee_id updated_by_id merged_by_id latest_closed_by_id user_id created_by_id last_edited_by_id merge_user_id resolved_by_id closed_by_id].freeze USER_REFERENCES = %w[author_id assignee_id updated_by_id merged_by_id latest_closed_by_id user_id created_by_id last_edited_by_id merge_user_id resolved_by_id closed_by_id].freeze
......
...@@ -166,6 +166,20 @@ describe Projects::Settings::OperationsController do ...@@ -166,6 +166,20 @@ describe Projects::Settings::OperationsController do
end end
end end
context 'metrics dashboard setting' do
describe 'PATCH #update' do
let(:params) do
{
metrics_setting_attributes: {
external_dashboard_url: 'https://gitlab.com'
}
}
end
it_behaves_like 'PATCHable'
end
end
private private
def project_params(project, params = {}) def project_params(project, params = {})
......
# frozen_string_literal: true
FactoryBot.define do
factory :project_metrics_setting, class: ProjectMetricsSetting do
project
external_dashboard_url 'https://grafana.com'
end
end
...@@ -322,6 +322,7 @@ project: ...@@ -322,6 +322,7 @@ project:
- pool_repository - pool_repository
- kubernetes_namespaces - kubernetes_namespaces
- error_tracking_setting - error_tracking_setting
- metrics_setting
award_emoji: award_emoji:
- awardable - awardable
- user - user
...@@ -360,3 +361,5 @@ error_tracking_setting: ...@@ -360,3 +361,5 @@ error_tracking_setting:
- project - project
suggestions: suggestions:
- note - note
metrics_setting:
- project
...@@ -606,7 +606,6 @@ ResourceLabelEvent: ...@@ -606,7 +606,6 @@ ResourceLabelEvent:
- user_id - user_id
- created_at - created_at
ErrorTracking::ProjectErrorTrackingSetting: ErrorTracking::ProjectErrorTrackingSetting:
- id
- api_url - api_url
- project_id - project_id
- project_name - project_name
...@@ -626,3 +625,8 @@ MergeRequestAssignee: ...@@ -626,3 +625,8 @@ MergeRequestAssignee:
- id - id
- user_id - user_id
- merge_request_id - merge_request_id
ProjectMetricsSetting:
- project_id
- external_dashboard_url
- created_at
- updated_at
# frozen_string_literal: true
require 'spec_helper'
describe ProjectMetricsSetting do
describe 'Associations' do
it { is_expected.to belong_to(:project) }
end
describe 'Validations' do
context 'when external_dashboard_url is over 255 chars' do
before do
subject.external_dashboard_url = 'https://' + 'a' * 250
end
it 'fails validation' do
expect(subject).not_to be_valid
expect(subject.errors.messages[:external_dashboard_url])
.to include('is too long (maximum is 255 characters)')
end
end
context 'with unsafe url' do
before do
subject.external_dashboard_url = %{https://replaceme.com/'><script>alert(document.cookie)</script>}
end
it { is_expected.to be_invalid }
end
context 'non ascii chars in external_dashboard_url' do
before do
subject.external_dashboard_url = 'http://gitlab.com/api/0/projects/project1/something€'
end
it { is_expected.to be_invalid }
end
context 'internal url in external_dashboard_url' do
before do
subject.external_dashboard_url = 'http://192.168.1.1'
end
it { is_expected.to be_valid }
end
context 'external_dashboard_url is blank' do
before do
subject.external_dashboard_url = ''
end
it { is_expected.to be_invalid }
end
end
end
...@@ -11,6 +11,56 @@ describe Projects::Operations::UpdateService do ...@@ -11,6 +11,56 @@ describe Projects::Operations::UpdateService do
subject { described_class.new(project, user, params) } subject { described_class.new(project, user, params) }
describe '#execute' do describe '#execute' do
context 'metrics dashboard setting' do
let(:params) do
{
metrics_setting_attributes: {
external_dashboard_url: 'http://gitlab.com'
}
}
end
context 'without existing metrics dashboard setting' do
it 'creates a setting' do
expect(result[:status]).to eq(:success)
expect(project.reload.metrics_setting.external_dashboard_url).to eq(
'http://gitlab.com'
)
end
end
context 'with existing metrics dashboard setting' do
before do
create(:project_metrics_setting, project: project)
end
it 'updates the settings' do
expect(result[:status]).to eq(:success)
expect(project.reload.metrics_setting.external_dashboard_url).to eq(
'http://gitlab.com'
)
end
context 'with blank external_dashboard_url in params' do
let(:params) do
{
metrics_setting_attributes: {
external_dashboard_url: ''
}
}
end
it 'destroys the metrics_setting entry in DB' do
expect(result[:status]).to eq(:success)
expect(project.reload.metrics_setting).to be_nil
end
end
end
end
context 'error tracking' do context 'error tracking' do
context 'with existing error tracking setting' do context 'with existing error tracking setting' do
let(:params) do let(:params) do
......
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