Commit f8820e15 authored by Jason Goodman's avatar Jason Goodman Committed by Douglas Barbosa Alexandre

Return only environments not in folders in Environment Dashboard

Delete EnvironmentFolder class
parent 7c89f8e0
...@@ -65,6 +65,7 @@ class Environment < ApplicationRecord ...@@ -65,6 +65,7 @@ class Environment < ApplicationRecord
scope :for_project, -> (project) { where(project_id: project) } scope :for_project, -> (project) { where(project_id: project) }
scope :with_deployment, -> (sha) { where('EXISTS (?)', Deployment.select(1).where('deployments.environment_id = environments.id').where(sha: sha)) } scope :with_deployment, -> (sha) { where('EXISTS (?)', Deployment.select(1).where('deployments.environment_id = environments.id').where(sha: sha)) }
scope :unfoldered, -> { where(environment_type: nil) }
state_machine :state, initial: :available do state_machine :state, initial: :available do
event :start do event :start do
......
...@@ -286,6 +286,7 @@ class Project < ApplicationRecord ...@@ -286,6 +286,7 @@ class Project < ApplicationRecord
has_many :variables, class_name: 'Ci::Variable' has_many :variables, class_name: 'Ci::Variable'
has_many :triggers, class_name: 'Ci::Trigger' has_many :triggers, class_name: 'Ci::Trigger'
has_many :environments has_many :environments
has_many :unfoldered_environments, -> { unfoldered.available }, class_name: 'Environment'
has_many :deployments has_many :deployments
has_many :pipeline_schedules, class_name: 'Ci::PipelineSchedule' has_many :pipeline_schedules, class_name: 'Ci::PipelineSchedule'
has_many :project_deploy_tokens has_many :project_deploy_tokens
......
# frozen_string_literal: true
class EnvironmentFolder
attr_reader :last_environment, :size
delegate :project, to: :last_environment
def self.find_for_projects(projects)
environments = ::Environment.where(project: projects).available
t = ::Environment.arel_table
folder_data = environments
.group('COALESCE(environment_type, name), project_id')
.pluck(t[:id].maximum, t[:id].count)
environments_by_id = environments
.id_in(folder_data.map { |(env_id, _)| env_id })
.includes(:project, last_visible_deployment: [:project, deployable: :user])
.index_by(&:id)
folders = folder_data.map do |(environment_id, count)|
environment = environments_by_id[environment_id]
next unless environment
new(environments_by_id[environment_id], count)
end
projects_with_folders = folders.compact.group_by(&:project)
projects.map { |p| { p => [] } }.reduce({}, :merge).merge(projects_with_folders)
end
def initialize(last_environment, size)
@last_environment = last_environment
@size = size
end
def within_folder?
last_environment.environment_type.present? || size > 1
end
end
# frozen_string_literal: true
class DashboardEnvironmentsFolderEntity < Grape::Entity
expose :last_environment, merge: true, using: DashboardEnvironmentEntity
expose :size
expose :within_folder?, as: :within_folder
end
...@@ -13,7 +13,5 @@ class DashboardEnvironmentsProjectEntity < Grape::Entity ...@@ -13,7 +13,5 @@ class DashboardEnvironmentsProjectEntity < Grape::Entity
end end
expose :namespace, using: API::Entities::NamespaceBasic expose :namespace, using: API::Entities::NamespaceBasic
expose :environments, using: DashboardEnvironmentsFolderEntity do |_project, options| expose :unfoldered_environments, as: :environments, using: DashboardEnvironmentEntity
options[:folders]
end
end end
...@@ -2,10 +2,4 @@ ...@@ -2,10 +2,4 @@
class DashboardEnvironmentsSerializer < BaseSerializer class DashboardEnvironmentsSerializer < BaseSerializer
entity DashboardEnvironmentsProjectEntity entity DashboardEnvironmentsProjectEntity
def represent(projects_with_folders, opts = {}, entity_class = nil)
projects_with_folders.map do |project, folders|
super(project, opts.merge(folders: folders), entity_class)
end
end
end end
...@@ -16,11 +16,9 @@ module Dashboard ...@@ -16,11 +16,9 @@ module Dashboard
attr_reader :user attr_reader :user
def load_projects(user) def load_projects(user)
projects = ::Dashboard::Operations::ProjectsService ::Dashboard::Operations::ProjectsService
.new(user) .new(user)
.execute(user.ops_dashboard_projects) .execute(user.ops_dashboard_projects)
EnvironmentFolder.find_for_projects(projects)
end end
end end
end end
......
...@@ -344,9 +344,9 @@ describe OperationsController do ...@@ -344,9 +344,9 @@ describe OperationsController do
expect(project2_json['environments'].map { |e| e['id'] }).to eq([environment3.id]) expect(project2_json['environments'].map { |e| e['id'] }).to eq([environment3.id])
end end
it 'groups like environments together in a folder' do it 'does not return environments that would be grouped into a folder' do
create(:environment, project: project, name: 'review/test-feature') create(:environment, project: project, name: 'review/test-feature')
environment = create(:environment, project: project, name: 'review/another-feature') create(:environment, project: project, name: 'review/another-feature')
get :environments_list get :environments_list
...@@ -355,14 +355,11 @@ describe OperationsController do ...@@ -355,14 +355,11 @@ describe OperationsController do
project_json = json_response['projects'].first project_json = json_response['projects'].first
expect(project_json['environments'].count).to eq(1) expect(project_json['environments'].count).to eq(0)
expect(project_json['environments'].first['id']).to eq(environment.id)
expect(project_json['environments'].first['size']).to eq(2)
expect(project_json['environments'].first['within_folder']).to eq(true)
end end
it 'returns an environment not in a folder' do it 'does not return environments that would be grouped into a folder even when there is only a single environment' do
environment = create(:environment, project: project, name: 'production') create(:environment, project: project, name: 'staging/test-feature')
get :environments_list get :environments_list
...@@ -371,52 +368,11 @@ describe OperationsController do ...@@ -371,52 +368,11 @@ describe OperationsController do
project_json = json_response['projects'].first project_json = json_response['projects'].first
expect(project_json['environments'].count).to eq(1) expect(project_json['environments'].count).to eq(0)
expect(project_json['environments'].first['id']).to eq(environment.id)
expect(project_json['environments'].first['size']).to eq(1)
expect(project_json['environments'].first['within_folder']).to eq(false)
end end
it 'returns true for within_folder when a folder contains only a single environment' do it 'returns an environment not in a folder' do
environment = create(:environment, project: project, name: 'review/test-feature') environment = create(:environment, project: project, name: 'production')
get :environments_list
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('dashboard/operations/environments_list', dir: 'ee')
project_json = json_response['projects'].first
expect(project_json['environments'].count).to eq(1)
expect(project_json['environments'].first['id']).to eq(environment.id)
expect(project_json['environments'].first['size']).to eq(1)
expect(project_json['environments'].first['within_folder']).to eq(true)
end
it 'counts only available environments' do
create(:environment, project: project, name: 'review/test-feature', state: :available)
environment = create(:environment, project: project, name: 'review/another-feature', state: :available)
create(:environment, project: project, name: 'review/great-feature', state: :stopped)
get :environments_list
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('dashboard/operations/environments_list', dir: 'ee')
project_json = json_response['projects'].first
expect(project_json['environments'].count).to eq(1)
expect(project_json['environments'].first['size']).to eq(2)
expect(project_json['environments'].first['within_folder']).to eq(true)
expect(project_json['environments'].first['id']).to eq(environment.id)
end
it "excludes environments with the same folder name for other projects" do
project2 = create(:project)
create(:environment, project: project, name: 'review/test')
create(:environment, project: project2, name: 'review/test')
environment = create(:environment, project: project, name: 'review/something')
user.update!(ops_dashboard_projects: [project])
get :environments_list get :environments_list
...@@ -426,29 +382,6 @@ describe OperationsController do ...@@ -426,29 +382,6 @@ describe OperationsController do
project_json = json_response['projects'].first project_json = json_response['projects'].first
expect(project_json['environments'].count).to eq(1) expect(project_json['environments'].count).to eq(1)
expect(project_json['environments'].first['size']).to eq(2)
expect(project_json['environments'].first['within_folder']).to eq(true)
expect(project_json['environments'].first['id']).to eq(environment.id)
end
it "groups environments scoped to projects for multiple projects included in the user's ops dashboard" do
project2 = create(:project)
project2.add_developer(user)
environment = create(:environment, project: project, name: 'review/test')
create(:environment, project: project2, name: 'review/test')
create(:environment, project: project2, name: 'review/thing')
user.update!(ops_dashboard_projects: [project, project2])
get :environments_list
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('dashboard/operations/environments_list', dir: 'ee')
project_json = json_response['projects'].find { |p| p['id'] == project.id }
expect(project_json['environments'].count).to eq(1)
expect(project_json['environments'].first['size']).to eq(1)
expect(project_json['environments'].first['within_folder']).to eq(true)
expect(project_json['environments'].first['id']).to eq(environment.id) expect(project_json['environments'].first['id']).to eq(environment.id)
end end
......
...@@ -55,16 +55,12 @@ ...@@ -55,16 +55,12 @@
"required": [ "required": [
"id", "id",
"name", "name",
"size",
"within_folder",
"external_url", "external_url",
"environment_path" "environment_path"
], ],
"properties": { "properties": {
"id": { "type": "integer" }, "id": { "type": "integer" },
"name": { "type": "string" }, "name": { "type": "string" },
"size": { "type": "integer" },
"within_folder": { "type": "boolean" },
"external_url": { "type": "string" }, "external_url": { "type": "string" },
"environment_path": { "type": "string" }, "environment_path": { "type": "string" },
"last_deployment": { "$ref": "../../../../../../../spec/fixtures/api/schemas/deployment.json" } "last_deployment": { "$ref": "../../../../../../../spec/fixtures/api/schemas/deployment.json" }
......
...@@ -17,8 +17,6 @@ describe('Environment Header', () => { ...@@ -17,8 +17,6 @@ describe('Environment Header', () => {
environment_path: '/enivronment/1', environment_path: '/enivronment/1',
name: 'staging', name: 'staging',
external_url: 'http://example.com', external_url: 'http://example.com',
size: 1,
within_folder: false,
}, },
}; };
}); });
......
# frozen_string_literal: true
require 'spec_helper'
describe EnvironmentFolder do
describe '.find_for_projects' do
it 'returns an environment within a folder when the last environment does not have an environment_type' do
project = create(:project)
create(:environment, project: project, name: 'production/azure')
last_environment = create(:environment, project: project, name: 'production')
projects_with_environment_folders = described_class.find_for_projects([project])
environment_folder = projects_with_environment_folders[project].first
expect(environment_folder.last_environment.id).to eq(last_environment.id)
expect(environment_folder.within_folder?).to eq(true)
end
it 'returns an environment outside a folder' do
project = create(:project)
create(:environment, project: project, name: 'production')
projects_with_environment_folders = described_class.find_for_projects([project])
environment_folder = projects_with_environment_folders[project].first
expect(environment_folder.within_folder?).to eq(false)
end
it 'returns a project without any environments' do
project = create(:project)
projects_with_environment_folders = described_class.find_for_projects([project])
expect(projects_with_environment_folders).to eq({ project => [] })
end
it 'returns a project without any available environments' do
project = create(:project)
create(:environment, project: project, state: :stopped)
projects_with_environment_folders = described_class.find_for_projects([project])
expect(projects_with_environment_folders).to eq({ project => [] })
end
it 'returns multiple projects' do
project1 = create(:project)
project2 = create(:project)
create(:environment, project: project1, state: :stopped)
environment = create(:environment, project: project2, state: :available)
projects_with_environment_folders = described_class.find_for_projects([project1, project2])
expect(projects_with_environment_folders[project1]).to eq([])
expect(projects_with_environment_folders[project2].count).to eq(1)
environment_folder = projects_with_environment_folders[project2].first
expect(environment_folder.last_environment).to eq(environment)
end
it 'preloads only relevant ci_builds' do
project = create(:project)
ci_build_a = create(:ci_build, project: project)
ci_build_b = create(:ci_build, project: project)
ci_build_c = create(:ci_build, project: project)
environment_a = create(:environment, project: project)
environment_b = create(:environment, project: project)
create(:deployment, :success, project: project, environment: environment_a, deployable: ci_build_a)
create(:deployment, :success, project: project, environment: environment_a, deployable: ci_build_b)
create(:deployment, :success, project: project, environment: environment_b, deployable: ci_build_c)
expect(CommitStatus).to receive(:instantiate)
.with(a_hash_including("id" => ci_build_b.id), anything)
.and_call_original
expect(CommitStatus).to receive(:instantiate)
.with(a_hash_including("id" => ci_build_c.id), anything)
.and_call_original
described_class.find_for_projects([project])
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe DashboardEnvironmentsFolderEntity do
describe '.as_json' do
it 'includes folder and environment attributes' do
user = create(:user)
environment = create(:environment)
create(:deployment, project: environment.project, environment: environment, status: :success)
size = 1
environment_folder = EnvironmentFolder.new(environment, size)
request = EntityRequest.new(current_user: user)
result = described_class.new(environment_folder, request: request).as_json
expect(result.keys.sort).to eq([:environment_path, :external_url, :id, :last_deployment,
:last_pipeline, :name, :size, :within_folder])
end
end
end
...@@ -8,11 +8,9 @@ describe DashboardEnvironmentsProjectEntity do ...@@ -8,11 +8,9 @@ describe DashboardEnvironmentsProjectEntity do
current_user = create(:user) current_user = create(:user)
project = create(:project) project = create(:project)
environment = create(:environment) environment = create(:environment)
size = 1
environment_folder = EnvironmentFolder.new(environment, size)
entity_request = EntityRequest.new(current_user: current_user) entity_request = EntityRequest.new(current_user: current_user)
result = described_class.new(project, { folders: [environment_folder], request: entity_request }).as_json result = described_class.new(project, { environments: [environment], request: entity_request }).as_json
expect(result.keys.sort).to eq([:avatar_url, :environments, :id, :name, :namespace, :remove_path, :web_url]) expect(result.keys.sort).to eq([:avatar_url, :environments, :id, :name, :namespace, :remove_path, :web_url])
end end
......
...@@ -6,9 +6,9 @@ describe DashboardEnvironmentsSerializer do ...@@ -6,9 +6,9 @@ describe DashboardEnvironmentsSerializer do
describe '.represent' do describe '.represent' do
it 'returns an empty array when there are no projects' do it 'returns an empty array when there are no projects' do
current_user = create(:user) current_user = create(:user)
projects_with_folders = {} projects = []
result = described_class.new(current_user: current_user).represent(projects_with_folders) result = described_class.new(current_user: current_user).represent(projects)
expect(result).to eq([]) expect(result).to eq([])
end end
...@@ -16,12 +16,10 @@ describe DashboardEnvironmentsSerializer do ...@@ -16,12 +16,10 @@ describe DashboardEnvironmentsSerializer do
it 'includes project attributes' do it 'includes project attributes' do
current_user = create(:user) current_user = create(:user)
project = create(:project) project = create(:project)
environment = create(:environment) create(:environment, project: project, state: :available)
size = 1 projects = [project]
environment_folder = EnvironmentFolder.new(environment, size)
projects_with_folders = { project => [environment_folder] }
result = described_class.new(current_user: current_user).represent(projects_with_folders) result = described_class.new(current_user: current_user).represent(projects)
expect(result.first.keys.sort).to eq([:avatar_url, :environments, :id, :name, :namespace, :remove_path, :web_url]) expect(result.first.keys.sort).to eq([:avatar_url, :environments, :id, :name, :namespace, :remove_path, :web_url])
end end
......
...@@ -14,9 +14,9 @@ describe Dashboard::Environments::ListService do ...@@ -14,9 +14,9 @@ describe Dashboard::Environments::ListService do
project.add_developer(user) project.add_developer(user)
user.update!(ops_dashboard_projects: [project]) user.update!(ops_dashboard_projects: [project])
projects_with_folders = described_class.new(user).execute projects_with_environments = described_class.new(user).execute
expect(projects_with_folders).to eq({ project => [] }) expect(projects_with_environments).to eq([project])
end end
context 'when unlicensed' do context 'when unlicensed' do
...@@ -24,15 +24,15 @@ describe Dashboard::Environments::ListService do ...@@ -24,15 +24,15 @@ describe Dashboard::Environments::ListService do
stub_licensed_features(operations_dashboard: false) stub_licensed_features(operations_dashboard: false)
end end
it 'returns an empty hash' do it 'returns an empty array' do
user = create(:user) user = create(:user)
project = create(:project) project = create(:project)
project.add_developer(user) project.add_developer(user)
user.update!(ops_dashboard_projects: [project]) user.update!(ops_dashboard_projects: [project])
projects_with_folders = described_class.new(user).execute projects_with_environments = described_class.new(user).execute
expect(projects_with_folders).to eq({}) expect(projects_with_environments).to eq([])
end end
end end
end end
......
...@@ -339,6 +339,7 @@ project: ...@@ -339,6 +339,7 @@ project:
- triggers - triggers
- pipeline_schedules - pipeline_schedules
- environments - environments
- unfoldered_environments
- deployments - deployments
- project_feature - project_feature
- auto_devops - auto_devops
......
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