ui: Overview UI Routing (#12493)

This PR adds routes and an initial landing page for the Cluster Overview page
This commit is contained in:
John Cowen 2022-03-15 12:58:14 +00:00 committed by GitHub
parent 7fbfab669c
commit e711b920c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1102 additions and 45 deletions

View File

@ -0,0 +1,11 @@
import BaseAbility from './base';
import { inject as service } from '@ember/service';
export default class HcpAbility extends BaseAbility {
@service('env') env;
get is() {
return false;
// return this.env.var('CONSUL_HCP');
}
}

View File

@ -0,0 +1,6 @@
import BaseAbility from './base';
export default class LicenseAbility extends BaseAbility {
resource = 'operator';
segmented = false;
}

View File

@ -0,0 +1,8 @@
import BaseAbility from './base';
export default class OverviewAbility extends BaseAbility {
get canAccess() {
return ['read services', 'read nodes', 'read license']
.some(item => this.permissions.can(item))
}
}

View File

@ -0,0 +1,6 @@
import BaseAbility from './base';
export default class RaftAbility extends BaseAbility {
resource = 'operator';
segmented = false;
}

View File

@ -1,6 +1,6 @@
import BaseAbility from './base';
export default class ServiceAbility extends BaseAbility {
export default class ZerviceAbility extends BaseAbility {
resource = 'service';
get isLinkable() {

View File

@ -107,6 +107,20 @@
@nspaces={{this.nspaces}}
@onchange={{action (mut this.nspaces) value="data"}}
/>
{{#if (can 'access overview')}}
<li
data-test-main-nav-overview
class={{class-map
(array 'is-active' (is-href 'dc.show' @dc.Name))
}}
>
<Action
@href={{href-to 'dc.show' @dc.Name}}
>
Overview
</Action>
</li>
{{/if}}
{{#if (can "read services")}}
<li data-test-main-nav-services class={{if (is-href 'dc.services' @dc.Name) 'is-active'}}>
<a href={{href-to 'dc.services' @dc.Name}}>Services</a>

View File

@ -0,0 +1,9 @@
import Helper from 'ember-can/helpers/can';
export default class extends Helper {
_addAbilityObserver(ability, propertyName) {
if(!this.isDestroyed && !this.isDestroying) {
super._addAbilityObserver(...arguments);
}
}
}

View File

@ -3,8 +3,8 @@ import { get } from '@ember/object';
import { camelize } from '@ember/string';
export const is = (helper, [abilityString, model], properties) => {
let { abilityName, propertyName } = helper.can.parse(abilityString);
let ability = helper.can.abilityFor(abilityName, model, properties);
let { abilityName, propertyName } = helper.abilities.parse(abilityString);
let ability = helper.abilities.abilityFor(abilityName, model, properties);
if(typeof ability.getCharacteristicProperty === 'function') {
propertyName = ability.getCharacteristicProperty(propertyName);
@ -17,8 +17,8 @@ export const is = (helper, [abilityString, model], properties) => {
return get(ability, propertyName);
}
export default Helper.extend({
export default class extends Helper {
compute([abilityString, model], properties) {
return is(this, [abilityString, model], properties);
},
});
}
}

View File

@ -1,7 +1,7 @@
import Helper from 'ember-can/helpers/can';
import { is } from 'consul-ui/helpers/is';
import Helper from './can';
import { is } from './is';
export default Helper.extend({
export default class extends Helper {
compute([abilityString, model], properties) {
switch(true) {
case abilityString.startsWith('can '):
@ -10,5 +10,5 @@ export default Helper.extend({
return is(this, [abilityString.substr(3), model], properties);
}
throw new Error(`${abilityString} is not supported by the 'test' helper.`);
},
});
}
}

View File

@ -1,10 +1,10 @@
import Service from 'ember-can/services/can';
export default class CanService extends Service {
export default class AbilitiesService extends Service {
parse(str) {
// It's nicer to talk about SSO but technically its part of the authMethod
// ability, we probably only need 'use SSO' but if we need more, reasses
// ability, we probably only need 'use SSO' but if we need more, reassess
// the `replace`
return super.parse(str.replace('use SSO', ' use authMethods'));
return super.parse(str.replace('use SSO', 'use authMethods').replace('service', 'zervice'));
}
}

View File

@ -59,7 +59,7 @@ const REQUIRED_PERMISSIONS = [
];
export default class PermissionService extends RepositoryService {
@service('env') env;
@service('can') _can;
@service('abilities') _can;
// TODO: move this to the store, if we want it to use ember-data
// currently this overwrites an inherited permissions service (this service)

View File

@ -47,7 +47,7 @@ as |source|>
{{! redirect if we aren't on a URL with dc information }}
{{#if (eq route.currentName 'index')}}
{{did-insert (route-action 'replaceWith' 'dc.services.index'
{{did-insert (route-action 'replaceWith' 'dc.show'
(hash
dc=(env 'CONSUL_DATACENTER_LOCAL')
)

View File

@ -0,0 +1,38 @@
<Route
@name={{routeName}}
as |route|>
<AppView>
<BlockSlot @name="header">
<h1>
<route.Title @title={{compute (fn route.t 'title')}} />
</h1>
</BlockSlot>
<BlockSlot @name="toolbar">
</BlockSlot>
<BlockSlot @name="content">
<TabNav @items={{
compact
(array
(hash
label=(compute (fn route.t 'serverstatus.title'))
href=(href-to "dc.show.serverstatus")
selected=(is-href "dc.show.serverstatus")
)
(hash
label=(compute (fn route.t 'health.title'))
href=(href-to 'dc.show.health')
selected=(is-href 'dc.show.health')
)
(if (and (can 'read license') (not (is 'hcp')))
(hash
label=(compute (fn route.t 'license.title'))
href=(href-to 'dc.show.license')
selected=(is-href 'dc.show.license')
)
)
'')
}}/>
</BlockSlot>
</AppView>
</Route>

View File

@ -92,7 +92,7 @@
"deepmerge": "^4.2.2",
"ember-assign-helper": "^0.3.0",
"ember-auto-import": "^1.5.3",
"ember-can": "^3.0.0",
"ember-can": "^4.1.0",
"ember-changeset-conditional-validations": "^0.6.0",
"ember-changeset-validations": "~3.9.0",
"ember-cli": "~3.20.2",

View File

@ -1,6 +1,6 @@
@setupApplicationTest
Feature: dc / error: Recovering from a dc 500 error
Background:
Feature: dc / error
Scenario: Recovering from a dc 500 error
Given 2 datacenter models from yaml
---
- dc-1
@ -23,12 +23,7 @@ Feature: dc / error: Recovering from a dc 500 error
Then the url should be /dc-500/services
And the title should be "Consul"
Then I see status on the error like "500"
Scenario: Clicking the back to root button
Given the url "/v1/internal/ui/services" responds with a 200 status
When I click home
Then I see 3 service models
Scenario: Choosing a different dc from the dc menu
Given the url "/v1/internal/ui/services" responds with a 200 status
And the url "/v1/internal/ui/services" responds with a 200 status
When I click dc on the navigation
And I click dcs.0.name on the navigation
Then I see 3 service models

View File

@ -2,7 +2,7 @@
Feature: dc / forwarding
In order to arrive at a useful page when only specifying a dc in the url
As a user
I should be redirected to the services page for the dc
I should be redirected to the overview page for the dc
@notNamespaceable
Scenario: Arriving at the datacenter index page with no other url info
Given 1 datacenter model with the value "datacenter"
@ -10,4 +10,4 @@ Feature: dc / forwarding
---
dc: datacenter
---
Then the url should be /datacenter/services
Then the url should be /datacenter/overview/server-status

View File

@ -4,4 +4,4 @@ Feature: index-forwarding
Scenario: Arriving at the index page when there is only one datacenter
Given 1 datacenter model with the value "dc1"
When I visit the index page
Then the url should be /dc1/services
Then the url should be /dc1/overview/server-status

View File

@ -10,8 +10,7 @@ Feature: page-navigation
---
dc: dc1
---
Then the url should be /dc1/services
Then a GET request was made to "/v1/internal/ui/services?dc=dc1&ns=@namespace"
Then the url should be /dc1/overview/server-status
Scenario: Clicking [Link] in the navigation takes me to [URL]
When I visit the services page for yaml
---

View File

@ -4,16 +4,19 @@ Feature: token-header
In order to authenticate with tokens
As a user
I need to be able to specify a ACL token AND/OR leave it blank to authenticate with the API
Scenario: Arriving at the index page having not set a token previously
Scenario: Arriving at the service page having not set a token previously
Given 1 datacenter model with the value "dc1"
When I visit the index page
When I visit the services page for yaml
---
dc: dc1
---
Then the url should be /dc1/services
And a GET request was made to "/v1/internal/ui/services?dc=dc1&ns=@namespace" from yaml
---
headers:
X-Consul-Token: ''
---
Scenario: Set the token to [Token] and then navigate to the index page
Scenario: Set the token to [Token] and then navigate to the service page
Given 1 datacenter model with the value "dc1"
And the url "/v1/acl/tokens" responds with a 403 status
When I visit the tokens page for yaml
@ -27,7 +30,10 @@ Feature: token-header
SecretID: [Token]
---
And I click submit on the authdialog.form
When I visit the index page
When I visit the services page for yaml
---
dc: dc1
---
Then the url should be /dc1/services
And a GET request was made to "/v1/internal/ui/services?dc=dc1&ns=@namespace" from yaml
---

View File

@ -1,4 +1,12 @@
dc:
show:
title: Cluster Overview
serverstatus:
title: Server status
health:
title: Health
license:
title: License
nodes:
show:
healthchecks:

View File

@ -7,9 +7,34 @@
index: {
_options: {
path: '/',
redirect: '../services',
redirect: '../show/serverstatus',
},
},
show: {
_options: {
path: '/overview',
redirect: './serverstatus',
abilities: ['access overview']
},
serverstatus: {
_options: {
path: '/server-status',
abilities: ['access overview', 'read raft']
},
},
health: {
_options: {
path: '/health',
abilities: ['access overview']
},
},
license: {
_options: {
path: '/license',
abilities: ['access overview', 'read licence']
},
}
},
services: {
_options: { path: '/services' },
index: {

File diff suppressed because it is too large Load Diff