Commit 060c8424 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 6867eff1
...@@ -457,9 +457,9 @@ end ...@@ -457,9 +457,9 @@ end
# Gitaly GRPC protocol definitions # Gitaly GRPC protocol definitions
gem 'gitaly', '~> 12.9.0.pre.rc4' gem 'gitaly', '~> 12.9.0.pre.rc4'
gem 'grpc', '~> 1.24.0' gem 'grpc', '~> 1.27.0'
gem 'google-protobuf', '~> 3.8.0' gem 'google-protobuf', '~> 3.11.2'
gem 'toml-rb', '~> 1.0.0' gem 'toml-rb', '~> 1.0.0'
......
...@@ -427,7 +427,7 @@ GEM ...@@ -427,7 +427,7 @@ GEM
mime-types (~> 3.0) mime-types (~> 3.0)
representable (~> 3.0) representable (~> 3.0)
retriable (>= 2.0, < 4.0) retriable (>= 2.0, < 4.0)
google-protobuf (3.8.0) google-protobuf (3.11.4)
googleapis-common-protos-types (1.0.4) googleapis-common-protos-types (1.0.4)
google-protobuf (~> 3.0) google-protobuf (~> 3.0)
googleauth (0.6.6) googleauth (0.6.6)
...@@ -468,8 +468,8 @@ GEM ...@@ -468,8 +468,8 @@ GEM
graphql (~> 1.6) graphql (~> 1.6)
html-pipeline (~> 2.8) html-pipeline (~> 2.8)
sass (~> 3.4) sass (~> 3.4)
grpc (1.24.0) grpc (1.27.0)
google-protobuf (~> 3.8) google-protobuf (~> 3.11)
googleapis-common-protos-types (~> 1.0) googleapis-common-protos-types (~> 1.0)
gssapi (1.2.0) gssapi (1.2.0)
ffi (>= 1.0.1) ffi (>= 1.0.1)
...@@ -1251,7 +1251,7 @@ DEPENDENCIES ...@@ -1251,7 +1251,7 @@ DEPENDENCIES
gitlab_omniauth-ldap (~> 2.1.1) gitlab_omniauth-ldap (~> 2.1.1)
gon (~> 6.2) gon (~> 6.2)
google-api-client (~> 0.23) google-api-client (~> 0.23)
google-protobuf (~> 3.8.0) google-protobuf (~> 3.11.2)
gpgme (~> 2.0.19) gpgme (~> 2.0.19)
grape (~> 1.1.0) grape (~> 1.1.0)
grape-entity (~> 0.7.1) grape-entity (~> 0.7.1)
...@@ -1260,7 +1260,7 @@ DEPENDENCIES ...@@ -1260,7 +1260,7 @@ DEPENDENCIES
graphiql-rails (~> 1.4.10) graphiql-rails (~> 1.4.10)
graphql (~> 1.10.5) graphql (~> 1.10.5)
graphql-docs (~> 1.6.0) graphql-docs (~> 1.6.0)
grpc (~> 1.24.0) grpc (~> 1.27.0)
gssapi gssapi
guard-rspec guard-rspec
haml_lint (~> 0.34.0) haml_lint (~> 0.34.0)
......
...@@ -222,7 +222,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController ...@@ -222,7 +222,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
def metrics_dashboard_params def metrics_dashboard_params
params params
.permit(:embedded, :group, :title, :y_label, :dashboard_path, :environment, :sample_metrics) .permit(:embedded, :group, :title, :y_label, :dashboard_path, :environment, :sample_metrics, :embed_json)
.merge(dashboard_path: params[:dashboard], environment: environment) .merge(dashboard_path: params[:dashboard], environment: environment)
end end
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
class EnvironmentsFinder class EnvironmentsFinder
attr_reader :project, :current_user, :params attr_reader :project, :current_user, :params
InvalidStatesError = Class.new(StandardError)
def initialize(project, current_user, params = {}) def initialize(project, current_user, params = {})
@project, @current_user, @params = project, current_user, params @project, @current_user, @params = project, current_user, params
end end
...@@ -45,6 +47,9 @@ class EnvironmentsFinder ...@@ -45,6 +47,9 @@ class EnvironmentsFinder
environments = by_name(environments) environments = by_name(environments)
environments = by_search(environments) environments = by_search(environments)
# Raises InvalidStatesError if params[:states] contains invalid states.
environments = by_states(environments)
environments environments
end end
...@@ -91,4 +96,27 @@ class EnvironmentsFinder ...@@ -91,4 +96,27 @@ class EnvironmentsFinder
environments environments
end end
end end
def by_states(environments)
if params[:states].present?
environments_with_states(environments)
else
environments
end
end
def environments_with_states(environments)
# Convert to array of strings
states = Array(params[:states]).map(&:to_s)
raise InvalidStatesError, _('Requested states are invalid') unless valid_states?(states)
environments.with_states(states)
end
def valid_states?(states)
valid_states = Environment.valid_states.map(&:to_s)
(states - valid_states).empty?
end
end end
...@@ -10,6 +10,10 @@ module Resolvers ...@@ -10,6 +10,10 @@ module Resolvers
required: false, required: false,
description: 'Search query' description: 'Search query'
argument :states, [GraphQL::STRING_TYPE],
required: false,
description: 'States of environments that should be included in result'
type Types::EnvironmentType, null: true type Types::EnvironmentType, null: true
alias_method :project, :object alias_method :project, :object
...@@ -18,6 +22,8 @@ module Resolvers ...@@ -18,6 +22,8 @@ module Resolvers
return unless project.present? return unless project.present?
EnvironmentsFinder.new(project, context[:current_user], args).find EnvironmentsFinder.new(project, context[:current_user], args).find
rescue EnvironmentsFinder::InvalidStatesError => exception
raise Gitlab::Graphql::Errors::ArgumentError, exception.message
end end
end end
end end
...@@ -119,6 +119,10 @@ class Environment < ApplicationRecord ...@@ -119,6 +119,10 @@ class Environment < ApplicationRecord
find_or_create_by(name: name) find_or_create_by(name: name)
end end
def self.valid_states
self.state_machine.states.map(&:name)
end
class << self class << self
## ##
# This method returns stop actions (jobs) for multiple environments within one # This method returns stop actions (jobs) for multiple environments within one
......
...@@ -12,21 +12,48 @@ class UsersStatistics < ApplicationRecord ...@@ -12,21 +12,48 @@ class UsersStatistics < ApplicationRecord
:blocked :blocked
].freeze ].freeze
private class << self
def create_current_stats!
def highest_role_stats stats_by_role = highest_role_stats
return unless Feature.enabled?(:users_statistics)
create!(
{ without_groups_and_projects: without_groups_and_projects_stats,
owner: batch_count_for_access_level(Gitlab::Access::OWNER), with_highest_role_guest: stats_by_role[:guest],
maintainer: batch_count_for_access_level(Gitlab::Access::MAINTAINER), with_highest_role_reporter: stats_by_role[:reporter],
developer: batch_count_for_access_level(Gitlab::Access::DEVELOPER), with_highest_role_developer: stats_by_role[:developer],
reporter: batch_count_for_access_level(Gitlab::Access::REPORTER), with_highest_role_maintainer: stats_by_role[:maintainer],
guest: batch_count_for_access_level(Gitlab::Access::GUEST) with_highest_role_owner: stats_by_role[:owner],
} bots: bot_stats,
end blocked: blocked_stats
)
end
private
def highest_role_stats
{
owner: batch_count_for_access_level(Gitlab::Access::OWNER),
maintainer: batch_count_for_access_level(Gitlab::Access::MAINTAINER),
developer: batch_count_for_access_level(Gitlab::Access::DEVELOPER),
reporter: batch_count_for_access_level(Gitlab::Access::REPORTER),
guest: batch_count_for_access_level(Gitlab::Access::GUEST)
}
end
def without_groups_and_projects_stats
batch_count_for_access_level(nil)
end
def bot_stats
Gitlab::Database::BatchCount.batch_count(User.bots)
end
def blocked_stats
Gitlab::Database::BatchCount.batch_count(User.blocked)
end
def batch_count_for_access_level(access_level) def batch_count_for_access_level(access_level)
Gitlab::Database::BatchCount.batch_count(UserHighestRole.with_highest_access_level(access_level)) Gitlab::Database::BatchCount.batch_count(UserHighestRole.with_highest_access_level(access_level))
end
end end
end end
# frozen_string_literal: true
# Acts as a pass-through to allow embeddable dashboards to be
# generated based on external data, but still processed with the
# required attributes that allow the FE to render them appropriately.
#
# Use Gitlab::Metrics::Dashboard::Finder to retrive dashboards.
module Metrics
module Dashboard
class TransientEmbedService < ::Metrics::Dashboard::BaseEmbedService
extend ::Gitlab::Utils::Override
class << self
def valid_params?(params)
[
embedded?(params[:embedded]),
params[:embed_json]
].all?
end
end
private
override :get_raw_dashboard
def get_raw_dashboard
JSON.parse(params[:embed_json])
end
override :sequence
def sequence
[STAGES::EndpointInserter]
end
end
end
end
...@@ -262,6 +262,13 @@ ...@@ -262,6 +262,13 @@
:resource_boundary: :unknown :resource_boundary: :unknown
:weight: 1 :weight: 1
:idempotent: :idempotent:
- :name: cronjob:users_create_statistics
:feature_category: :users
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent:
- :name: deployment:deployments_finished - :name: deployment:deployments_finished
:feature_category: :continuous_delivery :feature_category: :continuous_delivery
:has_external_dependencies: :has_external_dependencies:
......
# frozen_string_literal: true
module Users
class CreateStatisticsWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
# rubocop:disable Scalability/CronWorkerContext
# This worker does not perform work scoped to a context
include CronjobQueue
# rubocop:enable Scalability/CronWorkerContext
feature_category :users
def perform
UsersStatistics.create_current_stats!
rescue ActiveRecord::RecordInvalid => exception
Gitlab::ErrorTracking.track_exception(exception)
end
end
end
---
title: Add ability to search by environment state in environments GraphQL API
merge_request: 28567
author:
type: changed
---
title: Add Fluentd table for cluster apps
merge_request: 28844
author:
type: added
---
title: Add support for database-independent embedded metric charts
merge_request: 28618
author:
type: added
---
title: Add daily job to create users statistics
merge_request: 27883
author:
type: added
...@@ -552,6 +552,9 @@ Gitlab.ee do ...@@ -552,6 +552,9 @@ Gitlab.ee do
Settings.cron_jobs['sync_seat_link_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['sync_seat_link_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['sync_seat_link_worker']['cron'] ||= "#{rand(60)} 0 * * *" Settings.cron_jobs['sync_seat_link_worker']['cron'] ||= "#{rand(60)} 0 * * *"
Settings.cron_jobs['sync_seat_link_worker']['job_class'] = 'SyncSeatLinkWorker' Settings.cron_jobs['sync_seat_link_worker']['job_class'] = 'SyncSeatLinkWorker'
Settings.cron_jobs['users_create_statistics_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['users_create_statistics_worker']['cron'] ||= '2 15 * * *'
Settings.cron_jobs['users_create_statistics_worker']['job_class'] = 'Users::CreateStatisticsWorker'
end end
# #
......
# frozen_string_literal: true
class CreateClustersApplicationsFluentd < ActiveRecord::Migration[6.0]
DOWNTIME = false
def change
create_table :clusters_applications_fluentd do |t|
t.integer :protocol, null: false, limit: 2
t.integer :status, null: false
t.integer :port, null: false
t.references :cluster, null: false, index: { unique: true }, foreign_key: { on_delete: :cascade }
t.timestamps_with_timezone null: false
t.string :version, null: false, limit: 255
t.string :host, null: false, limit: 255
t.text :status_reason
end
end
end
...@@ -1649,6 +1649,28 @@ CREATE SEQUENCE public.clusters_applications_elastic_stacks_id_seq ...@@ -1649,6 +1649,28 @@ CREATE SEQUENCE public.clusters_applications_elastic_stacks_id_seq
ALTER SEQUENCE public.clusters_applications_elastic_stacks_id_seq OWNED BY public.clusters_applications_elastic_stacks.id; ALTER SEQUENCE public.clusters_applications_elastic_stacks_id_seq OWNED BY public.clusters_applications_elastic_stacks.id;
CREATE TABLE public.clusters_applications_fluentd (
id bigint NOT NULL,
protocol smallint NOT NULL,
status integer NOT NULL,
port integer NOT NULL,
cluster_id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
version character varying(255) NOT NULL,
host character varying(255) NOT NULL,
status_reason text
);
CREATE SEQUENCE public.clusters_applications_fluentd_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.clusters_applications_fluentd_id_seq OWNED BY public.clusters_applications_fluentd.id;
CREATE TABLE public.clusters_applications_helm ( CREATE TABLE public.clusters_applications_helm (
id integer NOT NULL, id integer NOT NULL,
cluster_id integer NOT NULL, cluster_id integer NOT NULL,
...@@ -7018,6 +7040,8 @@ ALTER TABLE ONLY public.clusters_applications_crossplane ALTER COLUMN id SET DEF ...@@ -7018,6 +7040,8 @@ ALTER TABLE ONLY public.clusters_applications_crossplane ALTER COLUMN id SET DEF
ALTER TABLE ONLY public.clusters_applications_elastic_stacks ALTER COLUMN id SET DEFAULT nextval('public.clusters_applications_elastic_stacks_id_seq'::regclass); ALTER TABLE ONLY public.clusters_applications_elastic_stacks ALTER COLUMN id SET DEFAULT nextval('public.clusters_applications_elastic_stacks_id_seq'::regclass);
ALTER TABLE ONLY public.clusters_applications_fluentd ALTER COLUMN id SET DEFAULT nextval('public.clusters_applications_fluentd_id_seq'::regclass);
ALTER TABLE ONLY public.clusters_applications_helm ALTER COLUMN id SET DEFAULT nextval('public.clusters_applications_helm_id_seq'::regclass); ALTER TABLE ONLY public.clusters_applications_helm ALTER COLUMN id SET DEFAULT nextval('public.clusters_applications_helm_id_seq'::regclass);
ALTER TABLE ONLY public.clusters_applications_ingress ALTER COLUMN id SET DEFAULT nextval('public.clusters_applications_ingress_id_seq'::regclass); ALTER TABLE ONLY public.clusters_applications_ingress ALTER COLUMN id SET DEFAULT nextval('public.clusters_applications_ingress_id_seq'::regclass);
...@@ -7687,6 +7711,9 @@ ALTER TABLE ONLY public.clusters_applications_crossplane ...@@ -7687,6 +7711,9 @@ ALTER TABLE ONLY public.clusters_applications_crossplane
ALTER TABLE ONLY public.clusters_applications_elastic_stacks ALTER TABLE ONLY public.clusters_applications_elastic_stacks
ADD CONSTRAINT clusters_applications_elastic_stacks_pkey PRIMARY KEY (id); ADD CONSTRAINT clusters_applications_elastic_stacks_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.clusters_applications_fluentd
ADD CONSTRAINT clusters_applications_fluentd_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.clusters_applications_helm ALTER TABLE ONLY public.clusters_applications_helm
ADD CONSTRAINT clusters_applications_helm_pkey PRIMARY KEY (id); ADD CONSTRAINT clusters_applications_helm_pkey PRIMARY KEY (id);
...@@ -8892,6 +8919,8 @@ CREATE UNIQUE INDEX index_clusters_applications_crossplane_on_cluster_id ON publ ...@@ -8892,6 +8919,8 @@ CREATE UNIQUE INDEX index_clusters_applications_crossplane_on_cluster_id ON publ
CREATE UNIQUE INDEX index_clusters_applications_elastic_stacks_on_cluster_id ON public.clusters_applications_elastic_stacks USING btree (cluster_id); CREATE UNIQUE INDEX index_clusters_applications_elastic_stacks_on_cluster_id ON public.clusters_applications_elastic_stacks USING btree (cluster_id);
CREATE UNIQUE INDEX index_clusters_applications_fluentd_on_cluster_id ON public.clusters_applications_fluentd USING btree (cluster_id);
CREATE UNIQUE INDEX index_clusters_applications_helm_on_cluster_id ON public.clusters_applications_helm USING btree (cluster_id); CREATE UNIQUE INDEX index_clusters_applications_helm_on_cluster_id ON public.clusters_applications_helm USING btree (cluster_id);
CREATE UNIQUE INDEX index_clusters_applications_ingress_on_cluster_id ON public.clusters_applications_ingress USING btree (cluster_id); CREATE UNIQUE INDEX index_clusters_applications_ingress_on_cluster_id ON public.clusters_applications_ingress USING btree (cluster_id);
...@@ -11164,6 +11193,9 @@ ALTER TABLE ONLY public.ci_refs ...@@ -11164,6 +11193,9 @@ ALTER TABLE ONLY public.ci_refs
ALTER TABLE ONLY public.ci_resources ALTER TABLE ONLY public.ci_resources
ADD CONSTRAINT fk_rails_430336af2d FOREIGN KEY (resource_group_id) REFERENCES public.ci_resource_groups(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_430336af2d FOREIGN KEY (resource_group_id) REFERENCES public.ci_resource_groups(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.clusters_applications_fluentd
ADD CONSTRAINT fk_rails_4319b1dcd2 FOREIGN KEY (cluster_id) REFERENCES public.clusters(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.lfs_file_locks ALTER TABLE ONLY public.lfs_file_locks
ADD CONSTRAINT fk_rails_43df7a0412 FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_43df7a0412 FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE;
...@@ -12976,6 +13008,7 @@ COPY "schema_migrations" (version) FROM STDIN; ...@@ -12976,6 +13008,7 @@ COPY "schema_migrations" (version) FROM STDIN;
20200331220930 20200331220930
20200402123926 20200402123926
20200402135250 20200402135250
20200402185044
20200403184110 20200403184110
20200403185127 20200403185127
20200403185422 20200403185422
......
...@@ -5875,6 +5875,11 @@ type Project { ...@@ -5875,6 +5875,11 @@ type Project {
Search query Search query
""" """
search: String search: String
"""
States of environments that should be included in result
"""
states: [String!]
): EnvironmentConnection ): EnvironmentConnection
""" """
......
...@@ -17785,6 +17785,24 @@ ...@@ -17785,6 +17785,24 @@
}, },
"defaultValue": null "defaultValue": null
}, },
{
"name": "states",
"description": "States of environments that should be included in result",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
},
"defaultValue": null
},
{ {
"name": "after", "name": "after",
"description": "Returns the elements in the list that come after the specified cursor.", "description": "Returns the elements in the list that come after the specified cursor.",
......
...@@ -182,7 +182,7 @@ before_script: ...@@ -182,7 +182,7 @@ before_script:
## Assuming you created the SSH_KNOWN_HOSTS variable, uncomment the ## Assuming you created the SSH_KNOWN_HOSTS variable, uncomment the
## following two lines. ## following two lines.
## ##
- echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts - echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts - chmod 644 ~/.ssh/known_hosts
## ##
...@@ -199,7 +199,7 @@ before_script: ...@@ -199,7 +199,7 @@ before_script:
## WARNING: Use this only with the Docker executor, if you use it with shell ## WARNING: Use this only with the Docker executor, if you use it with shell
## you will overwrite your user's SSH config. ## you will overwrite your user's SSH config.
## ##
#- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' #- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" >> ~/.ssh/config'
``` ```
## Example project ## Example project
......
...@@ -298,6 +298,9 @@ Some analyzers make it possible to filter out vulnerabilities under a given thre ...@@ -298,6 +298,9 @@ Some analyzers make it possible to filter out vulnerabilities under a given thre
| `SAST_FLAWFINDER_LEVEL` | 1 | Ignore Flawfinder vulnerabilities under given risk level. Integer, 0=No risk, 5=High risk. | | `SAST_FLAWFINDER_LEVEL` | 1 | Ignore Flawfinder vulnerabilities under given risk level. Integer, 0=No risk, 5=High risk. |
| `SAST_GITLEAKS_ENTROPY_LEVEL` | 8.0 | Minimum entropy for secret detection. Float, 0.0 = low, 8.0 = high. | | `SAST_GITLEAKS_ENTROPY_LEVEL` | 8.0 | Minimum entropy for secret detection. Float, 0.0 = low, 8.0 = high. |
| `SAST_GOSEC_LEVEL` | 0 | Ignore gosec vulnerabilities under given confidence level. Integer, 0=Undefined, 1=Low, 2=Medium, 3=High. | | `SAST_GOSEC_LEVEL` | 0 | Ignore gosec vulnerabilities under given confidence level. Integer, 0=Undefined, 1=Low, 2=Medium, 3=High. |
| `SAST_GITLEAKS_COMMIT_FROM` | - | The commit a gitleaks scan starts at. |
| `SAST_GITLEAKS_COMMIT_TO` | - | The commit a gitleaks scan ends at. |
| `SAST_GITLEAKS_HISTORIC_SCAN` | false | Flag to enable a historic gitleaks scan. |
#### Timeouts #### Timeouts
......
...@@ -110,7 +110,7 @@ module API ...@@ -110,7 +110,7 @@ module API
return unless %w[git-receive-pack git-upload-pack git-upload-archive].include?(action) return unless %w[git-receive-pack git-upload-pack git-upload-archive].include?(action)
{ {
repository: repository.gitaly_repository, repository: repository.gitaly_repository.to_h,
address: Gitlab::GitalyClient.address(container.repository_storage), address: Gitlab::GitalyClient.address(container.repository_storage),
token: Gitlab::GitalyClient.token(container.repository_storage), token: Gitlab::GitalyClient.token(container.repository_storage),
features: Feature::Gitaly.server_feature_flags features: Feature::Gitaly.server_feature_flags
......
...@@ -16,6 +16,7 @@ module Gitlab ...@@ -16,6 +16,7 @@ module Gitlab
::Metrics::Dashboard::GitlabAlertEmbedService, ::Metrics::Dashboard::GitlabAlertEmbedService,
::Metrics::Dashboard::CustomMetricEmbedService, ::Metrics::Dashboard::CustomMetricEmbedService,
::Metrics::Dashboard::GrafanaMetricEmbedService, ::Metrics::Dashboard::GrafanaMetricEmbedService,
::Metrics::Dashboard::TransientEmbedService,
::Metrics::Dashboard::DynamicEmbedService, ::Metrics::Dashboard::DynamicEmbedService,
::Metrics::Dashboard::DefaultEmbedService, ::Metrics::Dashboard::DefaultEmbedService,
::Metrics::Dashboard::SystemDashboardService, ::Metrics::Dashboard::SystemDashboardService,
......
...@@ -16997,6 +16997,9 @@ msgstr "" ...@@ -16997,6 +16997,9 @@ msgstr ""
msgid "Requested design version does not exist" msgid "Requested design version does not exist"
msgstr "" msgstr ""
msgid "Requested states are invalid"
msgstr ""
msgid "Requests Profiles" msgid "Requests Profiles"
msgstr "" msgstr ""
......
...@@ -7,6 +7,14 @@ FactoryBot.define do ...@@ -7,6 +7,14 @@ FactoryBot.define do
association :project, :repository association :project, :repository
sequence(:external_url) { |n| "https://env#{n}.example.gitlab.com" } sequence(:external_url) { |n| "https://env#{n}.example.gitlab.com" }
trait :available do
state { :available }
end
trait :stopped do
state { :stopped }
end
trait :with_review_app do |environment| trait :with_review_app do |environment|
transient do transient do
ref { 'master' } ref { 'master' }
......
# frozen_string_literal: true
FactoryBot.define do
factory :users_statistics do
end
end
...@@ -136,6 +136,36 @@ describe 'Metrics rendering', :js, :use_clean_rails_memory_store_caching, :sidek ...@@ -136,6 +136,36 @@ describe 'Metrics rendering', :js, :use_clean_rails_memory_store_caching, :sidek
end end
end end
context 'transient metrics embeds' do
let(:metrics_url) { urls.metrics_project_environment_url(project, environment, embed_json: embed_json) }
let(:title) { 'Important Metrics' }
let(:embed_json) do
{
panel_groups: [{
panels: [{
type: "line-graph",
title: title,
y_label: "metric",
metrics: [{
query_range: "metric * 0.5 < 1"
}]
}]
}]
}.to_json
end
before do
stub_any_prometheus_request_with_response
end
it 'shows embedded metrics' do
visit project_issue_path(project, issue)
expect(page).to have_css('div.prometheus-graph')
expect(page).to have_text(title)
end
end
def import_common_metrics def import_common_metrics
::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute ::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute
end end
......
...@@ -3,15 +3,15 @@ ...@@ -3,15 +3,15 @@
require 'spec_helper' require 'spec_helper'
describe EnvironmentsFinder do describe EnvironmentsFinder do
describe '#execute' do let(:project) { create(:project, :repository) }
let(:project) { create(:project, :repository) } let(:user) { project.creator }
let(:user) { project.creator } let(:environment) { create(:environment, :available, project: project) }
let(:environment) { create(:environment, project: project) }
before do before do
project.add_maintainer(user) project.add_maintainer(user)
end end
describe '#execute' do
context 'tagged deployment' do context 'tagged deployment' do
let(:environment_two) { create(:environment, project: project) } let(:environment_two) { create(:environment, project: project) }
# Environments need to include commits, so rewind two commits to fit # Environments need to include commits, so rewind two commits to fit
...@@ -124,4 +124,53 @@ describe EnvironmentsFinder do ...@@ -124,4 +124,53 @@ describe EnvironmentsFinder do
end end
end end
end end
describe '#find' do
context 'with states parameter' do
let(:stopped_environment) { create(:environment, :stopped, project: project) }
it 'returns environments with the requested state' do
result = described_class.new(project, user, states: 'available').find
expect(result).to contain_exactly(environment)
end
it 'returns environments with any of the requested states' do
result = described_class.new(project, user, states: %w(available stopped)).find
expect(result).to contain_exactly(environment, stopped_environment)
end
it 'raises exception when requested state is invalid' do
expect { described_class.new(project, user, states: %w(invalid stopped)).find }.to(
raise_error(described_class::InvalidStatesError, 'Requested states are invalid')
)
end
context 'works with symbols' do
it 'returns environments with the requested state' do
result = described_class.new(project, user, states: :available).find
expect(result).to contain_exactly(environment)
end
it 'returns environments with any of the requested states' do
result = described_class.new(project, user, states: [:available, :stopped]).find
expect(result).to contain_exactly(environment, stopped_environment)
end
end
end
context 'with search and states' do
let(:environment2) { create(:environment, :stopped, name: 'test2', project: project) }
let(:environment3) { create(:environment, :available, name: 'test3', project: project) }
it 'searches environments by name and state' do
result = described_class.new(project, user, search: 'test', states: :available).find
expect(result).to contain_exactly(environment3)
end
end
end
end end
...@@ -10,9 +10,9 @@ describe Resolvers::EnvironmentsResolver do ...@@ -10,9 +10,9 @@ describe Resolvers::EnvironmentsResolver do
context "with a group" do context "with a group" do
let(:group) { create(:group) } let(:group) { create(:group) }
let(:project) { create(:project, :public, group: group) } let(:project) { create(:project, :public, group: group) }
let!(:environment1) { create(:environment, name: 'production', project: project) } let!(:environment1) { create(:environment, :available, name: 'production', project: project) }
let!(:environment2) { create(:environment, name: 'test', project: project) } let!(:environment2) { create(:environment, :stopped, name: 'test', project: project) }
let!(:environment3) { create(:environment, name: 'test2', project: project) } let!(:environment3) { create(:environment, :available, name: 'test2', project: project) }
before do before do
group.add_developer(current_user) group.add_developer(current_user)
...@@ -41,6 +41,18 @@ describe Resolvers::EnvironmentsResolver do ...@@ -41,6 +41,18 @@ describe Resolvers::EnvironmentsResolver do
end end
end end
context 'with states' do
it 'searches environments by state' do
expect(resolve_environments(states: ['available'])).to contain_exactly(environment1, environment3)
end
it 'returns error if requested state is invalid' do
expect { resolve_environments(states: ['invalid']) }.to(
raise_error(Gitlab::Graphql::Errors::ArgumentError)
)
end
end
context 'when project is nil' do context 'when project is nil' do
subject { resolve(described_class, obj: nil, args: {}, ctx: { current_user: current_user }) } subject { resolve(described_class, obj: nil, args: {}, ctx: { current_user: current_user }) }
......
...@@ -98,6 +98,17 @@ describe Gitlab::Metrics::Dashboard::ServiceSelector do ...@@ -98,6 +98,17 @@ describe Gitlab::Metrics::Dashboard::ServiceSelector do
it { is_expected.to be Metrics::Dashboard::GrafanaMetricEmbedService } it { is_expected.to be Metrics::Dashboard::GrafanaMetricEmbedService }
end end
context 'with the embed defined in the arguments' do
let(:arguments) do
{
embedded: true,
embed_json: '{}'
}
end
it { is_expected.to be Metrics::Dashboard::TransientEmbedService }
end
end end
end end
end end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe UsersStatistics do
describe '.create_current_stats!' do
before do
create_list(:user_highest_role, 4)
create_list(:user_highest_role, 2, :guest)
create_list(:user_highest_role, 3, :reporter)
create_list(:user_highest_role, 4, :developer)
create_list(:user_highest_role, 3, :maintainer)
create_list(:user_highest_role, 2, :owner)
create_list(:user, 2, :bot)
create_list(:user, 1, :blocked)
allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(false)
end
context 'when successful' do
it 'creates an entry with the current statistics values' do
expect(described_class.create_current_stats!).to have_attributes(
without_groups_and_projects: 4,
with_highest_role_guest: 2,
with_highest_role_reporter: 3,
with_highest_role_developer: 4,
with_highest_role_maintainer: 3,
with_highest_role_owner: 2,
bots: 2,
blocked: 1
)
end
end
context 'when unsuccessful' do
it 'raises an ActiveRecord::RecordInvalid exception' do
allow(UsersStatistics).to receive(:create!).and_raise(ActiveRecord::RecordInvalid)
expect { described_class.create_current_stats! }.to raise_error(ActiveRecord::RecordInvalid)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Metrics::Dashboard::TransientEmbedService, :use_clean_rails_memory_store_caching do
let_it_be(:project) { build(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:environment) { create(:environment, project: project) }
before do
project.add_maintainer(user)
end
describe '.valid_params?' do
let(:params) { { embedded: 'true', embed_json: '{}' } }
subject { described_class.valid_params?(params) }
it { is_expected.to be_truthy }
context 'missing embedded' do
let(:params) { { embed_json: '{}' } }
it { is_expected.to be_falsey }
end
context 'not embedded' do
let(:params) { { embedded: 'false', embed_json: '{}' } }
it { is_expected.to be_falsey }
end
context 'missing embed_json' do
let(:params) { { embedded: 'true' } }
it { is_expected.to be_falsey }
end
end
describe '#get_dashboard' do
let(:embed_json) do
{
panel_groups: [{
panels: [{
type: 'line-graph',
title: 'title',
y_label: 'y_label',
metrics: [{
query_range: 'up',
label: 'y_label'
}]
}]
}]
}.to_json
end
let(:service_params) { [project, user, { environment: environment, embedded: 'true', embed_json: embed_json }] }
let(:service_call) { described_class.new(*service_params).get_dashboard }
it_behaves_like 'valid embedded dashboard service response'
it_behaves_like 'raises error for users with insufficient permissions'
it 'caches the unprocessed dashboard for subsequent calls' do
expect_any_instance_of(described_class)
.to receive(:get_raw_dashboard)
.once
.and_call_original
described_class.new(*service_params).get_dashboard
described_class.new(*service_params).get_dashboard
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Users::CreateStatisticsWorker do
describe '#perform' do
subject { described_class.new.perform }
before do
allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(false)
end
context 'when successful' do
it 'create an users statistics entry' do
expect { subject }.to change { UsersStatistics.count }.from(0).to(1)
end
end
context 'when unsuccessful' do
it 'logs an error' do
users_statistics = build(:users_statistics)
users_statistics.errors.add(:base, 'This is an error')
exception = ActiveRecord::RecordInvalid.new(users_statistics)
allow(UsersStatistics).to receive(:create_current_stats!).and_raise(exception)
expect(Gitlab::ErrorTracking).to receive(:track_exception).with(exception).and_call_original
subject
end
end
end
end
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