Support flat response for envs index route
To support environment folders in the UI on the Environments List page, the environments index route previously returned one environment per folder, excluding those other than the latest deploy. However, the environtments dropdown on the metrics dashboard requires that any environment be selectable. To accommodate both use cases, support an optional 'nested' parameter in the index route to return either a flat, complete response or a nested response based on the use case in question. The new default response structure is the flat response.
This commit is contained in:
parent
3fd9e48a7e
commit
db054bc166
9 changed files with 97 additions and 73 deletions
|
@ -143,7 +143,7 @@ export default {
|
||||||
*/
|
*/
|
||||||
created() {
|
created() {
|
||||||
this.service = new EnvironmentsService(this.endpoint);
|
this.service = new EnvironmentsService(this.endpoint);
|
||||||
this.requestData = { page: this.page, scope: this.scope };
|
this.requestData = { page: this.page, scope: this.scope, nested: true };
|
||||||
|
|
||||||
this.poll = new Poll({
|
this.poll = new Poll({
|
||||||
resource: this.service,
|
resource: this.service,
|
||||||
|
|
|
@ -7,8 +7,8 @@ export default class EnvironmentsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchEnvironments(options = {}) {
|
fetchEnvironments(options = {}) {
|
||||||
const { scope, page } = options;
|
const { scope, page, nested } = options;
|
||||||
return axios.get(this.environmentsEndpoint, { params: { scope, page } });
|
return axios.get(this.environmentsEndpoint, { params: { scope, page, nested } });
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line class-methods-use-this
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
|
|
@ -20,7 +20,8 @@ export default class EnvironmentsStore {
|
||||||
*
|
*
|
||||||
* Stores the received environments.
|
* Stores the received environments.
|
||||||
*
|
*
|
||||||
* In the main environments endpoint, each environment has the following schema
|
* In the main environments endpoint (with { nested: true } in params), each folder
|
||||||
|
* has the following schema:
|
||||||
* { name: String, size: Number, latest: Object }
|
* { name: String, size: Number, latest: Object }
|
||||||
* In the endpoint to retrieve environments from each folder, the environment does
|
* In the endpoint to retrieve environments from each folder, the environment does
|
||||||
* not have the `latest` key and the data is all in the root level.
|
* not have the `latest` key and the data is all in the root level.
|
||||||
|
|
|
@ -196,13 +196,13 @@ export default {
|
||||||
class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up"
|
class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up"
|
||||||
>
|
>
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="environment in store.environmentsData" :key="environment.latest.id">
|
<li v-for="environment in store.environmentsData" :key="environment.id">
|
||||||
<a
|
<a
|
||||||
:href="environment.latest.metrics_path"
|
:href="environment.metrics_path"
|
||||||
:class="{ 'is-active': environment.latest.name == currentEnvironmentName }"
|
:class="{ 'is-active': environment.name == currentEnvironmentName }"
|
||||||
class="dropdown-item"
|
class="dropdown-item"
|
||||||
>
|
>
|
||||||
{{ environment.latest.name }}
|
{{ environment.name }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -66,9 +66,7 @@ export default class MonitoringStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
storeEnvironmentsData(environmentsData = []) {
|
storeEnvironmentsData(environmentsData = []) {
|
||||||
this.environmentsData = environmentsData.filter(
|
this.environmentsData = environmentsData.filter(environment => !!environment.last_deployment);
|
||||||
environment => !!environment.latest.last_deployment,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getMetricsCount() {
|
getMetricsCount() {
|
||||||
|
|
|
@ -15,6 +15,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
|
||||||
push_frontend_feature_flag(:area_chart, project)
|
push_frontend_feature_flag(:area_chart, project)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns all environments or all folders based on the :nested param
|
||||||
def index
|
def index
|
||||||
@environments = project.environments
|
@environments = project.environments
|
||||||
.with_state(params[:scope] || :available)
|
.with_state(params[:scope] || :available)
|
||||||
|
@ -25,11 +26,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
|
||||||
Gitlab::PollingInterval.set_header(response, interval: 3_000)
|
Gitlab::PollingInterval.set_header(response, interval: 3_000)
|
||||||
|
|
||||||
render json: {
|
render json: {
|
||||||
environments: EnvironmentSerializer
|
environments: serialize_environments(request, response, params[:nested]),
|
||||||
.new(project: @project, current_user: @current_user)
|
|
||||||
.with_pagination(request, response)
|
|
||||||
.within_folders
|
|
||||||
.represent(@environments),
|
|
||||||
available_count: project.environments.available.count,
|
available_count: project.environments.available.count,
|
||||||
stopped_count: project.environments.stopped.count
|
stopped_count: project.environments.stopped.count
|
||||||
}
|
}
|
||||||
|
@ -37,6 +34,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns all environments for a given folder
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
# rubocop: disable CodeReuse/ActiveRecord
|
||||||
def folder
|
def folder
|
||||||
folder_environments = project.environments.where(environment_type: params[:id])
|
folder_environments = project.environments.where(environment_type: params[:id])
|
||||||
|
@ -48,10 +46,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
|
||||||
format.html
|
format.html
|
||||||
format.json do
|
format.json do
|
||||||
render json: {
|
render json: {
|
||||||
environments: EnvironmentSerializer
|
environments: serialize_environments(request, response),
|
||||||
.new(project: @project, current_user: @current_user)
|
|
||||||
.with_pagination(request, response)
|
|
||||||
.represent(@environments),
|
|
||||||
available_count: folder_environments.available.count,
|
available_count: folder_environments.available.count,
|
||||||
stopped_count: folder_environments.stopped.count
|
stopped_count: folder_environments.stopped.count
|
||||||
}
|
}
|
||||||
|
@ -186,6 +181,14 @@ class Projects::EnvironmentsController < Projects::ApplicationController
|
||||||
@environment ||= project.environments.find(params[:id])
|
@environment ||= project.environments.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def serialize_environments(request, response, nested = false)
|
||||||
|
serializer = EnvironmentSerializer
|
||||||
|
.new(project: @project, current_user: @current_user)
|
||||||
|
.with_pagination(request, response)
|
||||||
|
serializer = serializer.within_folders if nested
|
||||||
|
serializer.represent(@environments)
|
||||||
|
end
|
||||||
|
|
||||||
def authorize_stop_environment!
|
def authorize_stop_environment!
|
||||||
access_denied! unless can?(current_user, :stop_environment, environment)
|
access_denied! unless can?(current_user, :stop_environment, environment)
|
||||||
end
|
end
|
||||||
|
|
5
changelogs/unreleased/fix-49388.yml
Normal file
5
changelogs/unreleased/fix-49388.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Update metrics environment dropdown to show complete option set
|
||||||
|
merge_request: 24441
|
||||||
|
author:
|
||||||
|
type: fixed
|
|
@ -47,9 +47,43 @@ describe Projects::EnvironmentsController do
|
||||||
|
|
||||||
let(:environments) { json_response['environments'] }
|
let(:environments) { json_response['environments'] }
|
||||||
|
|
||||||
|
context 'with default parameters' do
|
||||||
|
before do
|
||||||
|
get :index, params: environment_params(format: :json)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'responds with a flat payload describing available environments' do
|
||||||
|
expect(environments.count).to eq 3
|
||||||
|
expect(environments.first['name']).to eq 'production'
|
||||||
|
expect(environments.second['name']).to eq 'staging/review-1'
|
||||||
|
expect(environments.third['name']).to eq 'staging/review-2'
|
||||||
|
expect(json_response['available_count']).to eq 3
|
||||||
|
expect(json_response['stopped_count']).to eq 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets the polling interval header' do
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect(response.headers['Poll-Interval']).to eq("3000")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a folder-based nested structure is requested' do
|
||||||
|
before do
|
||||||
|
get :index, params: environment_params(format: :json, nested: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'responds with a payload containing the latest environment for each folder' do
|
||||||
|
expect(environments.count).to eq 2
|
||||||
|
expect(environments.first['name']).to eq 'production'
|
||||||
|
expect(environments.second['name']).to eq 'staging'
|
||||||
|
expect(environments.second['size']).to eq 2
|
||||||
|
expect(environments.second['latest']['name']).to eq 'staging/review-2'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when requesting available environments scope' do
|
context 'when requesting available environments scope' do
|
||||||
before do
|
before do
|
||||||
get :index, params: environment_params(format: :json, scope: :available)
|
get :index, params: environment_params(format: :json, nested: true, scope: :available)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'responds with a payload describing available environments' do
|
it 'responds with a payload describing available environments' do
|
||||||
|
@ -64,16 +98,11 @@ describe Projects::EnvironmentsController do
|
||||||
expect(json_response['available_count']).to eq 3
|
expect(json_response['available_count']).to eq 3
|
||||||
expect(json_response['stopped_count']).to eq 1
|
expect(json_response['stopped_count']).to eq 1
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'sets the polling interval header' do
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
|
||||||
expect(response.headers['Poll-Interval']).to eq("3000")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when requesting stopped environments scope' do
|
context 'when requesting stopped environments scope' do
|
||||||
before do
|
before do
|
||||||
get :index, params: environment_params(format: :json, scope: :stopped)
|
get :index, params: environment_params(format: :json, nested: true, scope: :stopped)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'responds with a payload describing stopped environments' do
|
it 'responds with a payload describing stopped environments' do
|
||||||
|
|
|
@ -6597,58 +6597,46 @@ export function convertDatesMultipleSeries(multipleSeries) {
|
||||||
|
|
||||||
export const environmentData = [
|
export const environmentData = [
|
||||||
{
|
{
|
||||||
|
id: 34,
|
||||||
name: 'production',
|
name: 'production',
|
||||||
size: 1,
|
state: 'available',
|
||||||
latest: {
|
external_url: 'http://root-autodevops-deploy.my-fake-domain.com',
|
||||||
id: 34,
|
environment_type: null,
|
||||||
name: 'production',
|
stop_action: false,
|
||||||
state: 'available',
|
metrics_path: '/root/hello-prometheus/environments/34/metrics',
|
||||||
external_url: 'http://root-autodevops-deploy.my-fake-domain.com',
|
environment_path: '/root/hello-prometheus/environments/34',
|
||||||
environment_type: null,
|
stop_path: '/root/hello-prometheus/environments/34/stop',
|
||||||
stop_action: false,
|
terminal_path: '/root/hello-prometheus/environments/34/terminal',
|
||||||
metrics_path: '/root/hello-prometheus/environments/34/metrics',
|
folder_path: '/root/hello-prometheus/environments/folders/production',
|
||||||
environment_path: '/root/hello-prometheus/environments/34',
|
created_at: '2018-06-29T16:53:38.301Z',
|
||||||
stop_path: '/root/hello-prometheus/environments/34/stop',
|
updated_at: '2018-06-29T16:57:09.825Z',
|
||||||
terminal_path: '/root/hello-prometheus/environments/34/terminal',
|
last_deployment: {
|
||||||
folder_path: '/root/hello-prometheus/environments/folders/production',
|
id: 127,
|
||||||
created_at: '2018-06-29T16:53:38.301Z',
|
|
||||||
updated_at: '2018-06-29T16:57:09.825Z',
|
|
||||||
last_deployment: {
|
|
||||||
id: 127,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'review',
|
id: 35,
|
||||||
size: 1,
|
name: 'review/noop-branch',
|
||||||
latest: {
|
state: 'available',
|
||||||
id: 35,
|
external_url: 'http://root-autodevops-deploy-review-noop-branc-die93w.my-fake-domain.com',
|
||||||
name: 'review/noop-branch',
|
environment_type: 'review',
|
||||||
state: 'available',
|
stop_action: true,
|
||||||
external_url: 'http://root-autodevops-deploy-review-noop-branc-die93w.my-fake-domain.com',
|
metrics_path: '/root/hello-prometheus/environments/35/metrics',
|
||||||
environment_type: 'review',
|
environment_path: '/root/hello-prometheus/environments/35',
|
||||||
stop_action: true,
|
stop_path: '/root/hello-prometheus/environments/35/stop',
|
||||||
metrics_path: '/root/hello-prometheus/environments/35/metrics',
|
terminal_path: '/root/hello-prometheus/environments/35/terminal',
|
||||||
environment_path: '/root/hello-prometheus/environments/35',
|
folder_path: '/root/hello-prometheus/environments/folders/review',
|
||||||
stop_path: '/root/hello-prometheus/environments/35/stop',
|
created_at: '2018-07-03T18:39:41.702Z',
|
||||||
terminal_path: '/root/hello-prometheus/environments/35/terminal',
|
updated_at: '2018-07-03T18:44:54.010Z',
|
||||||
folder_path: '/root/hello-prometheus/environments/folders/review',
|
last_deployment: {
|
||||||
created_at: '2018-07-03T18:39:41.702Z',
|
id: 128,
|
||||||
updated_at: '2018-07-03T18:44:54.010Z',
|
|
||||||
last_deployment: {
|
|
||||||
id: 128,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'no-deployment',
|
id: 36,
|
||||||
size: 1,
|
name: 'no-deployment/noop-branch',
|
||||||
latest: {
|
state: 'available',
|
||||||
id: 36,
|
created_at: '2018-07-04T18:39:41.702Z',
|
||||||
name: 'no-deployment/noop-branch',
|
updated_at: '2018-07-04T18:44:54.010Z',
|
||||||
state: 'available',
|
|
||||||
created_at: '2018-07-04T18:39:41.702Z',
|
|
||||||
updated_at: '2018-07-04T18:44:54.010Z',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
Loading…
Reference in a new issue