mirror of https://github.com/status-im/consul.git
ui: Partitions Application Layer (#11017)
* Add Partition to all our models * Add partitions into our serializers/fingerprinting * Make some amends to a few adapters ready for partitions * Amend blueprints to avoid linting error * Update all our repositories to include partitions, also Remove enabled/disable nspace repo and just use a nspace with conditionals * Ensure nspace and parition parameters always return '' no matter what * Ensure data-sink finds the model properly This will later be replaced by a @dataSink decorator but we are find kicking that can down the road a little more * Add all the new partition data layer * Add a way to set the title of the page from inside the route and make it accessibile via a route announcer * Make the Consul Route the default/basic one * Tweak nspace and partition abilities not to check the length * Thread partition through all the components that need it * Some ACL tweaks * Move the entire app to use partitions * Delete all the tests we no longer need * Update some Unit tests to use partition * Fix up KV title tests * Fix up a few more acceptance tests * Fixup and temporarily ignore some acceptance tests * Stop using ember-cli-page-objects fillable as it doesn't seem to work * Fix lint error * Remove old ACL related test * Add a tick after filling out forms * Fix token warning modal * Found some more places where we need a partition var * Fixup some more acceptance tests * Tokens still needs a repo service for CRUD * Remove acceptance tests we no longer need * Fixup and "FIXME ignore" a few tests * Remove an s * Disable blocking queries for KV to revert to previous release for now * Fixup adapter tests to follow async/function resolving interface * Fixup all the serializer integration tests * Fixup service/repo integration tests * Fixup deleting acceptance test * Fixup some ent tests * Make sure nspaces passes the dc through for when thats important * ...aaaand acceptance nspaces with the extra dc param
This commit is contained in:
parent
0eb4a98fab
commit
fc14a412fd
|
@ -16,7 +16,7 @@ export default class NspaceAbility extends BaseAbility {
|
||||||
}
|
}
|
||||||
|
|
||||||
get canChoose() {
|
get canChoose() {
|
||||||
return this.canUse && (this.nspaces || []).length > 0;
|
return this.canUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
get canUse() {
|
get canUse() {
|
||||||
|
|
|
@ -16,7 +16,7 @@ export default class PartitionAbility extends BaseAbility {
|
||||||
}
|
}
|
||||||
|
|
||||||
get canChoose() {
|
get canChoose() {
|
||||||
return this.canUse && (this.partitions || []).length > 0;
|
return this.canUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
get canUse() {
|
get canUse() {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Adapter from './application';
|
import Adapter from './application';
|
||||||
|
|
||||||
export default class BindingRuleAdapter extends Adapter {
|
export default class BindingRuleAdapter extends Adapter {
|
||||||
requestForQuery(request, { dc, ns, partition, authmethod, index, id }) {
|
requestForQuery(request, { dc, ns, partition, authmethod, index }) {
|
||||||
return request`
|
return request`
|
||||||
GET /v1/acl/binding-rules?${{ dc, authmethod }}
|
GET /v1/acl/binding-rules?${{ dc, authmethod }}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ export default class IntentionAdapter extends Adapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
${{
|
${{
|
||||||
|
partition: '',
|
||||||
ns: '*',
|
ns: '*',
|
||||||
index,
|
index,
|
||||||
filter,
|
filter,
|
||||||
|
|
|
@ -7,11 +7,11 @@ import { SLUG_KEY } from 'consul-ui/models/kv';
|
||||||
const API_KEYS_KEY = 'keys';
|
const API_KEYS_KEY = 'keys';
|
||||||
|
|
||||||
export default class KvAdapter extends Adapter {
|
export default class KvAdapter extends Adapter {
|
||||||
requestForQuery(request, { dc, ns, partition, index, id, separator }) {
|
async requestForQuery(request, { dc, ns, partition, index, id, separator }) {
|
||||||
if (typeof id === 'undefined') {
|
if (typeof id === 'undefined') {
|
||||||
throw new Error('You must specify an id');
|
throw new Error('You must specify an id');
|
||||||
}
|
}
|
||||||
return request`
|
const respond = await request`
|
||||||
GET /v1/kv/${keyToArray(id)}?${{ [API_KEYS_KEY]: null, dc, separator }}
|
GET /v1/kv/${keyToArray(id)}?${{ [API_KEYS_KEY]: null, dc, separator }}
|
||||||
|
|
||||||
${{
|
${{
|
||||||
|
@ -20,9 +20,11 @@ export default class KvAdapter extends Adapter {
|
||||||
index,
|
index,
|
||||||
}}
|
}}
|
||||||
`;
|
`;
|
||||||
|
await respond((headers, body) => delete headers['x-consul-index']);
|
||||||
|
return respond;
|
||||||
}
|
}
|
||||||
|
|
||||||
requestForQueryRecord(request, { dc, ns, partition, index, id }) {
|
async requestForQueryRecord(request, { dc, ns, partition, index, id }) {
|
||||||
if (typeof id === 'undefined') {
|
if (typeof id === 'undefined') {
|
||||||
throw new Error('You must specify an id');
|
throw new Error('You must specify an id');
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ export default class NodeAdapter extends Adapter {
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this does not require a partition parameter
|
||||||
requestForQueryLeader(request, { dc, uri }) {
|
requestForQueryLeader(request, { dc, uri }) {
|
||||||
return request`
|
return request`
|
||||||
GET /v1/status/leader?${{ dc }}
|
GET /v1/status/leader?${{ dc }}
|
||||||
|
|
|
@ -3,9 +3,9 @@ import { SLUG_KEY } from 'consul-ui/models/nspace';
|
||||||
|
|
||||||
// namespaces aren't categorized by datacenter, therefore no dc
|
// namespaces aren't categorized by datacenter, therefore no dc
|
||||||
export default class NspaceAdapter extends Adapter {
|
export default class NspaceAdapter extends Adapter {
|
||||||
requestForQuery(request, { partition, index, uri }) {
|
requestForQuery(request, { dc, partition, index, uri }) {
|
||||||
return request`
|
return request`
|
||||||
GET /v1/namespaces
|
GET /v1/namespaces?${{ dc }}
|
||||||
X-Request-ID: ${uri}
|
X-Request-ID: ${uri}
|
||||||
|
|
||||||
${{
|
${{
|
||||||
|
@ -15,12 +15,12 @@ export default class NspaceAdapter extends Adapter {
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
requestForQueryRecord(request, { partition, index, id }) {
|
requestForQueryRecord(request, { dc, partition, index, id }) {
|
||||||
if (typeof id === 'undefined') {
|
if (typeof id === 'undefined') {
|
||||||
throw new Error('You must specify an name');
|
throw new Error('You must specify an name');
|
||||||
}
|
}
|
||||||
return request`
|
return request`
|
||||||
GET /v1/namespace/${id}
|
GET /v1/namespace/${id}?${{ dc }}
|
||||||
|
|
||||||
${{
|
${{
|
||||||
partition,
|
partition,
|
||||||
|
@ -32,6 +32,7 @@ export default class NspaceAdapter extends Adapter {
|
||||||
requestForCreateRecord(request, serialized, data) {
|
requestForCreateRecord(request, serialized, data) {
|
||||||
return request`
|
return request`
|
||||||
PUT /v1/namespace/${data[SLUG_KEY]}?${{
|
PUT /v1/namespace/${data[SLUG_KEY]}?${{
|
||||||
|
dc: data.Datacenter,
|
||||||
partition: data.Partition,
|
partition: data.Partition,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
@ -49,6 +50,7 @@ export default class NspaceAdapter extends Adapter {
|
||||||
requestForUpdateRecord(request, serialized, data) {
|
requestForUpdateRecord(request, serialized, data) {
|
||||||
return request`
|
return request`
|
||||||
PUT /v1/namespace/${data[SLUG_KEY]}?${{
|
PUT /v1/namespace/${data[SLUG_KEY]}?${{
|
||||||
|
dc: data.Datacenter,
|
||||||
partition: data.Partition,
|
partition: data.Partition,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
@ -65,6 +67,7 @@ export default class NspaceAdapter extends Adapter {
|
||||||
requestForDeleteRecord(request, serialized, data) {
|
requestForDeleteRecord(request, serialized, data) {
|
||||||
return request`
|
return request`
|
||||||
DELETE /v1/namespace/${data[SLUG_KEY]}?${{
|
DELETE /v1/namespace/${data[SLUG_KEY]}?${{
|
||||||
|
dc: data.Datacenter,
|
||||||
partition: data.Partition,
|
partition: data.Partition,
|
||||||
}}
|
}}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import Adapter from './application';
|
||||||
|
|
||||||
|
// Blocking query support for partitions is currently disabled
|
||||||
|
export default class PartitionAdapter extends Adapter {
|
||||||
|
// FIXME: Check overall hierarchy again
|
||||||
|
async requestForQuery(request, { ns, dc, index }) {
|
||||||
|
const respond = await request`
|
||||||
|
GET /v1/partitions?${{ dc }}
|
||||||
|
|
||||||
|
${{ index }}
|
||||||
|
`;
|
||||||
|
await respond((headers, body) => delete headers['x-consul-index']);
|
||||||
|
return respond;
|
||||||
|
}
|
||||||
|
|
||||||
|
async requestForQueryRecord(request, { ns, dc, index, id }) {
|
||||||
|
if (typeof id === 'undefined') {
|
||||||
|
throw new Error('You must specify an id');
|
||||||
|
}
|
||||||
|
const respond = await request`
|
||||||
|
GET /v1/partition/${id}?${{ dc }}
|
||||||
|
|
||||||
|
${{ index }}
|
||||||
|
`;
|
||||||
|
await respond((headers, body) => delete headers['x-consul-index']);
|
||||||
|
return respond;
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ export default class PermissionAdapter extends Adapter {
|
||||||
resources = resources.map(item => ({ ...item, Partition: partition }));
|
resources = resources.map(item => ({ ...item, Partition: partition }));
|
||||||
}
|
}
|
||||||
return request`
|
return request`
|
||||||
POST /v1/internal/acl/authorize?${{ dc, index }}
|
POST /v1/internal/acl/authorize?${{ dc }}
|
||||||
|
|
||||||
${resources}
|
${resources}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -17,12 +17,13 @@ export default class TokenAdapter extends Adapter {
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
requestForQueryRecord(request, { dc, ns, partition, index, id }) {
|
async requestForQueryRecord(request, { dc, ns, partition, index, id }) {
|
||||||
if (typeof id === 'undefined') {
|
if (typeof id === 'undefined') {
|
||||||
throw new Error('You must specify an id');
|
throw new Error('You must specify an id');
|
||||||
}
|
}
|
||||||
return request`
|
const respond = await request`
|
||||||
GET /v1/acl/token/${id}?${{ dc }}
|
GET /v1/acl/token/${id}?${{ dc }}
|
||||||
|
Cache-Control: no-store
|
||||||
|
|
||||||
${{
|
${{
|
||||||
ns,
|
ns,
|
||||||
|
@ -30,6 +31,8 @@ export default class TokenAdapter extends Adapter {
|
||||||
index,
|
index,
|
||||||
}}
|
}}
|
||||||
`;
|
`;
|
||||||
|
respond((headers, body) => delete headers['x-consul-index']);
|
||||||
|
return respond;
|
||||||
}
|
}
|
||||||
|
|
||||||
requestForCreateRecord(request, serialized, data) {
|
requestForCreateRecord(request, serialized, data) {
|
||||||
|
|
|
@ -22,7 +22,11 @@
|
||||||
token=token
|
token=token
|
||||||
) (hash
|
) (hash
|
||||||
AuthProfile=(component 'auth-profile' item=token)
|
AuthProfile=(component 'auth-profile' item=token)
|
||||||
AuthForm=(component 'auth-form' dc=dc nspace=nspace onsubmit=(action sink.open value="data"))
|
AuthForm=(component 'auth-form'
|
||||||
|
dc=dc
|
||||||
|
partition=partition
|
||||||
|
nspace=nspace
|
||||||
|
onsubmit=(action sink.open value="data"))
|
||||||
) as |api components|}}
|
) as |api components|}}
|
||||||
<State @matches="authorized">
|
<State @matches="authorized">
|
||||||
{{#yield-slot name="authorized"}}
|
{{#yield-slot name="authorized"}}
|
||||||
|
|
|
@ -77,7 +77,13 @@
|
||||||
{{#if (env 'CONSUL_SSO_ENABLED')}}
|
{{#if (env 'CONSUL_SSO_ENABLED')}}
|
||||||
{{!-- This `or` can be completely removed post 1.10 as 1.10 has optional params with default values --}}
|
{{!-- This `or` can be completely removed post 1.10 as 1.10 has optional params with default values --}}
|
||||||
<DataSource
|
<DataSource
|
||||||
@src={{concat '/' (or nspace '') '/' dc '/oidc/providers'}}
|
@src={{uri '/${partition}/${nspace}/${dc}/oidc/providers'
|
||||||
|
(hash
|
||||||
|
partition=partition
|
||||||
|
nspace=(or nspace '')
|
||||||
|
dc=dc
|
||||||
|
)
|
||||||
|
}}
|
||||||
@onchange={{queue (action (mut providers) value="data")}}
|
@onchange={{queue (action (mut providers) value="data")}}
|
||||||
@onerror={{queue (action (mut error) value="error.errors.firstObject")}}
|
@onerror={{queue (action (mut error) value="error.errors.firstObject")}}
|
||||||
@loading="lazy"
|
@loading="lazy"
|
||||||
|
@ -96,9 +102,11 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<State @matches="loading">
|
<State @matches="loading">
|
||||||
|
{{!FIXME: default partition?}}
|
||||||
<TokenSource
|
<TokenSource
|
||||||
@dc={{dc}}
|
@dc={{dc}}
|
||||||
@nspace={{or value.Namespace nspace}}
|
@nspace={{or value.Namespace nspace}}
|
||||||
|
@partition={{or value.Partition 'default'}}
|
||||||
@type={{if value.Name 'oidc' 'secret'}}
|
@type={{if value.Name 'oidc' 'secret'}}
|
||||||
@value={{if value.Name value.Name value}}
|
@value={{if value.Name value.Name value}}
|
||||||
@onchange={{queue (action dispatch "RESET") (action onsubmit)}}
|
@onchange={{queue (action dispatch "RESET") (action onsubmit)}}
|
||||||
|
|
|
@ -9,7 +9,14 @@
|
||||||
<span><YieldSlot @name="label">{{yield}}</YieldSlot></span>
|
<span><YieldSlot @name="label">{{yield}}</YieldSlot></span>
|
||||||
{{#if isOpen}}
|
{{#if isOpen}}
|
||||||
<DataSource
|
<DataSource
|
||||||
@src={{concat '/' nspace '/' dc '/' (pluralize type)}}
|
@src={{uri '/${partition}/${nspace}/${dc}/${type}'
|
||||||
|
(hash
|
||||||
|
partition=partition
|
||||||
|
nspace=nspace
|
||||||
|
dc=dc
|
||||||
|
type=(pluralize type)
|
||||||
|
)
|
||||||
|
}}
|
||||||
@onchange={{action (mut allOptions) value="data"}}
|
@onchange={{action (mut allOptions) value="data"}}
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
<AppView>
|
||||||
|
<BlockSlot @name="header">
|
||||||
|
<h1>
|
||||||
|
Tokens
|
||||||
|
</h1>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="content">
|
||||||
|
<EmptyState data-test-acls-disabled>
|
||||||
|
<BlockSlot @name="header">
|
||||||
|
<h2>Welcome to ACLs</h2>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="body">
|
||||||
|
<p>
|
||||||
|
ACLs are not enabled in this Consul cluster. We strongly encourage the use of ACLs in production environments for the best security practices.
|
||||||
|
</p>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="actions">
|
||||||
|
<li class="docs-link">
|
||||||
|
<a href="{{env 'CONSUL_DOCS_URL'}}/acl/index.html" rel="noopener noreferrer" target="_blank">Read the documentation</a>
|
||||||
|
</li>
|
||||||
|
<li class="learn-link">
|
||||||
|
<a href="{{env 'CONSUL_DOCS_LEARN_URL'}}/consul/security-networking/production-acls" rel="noopener noreferrer" target="_blank">Follow the guide</a>
|
||||||
|
</li>
|
||||||
|
</BlockSlot>
|
||||||
|
</EmptyState>
|
||||||
|
</BlockSlot>
|
||||||
|
</AppView>
|
||||||
|
|
|
@ -6,7 +6,7 @@ A presentational component for rendering HealthChecks.
|
||||||
<figure>
|
<figure>
|
||||||
<figcaption>Grab some mock data...</figcaption>
|
<figcaption>Grab some mock data...</figcaption>
|
||||||
|
|
||||||
<DataSource @src="/default/dc-1/node/my-node" as |source|>
|
<DataSource @src="/partition/default/dc-1/node/my-node" as |source|>
|
||||||
<figure>
|
<figure>
|
||||||
<figcaption>but only show a max of 2 items for docs purposes</figcaption>
|
<figcaption>but only show a max of 2 items for docs purposes</figcaption>
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
@type="intention"
|
@type="intention"
|
||||||
@dc={{@dc}}
|
@dc={{@dc}}
|
||||||
@nspace={{@nspace}}
|
@nspace={{@nspace}}
|
||||||
|
@partition={{@partition}}
|
||||||
@autofill={{@autofill}}
|
@autofill={{@autofill}}
|
||||||
@item={{@item}}
|
@item={{@item}}
|
||||||
@src={{@src}}
|
@src={{@src}}
|
||||||
|
@ -73,15 +74,27 @@ as |api|>
|
||||||
{{/let}}
|
{{/let}}
|
||||||
|
|
||||||
<DataSource
|
<DataSource
|
||||||
@src={{concat '/*/' @dc '/services'}}
|
@src={{uri '/${partition}/*/${dc}/services'
|
||||||
|
(hash
|
||||||
|
partition=@partition
|
||||||
|
dc=@dc
|
||||||
|
)
|
||||||
|
}}
|
||||||
@onchange={{action this.createServices item}}
|
@onchange={{action this.createServices item}}
|
||||||
/>
|
/>
|
||||||
{{#if (env 'CONSUL_NSPACES_ENABLED')}}
|
|
||||||
|
{{#if (can 'use nspaces')}}
|
||||||
<DataSource
|
<DataSource
|
||||||
@src="/*/*/namespaces"
|
@src={{uri '/${partition}/*/${dc}/namespaces'
|
||||||
|
(hash
|
||||||
|
partition=@partition
|
||||||
|
dc=@dc
|
||||||
|
)
|
||||||
|
}}
|
||||||
@onchange={{action this.createNspaces item}}
|
@onchange={{action this.createNspaces item}}
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if (and api.isCreate this.isManagedByCRDs)}}
|
{{#if (and api.isCreate this.isManagedByCRDs)}}
|
||||||
<Consul::Intention::Notice::CustomResource @type="warning" />
|
<Consul::Intention::Notice::CustomResource @type="warning" />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<DataForm
|
<DataForm
|
||||||
@dc={{dc}}
|
@dc={{dc}}
|
||||||
@nspace={{nspace}}
|
@nspace={{nspace}}
|
||||||
|
@partition={{partition}}
|
||||||
@type="kv"
|
@type="kv"
|
||||||
@label="key"
|
@label="key"
|
||||||
@autofill={{autofill}}
|
@autofill={{autofill}}
|
||||||
|
@ -19,11 +20,11 @@
|
||||||
{{#if api.isCreate}}
|
{{#if api.isCreate}}
|
||||||
<label class="type-text{{if api.data.error.Key ' has-error'}}">
|
<label class="type-text{{if api.data.error.Key ' has-error'}}">
|
||||||
<span>Key or folder</span>
|
<span>Key or folder</span>
|
||||||
<input autofocus="autofocus" type="text" value={{left-trim api.data.Key parent.Key}} name="additional" oninput={{action api.change}} placeholder="Key or folder" />
|
<input autofocus="autofocus" type="text" value={{left-trim api.data.Key parent}} name="additional" oninput={{action api.change}} placeholder="Key or folder" />
|
||||||
<em>To create a folder, end a key with <code>/</code></em>
|
<em>To create a folder, end a key with <code>/</code></em>
|
||||||
</label>
|
</label>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (or (eq (left-trim api.data.Key parent.Key) '') (not-eq (last api.data.Key) '/'))}}
|
{{#if (or (eq (left-trim api.data.Key parent) '') (not-eq (last api.data.Key) '/'))}}
|
||||||
<div>
|
<div>
|
||||||
<div class="type-toggle">
|
<div class="type-toggle">
|
||||||
<label>
|
<label>
|
||||||
|
|
|
@ -26,7 +26,7 @@ export default Component.extend({
|
||||||
set(item, 'Value', this.encoder.execute(target.value));
|
set(item, 'Value', this.encoder.execute(target.value));
|
||||||
break;
|
break;
|
||||||
case 'additional':
|
case 'additional':
|
||||||
parent = get(this, 'parent.Key');
|
parent = get(this, 'parent');
|
||||||
set(item, 'Key', `${parent !== '/' ? parent : ''}${target.value}`);
|
set(item, 'Key', `${parent !== '/' ? parent : ''}${target.value}`);
|
||||||
break;
|
break;
|
||||||
case 'json':
|
case 'json':
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<DataForm
|
<DataForm
|
||||||
@dc={{dc}}
|
@dc={{dc}}
|
||||||
@nspace={{nspace}}
|
@nspace={{nspace}}
|
||||||
|
@partition={{partition}}
|
||||||
@item={{item}}
|
@item={{item}}
|
||||||
@type="session"
|
@type="session"
|
||||||
@onsubmit={{action onsubmit}}
|
@onsubmit={{action onsubmit}}
|
||||||
|
|
|
@ -4,7 +4,7 @@ class: ember
|
||||||
# Consul::LockSession::List
|
# Consul::LockSession::List
|
||||||
|
|
||||||
```hbs preview-template
|
```hbs preview-template
|
||||||
<DataSource @src="/default/dc-1/sessions/for-node/my-node" as |source|>
|
<DataSource @src="/partition/default/dc-1/sessions/for-node/my-node" as |source|>
|
||||||
<Consul::LockSession::List
|
<Consul::LockSession::List
|
||||||
@items={{source.data}}
|
@items={{source.data}}
|
||||||
@onInvalidate={{action (noop)}}
|
@onInvalidate={{action (noop)}}
|
||||||
|
|
|
@ -8,7 +8,7 @@ A presentational component for presenting Consul Metadata
|
||||||
The following example shows how to construct the required structure from the Consul API using ember-componsable-helpers' `entries` helper.
|
The following example shows how to construct the required structure from the Consul API using ember-componsable-helpers' `entries` helper.
|
||||||
|
|
||||||
```hbs
|
```hbs
|
||||||
<DataSource @src="/default/dc-1/service-instance/service-id/node-0/service-0" as |source|>
|
<DataSource @src="/partition/default/dc-1/service-instance/service-id/node-0/service-0" as |source|>
|
||||||
<Consul::Metadata::List
|
<Consul::Metadata::List
|
||||||
@items={{entries source.data.firstObject.Meta}}
|
@items={{entries source.data.firstObject.Meta}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -4,7 +4,7 @@ class: ember
|
||||||
## Consul::Nspace::List
|
## Consul::Nspace::List
|
||||||
|
|
||||||
```hbs
|
```hbs
|
||||||
<DataSource @src="/default/dc-1/namespaces" as |source|>
|
<DataSource @src="/partition/default/dc-1/namespaces" as |source|>
|
||||||
<Consul::Nspace::List
|
<Consul::Nspace::List
|
||||||
@items={{source.data}}
|
@items={{source.data}}
|
||||||
@ondelete={{action (noop)}}
|
@ondelete={{action (noop)}}
|
||||||
|
|
|
@ -81,7 +81,15 @@ as |key value|}}
|
||||||
{{#each dcs as |dc|}}
|
{{#each dcs as |dc|}}
|
||||||
<Option @value={{dc.Name}} @selected={{contains dc.Name @filter.datacenter.value}}>{{dc.Name}}</Option>
|
<Option @value={{dc.Name}} @selected={{contains dc.Name @filter.datacenter.value}}>{{dc.Name}}</Option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
<DataSource @src="/*/*/datacenters" @loading="lazy" @onchange={{action (mut this.dcs) value="data"}} />
|
<DataSource
|
||||||
|
@src={{uri "/${partition}/*/*/datacenters"
|
||||||
|
(hash
|
||||||
|
partition=@partition
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
@loading="lazy"
|
||||||
|
@onchange={{action (mut this.dcs) value="data"}}
|
||||||
|
/>
|
||||||
{{/let}}
|
{{/let}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</search.Select>
|
</search.Select>
|
||||||
|
|
|
@ -19,7 +19,7 @@ Lastly, a `SearchService` in `services/search.js` configures what is available f
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<figcaption>Get some data to search on</figcaption>
|
<figcaption>Get some data to search on</figcaption>
|
||||||
<DataSource @src="/nspace/dc-1/services" as |source|>
|
<DataSource @src="/partition/nspace/dc-1/services" as |source|>
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<figcaption>and show the complete set of data</figcaption>
|
<figcaption>and show the complete set of data</figcaption>
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<DataLoader
|
<DataLoader
|
||||||
@items={{item}}
|
@items={{item}}
|
||||||
@src={{uri
|
@src={{uri
|
||||||
'/${nspace}/${dc}/${type}/${src}'
|
'/${partition}/${nspace}/${dc}/${type}/${src}'
|
||||||
(hash
|
(hash
|
||||||
|
partition=partition
|
||||||
nspace=nspace
|
nspace=nspace
|
||||||
dc=dc
|
dc=dc
|
||||||
type=type
|
type=type
|
||||||
|
@ -16,8 +17,9 @@
|
||||||
|
|
||||||
<DataWriter
|
<DataWriter
|
||||||
@sink={{uri
|
@sink={{uri
|
||||||
'/${nspace}/${dc}/${type}'
|
'/${partition}/${nspace}/${dc}/${type}'
|
||||||
(hash
|
(hash
|
||||||
|
partition=partition
|
||||||
nspace=nspace
|
nspace=nspace
|
||||||
dc=(or data.Datacenter dc)
|
dc=(or data.Datacenter dc)
|
||||||
type=type
|
type=type
|
||||||
|
|
|
@ -46,7 +46,7 @@ export default Component.extend(Slotted, {
|
||||||
}
|
}
|
||||||
// mark as creating
|
// mark as creating
|
||||||
// and autofill the new record if required
|
// and autofill the new record if required
|
||||||
if (get(changeset, 'isNew')) {
|
if (get(data, 'isNew')) {
|
||||||
set(this, 'create', true);
|
set(this, 'create', true);
|
||||||
changeset = Object.entries(this.autofill || {}).reduce(function(prev, [key, value]) {
|
changeset = Object.entries(this.autofill || {}).reduce(function(prev, [key, value]) {
|
||||||
set(prev, key, value);
|
set(prev, key, value);
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
# DataLoader
|
||||||
|
|
||||||
|
`<DataLoader />` works similarly to, and uses, `<DataSource />` but additionally
|
||||||
|
exposes various common states based on the status of the loading of the data.
|
||||||
|
These states are exposed as slots to enable you to easily render different
|
||||||
|
elements based on the state of the data.
|
||||||
|
|
||||||
|
|
||||||
|
Use the `@dataSource` decorator in your repositories to define URI to async
|
||||||
|
method mapping.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
class SomethingRepository extends Service {
|
||||||
|
@dataSource('/:partition/:nspace/:dc/services')
|
||||||
|
async youCouldCallItAnythingTodoWithGettingServices(params) {
|
||||||
|
console.log(params);
|
||||||
|
// {partition: "partition", nspace: "nspace", dc: "dc"}
|
||||||
|
return getTheThing(params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```hbs preview-template
|
||||||
|
<DataLoader
|
||||||
|
@src="/partition/nspace/dc/services"
|
||||||
|
as |loader|>
|
||||||
|
<BlockSlot @name="loading">
|
||||||
|
Loading...
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="error">
|
||||||
|
Error {{loader.error.status}}
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="disconnected">
|
||||||
|
Whilst we could load the initial data, something happened subsequently that
|
||||||
|
meant we could load longer load updates to the data.
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="loaded">
|
||||||
|
{{#each loader.data as |service|}}
|
||||||
|
{{service.Name}}<br />
|
||||||
|
{{/each}}
|
||||||
|
</BlockSlot>
|
||||||
|
</DataLoader>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Attributes
|
||||||
|
|
||||||
|
| Argument | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `src` | `String` | | The source to subscribe to updates to, this should map to a string based URI |
|
||||||
|
|
||||||
|
## Exports
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `data` | The loaded dataset once any data has been loaded successfully |
|
||||||
|
| `error` | The error thrown if an error is encountered whilst loading data |
|
||||||
|
|
||||||
|
## Slots
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `loading` | Rendered whilst waiting for the initial data to load. |
|
||||||
|
| `error` | If there is an error only whilst waiting for the initial data to load, this slot is rendered. |
|
||||||
|
| `disconnected` | Rendered when the initial data has already loaded, but a subsequent set of loaded data causes an error to be thrown.|
|
||||||
|
| `loaded` | Rendered once the initial data is loaded and on subsequent successful loads of data. |
|
||||||
|
|
||||||
|
## See
|
||||||
|
|
||||||
|
- [DataSource](../data-source/README.mdx)
|
||||||
|
- [Component Source Code](./index.js)
|
||||||
|
- [Template Source Code](./index.hbs)
|
||||||
|
|
||||||
|
---
|
|
@ -2,6 +2,9 @@ export default {
|
||||||
id: 'data-loader',
|
id: 'data-loader',
|
||||||
initial: 'load',
|
initial: 'load',
|
||||||
on: {
|
on: {
|
||||||
|
OPEN: {
|
||||||
|
target: 'load',
|
||||||
|
},
|
||||||
ERROR: {
|
ERROR: {
|
||||||
target: 'disconnected',
|
target: 'disconnected',
|
||||||
},
|
},
|
||||||
|
|
|
@ -54,12 +54,14 @@
|
||||||
{{#yield-slot name="disconnected" params=(block-params (component 'notification' after=(action dispatch "RESET")))}}
|
{{#yield-slot name="disconnected" params=(block-params (component 'notification' after=(action dispatch "RESET")))}}
|
||||||
{{yield api}}
|
{{yield api}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
{{#if (not eq error.status '401')}}
|
||||||
<Notification @sticky={{true}}>
|
<Notification @sticky={{true}}>
|
||||||
<p data-notification role="alert" class="warning notification-update">
|
<p data-notification role="alert" class="warning notification-update">
|
||||||
<strong>Warning!</strong>
|
<strong>Warning!</strong>
|
||||||
An error was returned whilst loading this data, refresh to try again.
|
An error was returned whilst loading this data, refresh to try again.
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</Notification>
|
||||||
|
{{/if}}
|
||||||
{{/yield-slot}}
|
{{/yield-slot}}
|
||||||
</State>
|
</State>
|
||||||
{{#if (eq error.status "403")}}
|
{{#if (eq error.status "403")}}
|
||||||
|
|
|
@ -1,8 +1,22 @@
|
||||||
## DataSource
|
# DataSource
|
||||||
|
|
||||||
|
Use the `@dataSource` decorator in your repositories to define URI to async
|
||||||
|
method mapping.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
class SomethingRepository extends Service {
|
||||||
|
@dataSource('/:partition/:nspace/:dc/services')
|
||||||
|
async youCouldCallItAnythingTodoWithGettingServices(params) {
|
||||||
|
console.log(params);
|
||||||
|
// {partition: "partition", nspace: "nspace", dc: "dc"}
|
||||||
|
return getTheThing(params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
```hbs preview-template
|
```hbs preview-template
|
||||||
<DataSource
|
<DataSource
|
||||||
@src="/nspace/dc/services"
|
@src="/partition/nspace/dc/services"
|
||||||
@loading="eager"
|
@loading="eager"
|
||||||
@disabled={{false}}
|
@disabled={{false}}
|
||||||
as |source|>
|
as |source|>
|
||||||
|
@ -12,7 +26,7 @@ as |source|>
|
||||||
</DataSource>
|
</DataSource>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Arguments
|
## Attributes
|
||||||
|
|
||||||
| Argument | Type | Default | Description |
|
| Argument | Type | Default | Description |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
|
@ -34,14 +48,14 @@ Behind the scenes in the Consul UI we map URIs back to our `ember-data` backed `
|
||||||
`DataSource` is not just restricted to HTTP API data, and can be configured to listen for data changes using a variety of methods and sources. For example we have also configured `DataSource` to listen to `LocalStorage` changes using the `settings://` pseudo-protocol in the URI (See examples below).
|
`DataSource` is not just restricted to HTTP API data, and can be configured to listen for data changes using a variety of methods and sources. For example we have also configured `DataSource` to listen to `LocalStorage` changes using the `settings://` pseudo-protocol in the URI (See examples below).
|
||||||
|
|
||||||
|
|
||||||
### Examples
|
## Examples
|
||||||
|
|
||||||
Straightforward usage can use `mut` to easily update data within a template using an event handler approach.
|
Straightforward usage can use `mut` to easily update data within a template using an event handler approach.
|
||||||
|
|
||||||
```hbs
|
```hbs
|
||||||
{{! listen for HTTP API changes}}
|
{{! listen for HTTP API changes}}
|
||||||
<DataSource
|
<DataSource
|
||||||
@src="/nspace/dc/services"
|
@src="/partition/nspace/dc/services"
|
||||||
@onchange={{action (mut items) value="data"}}
|
@onchange={{action (mut items) value="data"}}
|
||||||
@onerror={{action (mut error) value="error"}}
|
@onerror={{action (mut error) value="error"}}
|
||||||
/>
|
/>
|
||||||
|
@ -71,7 +85,7 @@ A property approach to easily update data within a template
|
||||||
```hbs
|
```hbs
|
||||||
{{! listen for HTTP API changes}}
|
{{! listen for HTTP API changes}}
|
||||||
<DataSource
|
<DataSource
|
||||||
@src="/nspace/dc/services"
|
@src="/partition/nspace/dc/services"
|
||||||
as |source|>
|
as |source|>
|
||||||
{{#if source.error}}
|
{{#if source.error}}
|
||||||
Something went wrong!
|
Something went wrong!
|
||||||
|
@ -101,19 +115,19 @@ DataSources can also be recursively nested for loading in series as opposed to i
|
||||||
|
|
||||||
{{! listen for HTTP API changes}}
|
{{! listen for HTTP API changes}}
|
||||||
<DataSource
|
<DataSource
|
||||||
@src="/nspace/dc/services"
|
@src="/partition/nspace/dc/services"
|
||||||
@onerror={{action (mut error) value="error"}}
|
@onerror={{action (mut error) value="error"}}
|
||||||
as |source|>
|
as |source|>
|
||||||
|
|
||||||
<source.Source
|
<source.Source
|
||||||
@src="/nspace/dc/service/{{source.data.firstObject.Name}}"
|
@src="/partition/nspace/dc/service/{{source.data.firstObject.Name}}"
|
||||||
@onerror={{action (mut error) value="error"}}
|
@onerror={{action (mut error) value="error"}}
|
||||||
as |source|>
|
as |source|>
|
||||||
|
|
||||||
{{source.data.Service.Service.Name}} <== Detailed information for the first service
|
{{source.data.Service.Service.Name}} <== Detailed information for the first service
|
||||||
|
|
||||||
<source.Source
|
<source.Source
|
||||||
@src="/nspace/dc/proxy/for-service/{{source.data.Service.ID}}"
|
@src="/partition/nspace/dc/proxy/for-service/{{source.data.Service.ID}}"
|
||||||
@onerror={{action (mut error) value="error"}}
|
@onerror={{action (mut error) value="error"}}
|
||||||
@onchange={{action (mut loaded) true}}
|
@onchange={{action (mut loaded) true}}
|
||||||
as |source|>
|
as |source|>
|
||||||
|
@ -127,7 +141,7 @@ DataSources can also be recursively nested for loading in series as opposed to i
|
||||||
</DataSource>
|
</DataSource>
|
||||||
```
|
```
|
||||||
|
|
||||||
### See
|
## See
|
||||||
|
|
||||||
- [Component Source Code](./index.js)
|
- [Component Source Code](./index.js)
|
||||||
- [Template Source Code](./index.hbs)
|
- [Template Source Code](./index.hbs)
|
||||||
|
|
|
@ -89,6 +89,14 @@ export default class DataSource extends Component {
|
||||||
|
|
||||||
@action
|
@action
|
||||||
disconnect() {
|
disconnect() {
|
||||||
|
// FIXME? Should we be doing this here
|
||||||
|
if (
|
||||||
|
typeof this.data !== 'undefined' &&
|
||||||
|
typeof this.data.length === 'undefined' &&
|
||||||
|
typeof this.data.rollbackAttributes === 'function'
|
||||||
|
) {
|
||||||
|
this.data.rollbackAttributes();
|
||||||
|
}
|
||||||
this.close();
|
this.close();
|
||||||
this._listeners.remove();
|
this._listeners.remove();
|
||||||
this._lazyListeners.remove();
|
this._lazyListeners.remove();
|
||||||
|
@ -169,6 +177,15 @@ export default class DataSource extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@action
|
||||||
|
async invalidate() {
|
||||||
|
this.source.readyState = 2;
|
||||||
|
this.disconnect();
|
||||||
|
schedule('afterRender', () => {
|
||||||
|
// FIXME: Lazy data-sources
|
||||||
|
this.connect([]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// keep this argumentless
|
// keep this argumentless
|
||||||
@action
|
@action
|
||||||
|
|
|
@ -11,9 +11,7 @@
|
||||||
</:home-nav>
|
</:home-nav>
|
||||||
|
|
||||||
<:main-nav>
|
<:main-nav>
|
||||||
{{#if @dc}}
|
|
||||||
<ul>
|
<ul>
|
||||||
|
|
||||||
<li
|
<li
|
||||||
class="dcs"
|
class="dcs"
|
||||||
data-test-datacenter-menu
|
data-test-datacenter-menu
|
||||||
|
@ -28,7 +26,7 @@
|
||||||
<BlockSlot @name="menu">
|
<BlockSlot @name="menu">
|
||||||
{{#let components.MenuItem components.MenuSeparator as |MenuItem MenuSeparator|}}
|
{{#let components.MenuItem components.MenuSeparator as |MenuItem MenuSeparator|}}
|
||||||
<DataSource
|
<DataSource
|
||||||
@src="/*/*/datacenters"
|
@src="/*/*/*/datacenters"
|
||||||
@onchange={{action (mut @dcs) value="data"}}
|
@onchange={{action (mut @dcs) value="data"}}
|
||||||
@loading="lazy"
|
@loading="lazy"
|
||||||
/>
|
/>
|
||||||
|
@ -51,8 +49,61 @@
|
||||||
</PopoverMenu>
|
</PopoverMenu>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
{{#let (or this.nspaces @nspaces) as |nspaces|}}
|
{{#if (can "choose partitions")}}
|
||||||
{{#if (can "choose nspaces" nspaces=nspaces)}}
|
<li
|
||||||
|
class="partitions"
|
||||||
|
data-test-partition-menu
|
||||||
|
>
|
||||||
|
<PopoverMenu
|
||||||
|
aria-label="Admin Partition"
|
||||||
|
@position="left"
|
||||||
|
as |components api|>
|
||||||
|
<BlockSlot @name="trigger">
|
||||||
|
{{@partition}}
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="menu">
|
||||||
|
{{#let components.MenuItem components.MenuSeparator as |MenuItem MenuSeparator|}}
|
||||||
|
<DataSource
|
||||||
|
@src={{uri
|
||||||
|
'/*/*/${dc}/partitions'
|
||||||
|
(hash
|
||||||
|
dc=@dc.Name
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
@onchange={{action (mut this.partitions) value="data"}}
|
||||||
|
/>
|
||||||
|
{{!FIXME: Do partitions do the same as namespace deletion? }}
|
||||||
|
{{#each (reject-by 'DeletedAt' this.partitions) as |item|}}
|
||||||
|
<MenuItem
|
||||||
|
class={{if (eq @partition item.Name) 'is-active'}}
|
||||||
|
@href={{href-to '.' params=(hash
|
||||||
|
partition=item.Name
|
||||||
|
nspace=(if (gt @nspace.length 0) @nspace undefined)
|
||||||
|
)}}
|
||||||
|
>
|
||||||
|
<BlockSlot @name="label">
|
||||||
|
{{item.Name}}
|
||||||
|
</BlockSlot>
|
||||||
|
</MenuItem>
|
||||||
|
{{/each}}
|
||||||
|
{{#if (and false (can 'manage partitions'))}}
|
||||||
|
<MenuSeparator />
|
||||||
|
<MenuItem
|
||||||
|
data-test-main-nav-partitions
|
||||||
|
@href={{href-to 'dc.nspaces' @dc.Name}}
|
||||||
|
>
|
||||||
|
<BlockSlot @name="label">
|
||||||
|
Manage Admin Partitions
|
||||||
|
</BlockSlot>
|
||||||
|
</MenuItem>
|
||||||
|
{{/if}}
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverMenu>
|
||||||
|
</li>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if (can "choose nspaces")}}
|
||||||
<li
|
<li
|
||||||
class="nspaces"
|
class="nspaces"
|
||||||
data-test-nspace-menu
|
data-test-nspace-menu
|
||||||
|
@ -62,7 +113,7 @@
|
||||||
@position="left"
|
@position="left"
|
||||||
as |components api|>
|
as |components api|>
|
||||||
<BlockSlot @name="trigger">
|
<BlockSlot @name="trigger">
|
||||||
{{@nspace.Name}}
|
{{@nspace}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
{{#if (is-href 'dc.nspaces')}}
|
{{#if (is-href 'dc.nspaces')}}
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
|
@ -74,14 +125,23 @@
|
||||||
<BlockSlot @name="menu">
|
<BlockSlot @name="menu">
|
||||||
{{#let components.MenuItem components.MenuSeparator as |MenuItem MenuSeparator|}}
|
{{#let components.MenuItem components.MenuSeparator as |MenuItem MenuSeparator|}}
|
||||||
<DataSource
|
<DataSource
|
||||||
@src="/*/*/namespaces"
|
@src={{uri
|
||||||
|
'/${partition}/*/${dc}/namespaces'
|
||||||
|
(hash
|
||||||
|
partition=@partition
|
||||||
|
dc=@dc.Name
|
||||||
|
)
|
||||||
|
}}
|
||||||
@onchange={{action (mut this.nspaces) value="data"}}
|
@onchange={{action (mut this.nspaces) value="data"}}
|
||||||
@loading="lazy"
|
@loading="lazy"
|
||||||
/>
|
/>
|
||||||
{{#each (reject-by 'DeletedAt' nspaces) as |item|}}
|
{{#each (reject-by 'DeletedAt' this.nspaces) as |item|}}
|
||||||
<MenuItem
|
<MenuItem
|
||||||
class={{if (eq @nspace.Name item.Name) 'is-active'}}
|
class={{if (eq @nspace item.Name) 'is-active'}}
|
||||||
@href={{href-to '.' params=(hash nspace=item.Name)}}
|
@href={{href-to '.' params=(hash
|
||||||
|
partition=(if (gt @partition.length 0) @partition undefined)
|
||||||
|
nspace=item.Name
|
||||||
|
)}}
|
||||||
>
|
>
|
||||||
<BlockSlot @name="label">
|
<BlockSlot @name="label">
|
||||||
{{item.Name}}
|
{{item.Name}}
|
||||||
|
@ -104,7 +164,6 @@
|
||||||
</PopoverMenu>
|
</PopoverMenu>
|
||||||
</li>
|
</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/let}}
|
|
||||||
{{#if (can "read services")}}
|
{{#if (can "read services")}}
|
||||||
<li data-test-main-nav-services class={{if (is-href 'dc.services' @dc.Name) 'is-active'}}>
|
<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>
|
<a href={{href-to 'dc.services' @dc.Name}}>Services</a>
|
||||||
|
@ -158,8 +217,6 @@
|
||||||
</li>
|
</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</ul>
|
</ul>
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
</:main-nav>
|
</:main-nav>
|
||||||
|
|
||||||
<:complementary-nav>
|
<:complementary-nav>
|
||||||
|
@ -207,13 +264,17 @@
|
||||||
</PopoverMenu>
|
</PopoverMenu>
|
||||||
</li>
|
</li>
|
||||||
<li data-test-main-nav-settings class={{if (is-href 'settings') 'is-active'}}>
|
<li data-test-main-nav-settings class={{if (is-href 'settings') 'is-active'}}>
|
||||||
<a href={{href-to 'settings'}}>Settings</a>
|
<a href={{href-to 'settings' params=(hash
|
||||||
|
nspace=undefined
|
||||||
|
partition=undefined
|
||||||
|
)}}>Settings</a>
|
||||||
</li>
|
</li>
|
||||||
{{#if (can 'authenticate')}}
|
{{#if (can 'authenticate')}}
|
||||||
<li data-test-main-nav-auth>
|
<li data-test-main-nav-auth>
|
||||||
<AuthDialog
|
<AuthDialog
|
||||||
@dc={{@dc.Name}}
|
@dc={{@dc.Name}}
|
||||||
@nspace={{@nspace.Name}}
|
@nspace={{@nspace}}
|
||||||
|
@partition={{@partition}}
|
||||||
@onchange={{this.reauthorize}} as |authDialog components|
|
@onchange={{this.reauthorize}} as |authDialog components|
|
||||||
>
|
>
|
||||||
{{#let components.AuthForm components.AuthProfile as |AuthForm AuthProfile|}}
|
{{#let components.AuthForm components.AuthProfile as |AuthForm AuthProfile|}}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
%main-nav-vertical:not(.in-viewport) {
|
%main-nav-vertical:not(.in-viewport) {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
%main-nav-vertical li.partitions,
|
||||||
%main-nav-vertical li.nspaces,
|
%main-nav-vertical li.nspaces,
|
||||||
%main-nav-vertical li.dcs {
|
%main-nav-vertical li.dcs {
|
||||||
margin-bottom: 25px;
|
margin-bottom: 25px;
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
{{#each templates as |template|}}
|
{{#each templates as |template|}}
|
||||||
<label data-test-radiobutton={{concat 'template_' template.template}}>
|
<label data-test-radiobutton={{concat 'template_' template.template}}>
|
||||||
<span>{{template.name}}</span>
|
<span>{{template.name}}</span>
|
||||||
<input type="radio" name={{concat name '[template]'}} value={{template.template}} checked={{eq item.template template.template}} onchange={{action (changeset-set item 'template') value='target.value'}}/>
|
<input type="radio" name={{concat name '[template]'}} value={{template.template}} checked={{eq item.template template.template}} onchange={{action (optional (changeset-set item 'template')) value='target.value'}}/>
|
||||||
</label>
|
</label>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -72,7 +72,8 @@
|
||||||
</label>
|
</label>
|
||||||
{{#if (eq item.template 'node-identity')}}
|
{{#if (eq item.template 'node-identity')}}
|
||||||
|
|
||||||
<DataSource @src="/*/*/datacenters"
|
<DataSource
|
||||||
|
@src={{uri '/*/*/*/datacenters'}}
|
||||||
@onchange={{action (mut datacenters) value="data"}}
|
@onchange={{action (mut datacenters) value="data"}}
|
||||||
/>
|
/>
|
||||||
<label class="type-select" data-test-datacenter>
|
<label class="type-select" data-test-datacenter>
|
||||||
|
@ -95,7 +96,8 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{{#if isScoped }}
|
{{#if isScoped }}
|
||||||
<DataSource @src="/*/*/datacenters"
|
<DataSource
|
||||||
|
@src={{uri '/*/*/*/datacenters'}}
|
||||||
@onchange={{action (mut datacenters) value="data"}}
|
@onchange={{action (mut datacenters) value="data"}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
@disabled={{disabled}}
|
@disabled={{disabled}}
|
||||||
@repo={{repo}}
|
@repo={{repo}}
|
||||||
@dc={{dc}}
|
@dc={{dc}}
|
||||||
|
@partition={{partition}}
|
||||||
@nspace={{nspace}}
|
@nspace={{nspace}}
|
||||||
@type="policy"
|
@type="policy"
|
||||||
@placeholder="Search for policy"
|
@placeholder="Search for policy"
|
||||||
|
@ -38,7 +39,13 @@
|
||||||
<h2>New Policy</h2>
|
<h2>New Policy</h2>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="body">
|
<BlockSlot @name="body">
|
||||||
<PolicyForm @form={{form}} @nspace={{nspace}} @dc={{dc}} @allowServiceIdentity={{allowServiceIdentity}} />
|
<PolicyForm
|
||||||
|
@form={{form}}
|
||||||
|
@nspace={{nspace}}
|
||||||
|
@partition={{partition}}
|
||||||
|
@dc={{dc}}
|
||||||
|
@allowServiceIdentity={{allowServiceIdentity}}
|
||||||
|
/>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="actions" as |close|>
|
<BlockSlot @name="actions" as |close|>
|
||||||
<button type="submit"
|
<button type="submit"
|
||||||
|
@ -79,7 +86,14 @@
|
||||||
<BlockSlot @name="details">
|
<BlockSlot @name="details">
|
||||||
{{#if (eq item.template '')}}
|
{{#if (eq item.template '')}}
|
||||||
<DataSource
|
<DataSource
|
||||||
@src={{concat '/' item.Namespace '/' dc '/policy/' item.ID}}
|
@src={{uri '/${partition}/${nspace}/${dc}/policy/${id}'
|
||||||
|
(hash
|
||||||
|
partition=item.Partition
|
||||||
|
nspace=item.Namespace
|
||||||
|
dc=dc
|
||||||
|
id=item.ID
|
||||||
|
)
|
||||||
|
}}
|
||||||
@onchange={{action (mut loadedItem) value="data"}}
|
@onchange={{action (mut loadedItem) value="data"}}
|
||||||
@loading="lazy"
|
@loading="lazy"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
<PolicySelector
|
<PolicySelector
|
||||||
@disabled={{not (can "write role" item=item)}}
|
@disabled={{not (can "write role" item=item)}}
|
||||||
@dc={{dc}}
|
@dc={{dc}}
|
||||||
|
@partition={{partition}}
|
||||||
@nspace={{nspace}}
|
@nspace={{nspace}}
|
||||||
@items={{item.Policies}}
|
@items={{item.Policies}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -18,10 +18,21 @@ as |modal|>
|
||||||
<BlockSlot @name="body">
|
<BlockSlot @name="body">
|
||||||
|
|
||||||
<input id="{{name}}_state_role" type="radio" name="{{name}}[state]" value="role" checked={{if (eq state 'role') 'checked'}} onchange={{action 'change'}} />
|
<input id="{{name}}_state_role" type="radio" name="{{name}}[state]" value="role" checked={{if (eq state 'role') 'checked'}} onchange={{action 'change'}} />
|
||||||
<RoleForm @form={{form}} @dc={{dc}} @nspace={{nspace}}>
|
<RoleForm
|
||||||
|
@form={{form}}
|
||||||
|
@dc={{dc}}
|
||||||
|
@nspace={{nspace}}
|
||||||
|
@partition={{partition}}
|
||||||
|
>
|
||||||
<BlockSlot @name="policy">
|
<BlockSlot @name="policy">
|
||||||
|
|
||||||
<PolicySelector @source={{source}} @dc={{dc}} @nspace={{nspace}} @items={{item.Policies}}>
|
<PolicySelector
|
||||||
|
@source={{source}}
|
||||||
|
@dc={{dc}}
|
||||||
|
@partition={{partition}}
|
||||||
|
@nspace={{nspace}}
|
||||||
|
@items={{item.Policies}}
|
||||||
|
>
|
||||||
<BlockSlot @name="trigger">
|
<BlockSlot @name="trigger">
|
||||||
<label for="{{name}}_state_policy" data-test-create-policy class="type-dialog">
|
<label for="{{name}}_state_policy" data-test-create-policy class="type-dialog">
|
||||||
<span>Create new policy</span>
|
<span>Create new policy</span>
|
||||||
|
@ -33,7 +44,14 @@ as |modal|>
|
||||||
</RoleForm>
|
</RoleForm>
|
||||||
|
|
||||||
<input id="{{name}}_state_policy" type="radio" name="{{name}}[state]" value="policy" checked={{if (eq state 'policy') 'checked'}} onchange={{action 'change'}} />
|
<input id="{{name}}_state_policy" type="radio" name="{{name}}[state]" value="policy" checked={{if (eq state 'policy') 'checked'}} onchange={{action 'change'}} />
|
||||||
<PolicyForm data-test-policy-form @name="role[policy]" @form={{policyForm}} @dc={{dc}} />
|
<PolicyForm
|
||||||
|
data-test-policy-form
|
||||||
|
@name="role[policy]"
|
||||||
|
@form={{policyForm}}
|
||||||
|
@dc={{dc}}
|
||||||
|
@nspace={{nspace}}
|
||||||
|
@partition={{partition}}
|
||||||
|
/>
|
||||||
|
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="actions" as |close|>
|
<BlockSlot @name="actions" as |close|>
|
||||||
|
@ -62,7 +80,16 @@ as |modal|>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</ModalDialog>
|
</ModalDialog>
|
||||||
|
|
||||||
<ChildSelector @disabled={{disabled}} @repo={{repo}} @dc={{dc}} @nspace={{nspace}} @type="role" @placeholder="Search for role" @items={{items}}>
|
<ChildSelector
|
||||||
|
@disabled={{disabled}}
|
||||||
|
@repo={{repo}}
|
||||||
|
@dc={{dc}}
|
||||||
|
@partition={{partition}}
|
||||||
|
@nspace={{nspace}}
|
||||||
|
@type="role"
|
||||||
|
@placeholder="Search for role"
|
||||||
|
@items={{items}}
|
||||||
|
>
|
||||||
<BlockSlot @name="label">
|
<BlockSlot @name="label">
|
||||||
Apply an existing role
|
Apply an existing role
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
|
@ -12,8 +12,8 @@ routes.
|
||||||
| Argument | Type | Default | Description |
|
| Argument | Type | Default | Description |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `name` | `String` | `undefined` | The name of the route in ember routeName format e.g. `dc.services.index`. This is generally the `routeName` variable that is available to you in all Consul UI route/page level templates.|
|
| `name` | `String` | `undefined` | The name of the route in ember routeName format e.g. `dc.services.index`. This is generally the `routeName` variable that is available to you in all Consul UI route/page level templates.|
|
||||||
| `title` | `String` | `undefined` | The title for this page (eventually passed through to the `{{page-title}}` helper. This argument should be omitted if a title change isn't required. |
|
| `title` | `String` | `undefined` | Deprecated: The title for this page (eventually passed through to the `{{page-title}}` helper. This argument should be omitted if a title change isn't required. Also see the exported `<Title />` component which is now preferred |
|
||||||
| `titleSeparator` | `String` | `undefined` | This can be used in the top-level route to configure the separator for the `{{page-title}}` helper |
|
| `titleSeparator` | `String` | `undefined` | Deprecated: This can be used in the top-level route to configure the separator for the `{{page-title}}` helper. Also see the exported `<Title />` component which is now preferred |
|
||||||
|
|
||||||
## Exports
|
## Exports
|
||||||
|
|
||||||
|
@ -21,16 +21,28 @@ routes.
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `model` | `Object` | `undefined` | Arbitrary hash of data passed down from the parent route/outlet |
|
| `model` | `Object` | `undefined` | Arbitrary hash of data passed down from the parent route/outlet |
|
||||||
| `params` | `Object` | `undefined` | An object/merge of **all** optional route params and normal route params |
|
| `params` | `Object` | `undefined` | An object/merge of **all** optional route params and normal route params |
|
||||||
|
| `Title` | `Component` | `` | An inline component to allow you to set a title within the Route component |
|
||||||
|
| `Announcer` | `Component` | `` | An inline component to allow you to specify where the route announcer is rendered. This should be at the very top of your app probably under your `<Route />` in `application.hbs` |
|
||||||
|
|
||||||
```hbs
|
```hbs
|
||||||
|
<!-- application.hbs -->
|
||||||
<Route
|
<Route
|
||||||
@name={{routeName}}
|
@name={{routeName}}
|
||||||
@title="Page Title"
|
|
||||||
@titleSeparator=" - "
|
|
||||||
as |route|>
|
as |route|>
|
||||||
|
<route.Announcer />
|
||||||
|
...
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
<!-- All route templates that change the title -->
|
||||||
|
<Route
|
||||||
|
@name={{routeName}}
|
||||||
|
as |route|>
|
||||||
|
<h1><route.Title @title="Page Title" /></h1>
|
||||||
{{route.model.dc.Name}}
|
{{route.model.dc.Name}}
|
||||||
</Route>
|
</Route>
|
||||||
```
|
```
|
||||||
|
|
||||||
Every page/route template has a `routeName` variable exposed specifically to
|
Every page/route template has a `routeName` variable exposed specifically to
|
||||||
allow you to use this to set the `@name` of the route.
|
allow you to use this to set the `@name` of the route.
|
||||||
|
|
||||||
|
The `<Title @title=""/>` component should be used to control the title of the page. This component also yields the value of the `@title` attribute allowing you to use it to avoid repeating the title of the page for things like reusing the same value for a `<h1>`. The `Title` component is one of the only components which uses an attribute to specify textual copy, this makes it hard to add further HTML elements to the value of `@title` which would not be supported in the HTML `<title>` element.
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
{{page-title @title separator=(or @separator ' - ')}}
|
||||||
|
<PortalTarget @name="route-announcer" />
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
{{did-insert this.connect}}
|
{{did-insert this.connect}}
|
||||||
{{will-destroy this.disconnect}}
|
{{will-destroy this.disconnect}}
|
||||||
|
|
||||||
{{#if this.title}}
|
|
||||||
{{page-title this.title separator=@titleSeparator}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{yield (hash
|
{{yield (hash
|
||||||
model=this.model
|
model=this.model
|
||||||
params=this.params
|
params=this.params
|
||||||
|
currentName=this.router.currentRoute.name
|
||||||
|
|
||||||
|
refresh=this.refresh
|
||||||
|
|
||||||
|
Title=(component "route/title")
|
||||||
|
Announcer=(component "route/announcer")
|
||||||
)}}
|
)}}
|
|
@ -5,13 +5,10 @@ import { tracked } from '@glimmer/tracking';
|
||||||
|
|
||||||
export default class RouteComponent extends Component {
|
export default class RouteComponent extends Component {
|
||||||
@service('routlet') routlet;
|
@service('routlet') routlet;
|
||||||
|
@service('router') router;
|
||||||
|
|
||||||
@tracked model;
|
@tracked model;
|
||||||
|
|
||||||
get title() {
|
|
||||||
return this.args.title;
|
|
||||||
}
|
|
||||||
|
|
||||||
get params() {
|
get params() {
|
||||||
return this.routlet.paramsFor(this.args.name);
|
return this.routlet.paramsFor(this.args.name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
{{page-title @title separator=@separator}}
|
||||||
|
{{#if (not-eq @render false)}}
|
||||||
|
{{@title}}
|
||||||
|
{{/if}}
|
||||||
|
<Portal @target="route-announcer">
|
||||||
|
<div
|
||||||
|
class="route-title"
|
||||||
|
...attributes
|
||||||
|
aria-live="assertive"
|
||||||
|
aria-atomic="true"
|
||||||
|
>
|
||||||
|
{{! Using a handlebars concat here avoid whitespace issues}}
|
||||||
|
{{concat 'Navigated to ' @title}}
|
||||||
|
</div>
|
||||||
|
</Portal>
|
|
@ -0,0 +1,6 @@
|
||||||
|
%route-title {
|
||||||
|
@extend %visually-hidden;
|
||||||
|
}
|
||||||
|
.route-title {
|
||||||
|
@extend %route-title;
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ class: ember
|
||||||
<TokenSource
|
<TokenSource
|
||||||
@dc={{dc}}
|
@dc={{dc}}
|
||||||
@nspace={{nspace}}
|
@nspace={{nspace}}
|
||||||
|
@partition={{partition}}
|
||||||
@type={{or 'oidc' 'secret'}}
|
@type={{or 'oidc' 'secret'}}
|
||||||
@value={{or identifierForProvider secret}}
|
@value={{or identifierForProvider secret}}
|
||||||
@onchange={{action 'change'}}
|
@onchange={{action 'change'}}
|
||||||
|
@ -20,6 +21,7 @@ class: ember
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `dc` | `String` | | The name of the current datacenter |
|
| `dc` | `String` | | The name of the current datacenter |
|
||||||
| `nspace` | `String` | | The name of the current namespace |
|
| `nspace` | `String` | | The name of the current namespace |
|
||||||
|
| `partition` | `String` | | The name of the current partition |
|
||||||
| `onchange` | `Function` | | The action to fire when the data changes. Emits an Event-like object with a `data` property containing the jwt data, in this case the autorizationCode and the status |
|
| `onchange` | `Function` | | The action to fire when the data changes. Emits an Event-like object with a `data` property containing the jwt data, in this case the autorizationCode and the status |
|
||||||
| `onerror` | `Function` | | The action to fire when an error occurs. Emits ErrorEvent object with an `error` property containing the Error. |
|
| `onerror` | `Function` | | The action to fire when an error occurs. Emits ErrorEvent object with an `error` property containing the Error. |
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<StateChart @src={{chart}} @initial={{if (eq type 'oidc') 'provider' 'secret'}} as |State Guard Action dispatch state|>
|
<StateChart @src={{chart}} @initial={{if (eq type 'oidc') 'provider' 'secret'}} as |State Guard Action dispatch state|>
|
||||||
<Guard @name="isSecret" @cond={{action 'isSecret'}} />
|
<Guard @name="isSecret" @cond={{action 'isSecret'}} />
|
||||||
{{!-- This `or` can be completely removed post 1.10 as 1.10 has optional params with default values --}}
|
{{!-- This `or` can be completely removed post 1.10 as 1.10 has optional params with default values --}}
|
||||||
{{#let (concat '/' (or nspace '') '/' dc) as |path|}}
|
{{#let (concat '/' (or partition '') '/' (or nspace '') '/' dc) as |path|}}
|
||||||
<State @matches="secret">
|
<State @matches="secret">
|
||||||
<DataSource
|
<DataSource
|
||||||
@src={{concat path '/token/self/' value}}
|
@src={{concat path '/token/self/' value}}
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
<TopologyMetrics::Stats
|
<TopologyMetrics::Stats
|
||||||
data-test-topology-metrics-downstream-stats
|
data-test-topology-metrics-downstream-stats
|
||||||
@nspace={{or item.Namespace 'default'}}
|
@nspace={{or item.Namespace 'default'}}
|
||||||
|
@partition={{or item.Partition 'default'}}
|
||||||
@dc={{item.Datacenter}}
|
@dc={{item.Datacenter}}
|
||||||
@endpoint='downstream-summary-for-service'
|
@endpoint='downstream-summary-for-service'
|
||||||
@service={{@service.Service.Service}}
|
@service={{@service.Service.Service}}
|
||||||
|
@ -48,6 +49,7 @@
|
||||||
{{#if @hasMetricsProvider}}
|
{{#if @hasMetricsProvider}}
|
||||||
<TopologyMetrics::Series
|
<TopologyMetrics::Series
|
||||||
@nspace={{or @service.Service.Namespace 'default'}}
|
@nspace={{or @service.Service.Namespace 'default'}}
|
||||||
|
@partition={{or service.Service.Partition 'default'}}
|
||||||
@dc={{@dc}}
|
@dc={{@dc}}
|
||||||
@service={{@service.Service.Service}}
|
@service={{@service.Service.Service}}
|
||||||
@protocol={{@topology.Protocol}}
|
@protocol={{@topology.Protocol}}
|
||||||
|
@ -56,6 +58,7 @@
|
||||||
{{#if this.mainNotIngressService}}
|
{{#if this.mainNotIngressService}}
|
||||||
<TopologyMetrics::Stats
|
<TopologyMetrics::Stats
|
||||||
@nspace={{or @service.Service.Namespace 'default'}}
|
@nspace={{or @service.Service.Namespace 'default'}}
|
||||||
|
@partition={{or service.Service.Partition 'default'}}
|
||||||
@dc={{@dc}}
|
@dc={{@dc}}
|
||||||
@endpoint='summary-for-service'
|
@endpoint='summary-for-service'
|
||||||
@service={{@service.Service.Service}}
|
@service={{@service.Service.Service}}
|
||||||
|
@ -104,6 +107,7 @@
|
||||||
{{!-- One of the only places in the app where it's acceptable to default to 'default' namespace --}}
|
{{!-- One of the only places in the app where it's acceptable to default to 'default' namespace --}}
|
||||||
<TopologyMetrics::Stats
|
<TopologyMetrics::Stats
|
||||||
@nspace={{or item.Namespace 'default'}}
|
@nspace={{or item.Namespace 'default'}}
|
||||||
|
@partition={{or item.Partition 'default'}}
|
||||||
@dc={{item.Datacenter}}
|
@dc={{item.Datacenter}}
|
||||||
@endpoint='upstream-summary-for-service'
|
@endpoint='upstream-summary-for-service'
|
||||||
@service={{@service.Service.Service}}
|
@service={{@service.Service.Service}}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
{{#if (not @noMetricsReason)}}
|
{{#if (not @noMetricsReason)}}
|
||||||
<DataSource
|
<DataSource
|
||||||
@src={{uri
|
@src={{uri '/${partition}/${nspace}/${dc}/metrics/summary-for-service/${service}/${protocol}'
|
||||||
'/${nspace}/${dc}/metrics/summary-for-service/${service}/${protocol}'
|
|
||||||
(hash
|
(hash
|
||||||
nspace=@nspace
|
nspace=@nspace
|
||||||
|
partition=@partition
|
||||||
dc=@dc
|
dc=@dc
|
||||||
service=@service
|
service=@service
|
||||||
protocol=@protocol
|
protocol=@protocol
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
{{#if (not @noMetricsReason)}}
|
{{#if (not @noMetricsReason)}}
|
||||||
<DataSource
|
<DataSource
|
||||||
@src={{uri
|
@src={{uri
|
||||||
'/${nspace}/${dc}/metrics/${endpoint}/${service}/${protocol}'
|
'/${partition}/${nspace}/${dc}/metrics/${endpoint}/${service}/${protocol}'
|
||||||
(hash
|
(hash
|
||||||
nspace=@nspace
|
nspace=@nspace
|
||||||
|
partition=@partition
|
||||||
dc=@dc
|
dc=@dc
|
||||||
endpoint=@endpoint
|
endpoint=@endpoint
|
||||||
service=@service
|
service=@service
|
||||||
|
|
|
@ -5,14 +5,9 @@ import { get, action } from '@ember/object';
|
||||||
import transitionable from 'consul-ui/utils/routing/transitionable';
|
import transitionable from 'consul-ui/utils/routing/transitionable';
|
||||||
|
|
||||||
export default class ApplicationController extends Controller {
|
export default class ApplicationController extends Controller {
|
||||||
@service('router')
|
@service('router') router;
|
||||||
router;
|
@service('store') store;
|
||||||
|
@service('feedback') feedback;
|
||||||
@service('store')
|
|
||||||
store;
|
|
||||||
|
|
||||||
@service('feedback')
|
|
||||||
feedback;
|
|
||||||
|
|
||||||
// TODO: We currently do this in the controller instead of the router
|
// TODO: We currently do this in the controller instead of the router
|
||||||
// as the nspace and dc variables aren't available directly on the Route
|
// as the nspace and dc variables aren't available directly on the Route
|
||||||
|
@ -29,7 +24,7 @@ export default class ApplicationController extends Controller {
|
||||||
// TODO: Currently we clear cache from the ember-data store
|
// TODO: Currently we clear cache from the ember-data store
|
||||||
// ideally this would be a static method of the abstract Repository class
|
// ideally this would be a static method of the abstract Repository class
|
||||||
// once we move to proper classes for services take another look at this.
|
// once we move to proper classes for services take another look at this.
|
||||||
this.store.clear();
|
this.store.invalidate();
|
||||||
//
|
//
|
||||||
const params = {};
|
const params = {};
|
||||||
if (e.data) {
|
if (e.data) {
|
||||||
|
|
|
@ -7,25 +7,11 @@ export default Controller.extend({
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
this.form = this.builder.form('nspace');
|
this.form = this.builder.form('nspace');
|
||||||
},
|
},
|
||||||
setProperties: function(model) {
|
|
||||||
// essentially this replaces the data with changesets
|
|
||||||
this._super(
|
|
||||||
Object.keys(model).reduce((prev, key, i) => {
|
|
||||||
switch (key) {
|
|
||||||
case 'item':
|
|
||||||
prev[key] = this.form.setData(prev[key]).getData();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return prev;
|
|
||||||
}, model)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
actions: {
|
actions: {
|
||||||
change: function(e, value, item) {
|
change: function(e, value, item) {
|
||||||
const event = this.dom.normalizeEvent(e, value);
|
const event = this.dom.normalizeEvent(e, value);
|
||||||
const form = this.form;
|
|
||||||
try {
|
try {
|
||||||
form.handleEvent(event);
|
this.form.handleEvent(event);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const target = event.target;
|
const target = event.target;
|
||||||
switch (target.name) {
|
switch (target.name) {
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
import Controller from '@ember/controller';
|
|
||||||
import { get, set } from '@ember/object';
|
|
||||||
import { inject as service } from '@ember/service';
|
|
||||||
|
|
||||||
export default Controller.extend({
|
|
||||||
dom: service('dom'),
|
|
||||||
actions: {
|
|
||||||
change: function(e, value, item) {
|
|
||||||
const event = this.dom.normalizeEvent(e, value);
|
|
||||||
// TODO: Switch to using forms like the rest of the app
|
|
||||||
// setting utils/form/builder for things to be done before we
|
|
||||||
// can do that. For the moment just do things normally its a simple
|
|
||||||
// enough form at the moment
|
|
||||||
|
|
||||||
const target = event.target;
|
|
||||||
const blocking = get(this, 'item.client.blocking');
|
|
||||||
switch (target.name) {
|
|
||||||
case 'client[blocking]':
|
|
||||||
set(this, 'item.client.blocking', !blocking);
|
|
||||||
this.send('update', 'client', this.item.client);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import Route from 'consul-ui/routing/route';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'routing',
|
||||||
|
initialize(application) {
|
||||||
|
application.register('route:basic', Route);
|
||||||
|
},
|
||||||
|
};
|
|
@ -13,7 +13,7 @@ export default {
|
||||||
let repositories = container
|
let repositories = container
|
||||||
.get('container-debug-adapter:main')
|
.get('container-debug-adapter:main')
|
||||||
.catalogEntriesByType('service')
|
.catalogEntriesByType('service')
|
||||||
.filter(item => item.startsWith('repository/'));
|
.filter(item => item.startsWith('repository/') || item === 'ui-config');
|
||||||
|
|
||||||
// during testing we get -test files in here, filter those out but only in debug envs
|
// during testing we get -test files in here, filter those out but only in debug envs
|
||||||
runInDebug(() => (repositories = repositories.filter(item => !item.endsWith('-test'))));
|
runInDebug(() => (repositories = repositories.filter(item => !item.endsWith('-test'))));
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
export function initialize(container) {
|
|
||||||
const env = container.lookup('service:env');
|
|
||||||
if (env.var('CONSUL_NSPACES_ENABLED')) {
|
|
||||||
// enable the nspace repo
|
|
||||||
['dc', 'settings', 'dc.intentions.edit', 'dc.intentions.create'].forEach(function(item) {
|
|
||||||
container.inject(`route:${item}`, 'nspacesRepo', 'service:repository/nspace/enabled');
|
|
||||||
container.inject(`route:nspace.${item}`, 'nspacesRepo', 'service:repository/nspace/enabled');
|
|
||||||
});
|
|
||||||
container.inject('route:application', 'nspacesRepo', 'service:repository/nspace/enabled');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
initialize,
|
|
||||||
};
|
|
|
@ -165,7 +165,7 @@ export default class FSMWithOptionalLocation {
|
||||||
|
|
||||||
optionalParams() {
|
optionalParams() {
|
||||||
let optional = this.optional || {};
|
let optional = this.optional || {};
|
||||||
return Object.keys(OPTIONAL).reduce((prev, item) => {
|
return ['partition', 'nspace'].reduce((prev, item) => {
|
||||||
let value = '';
|
let value = '';
|
||||||
if (typeof optional[item] !== 'undefined') {
|
if (typeof optional[item] !== 'undefined') {
|
||||||
value = optional[item].match;
|
value = optional[item].match;
|
||||||
|
@ -263,7 +263,7 @@ export default class FSMWithOptionalLocation {
|
||||||
optional = undefined;
|
optional = undefined;
|
||||||
}
|
}
|
||||||
optional = Object.values(optional || this.optional || {});
|
optional = Object.values(optional || this.optional || {});
|
||||||
optional = optional.map(item => item.value || item, []);
|
optional = optional.filter(item => Boolean(item)).map(item => item.value || item, []);
|
||||||
temp.splice(...[1, 0].concat(optional));
|
temp.splice(...[1, 0].concat(optional));
|
||||||
url = temp.join('/');
|
url = temp.join('/');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
import Mixin from '@ember/object/mixin';
|
|
||||||
import { get } from '@ember/object';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for create-type Routes
|
|
||||||
*
|
|
||||||
* 'repo' is standardized across the app
|
|
||||||
* 'item' is standardized across the app
|
|
||||||
* they could be replaced with `getRepo` and `getItem`
|
|
||||||
*/
|
|
||||||
export default Mixin.create({
|
|
||||||
beforeModel: function() {
|
|
||||||
this._super(...arguments);
|
|
||||||
this.repo.invalidate();
|
|
||||||
},
|
|
||||||
deactivate: function() {
|
|
||||||
this._super(...arguments);
|
|
||||||
// TODO: This is dependent on ember-changeset
|
|
||||||
// Change changeset to support ember-data props
|
|
||||||
const item = get(this.controller, 'item.data');
|
|
||||||
// TODO: Look and see if rollbackAttributes is good here
|
|
||||||
if (get(item, 'isNew')) {
|
|
||||||
item.destroyRecord();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,4 +0,0 @@
|
||||||
import Mixin from '@ember/object/mixin';
|
|
||||||
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
|
||||||
|
|
||||||
export default Mixin.create(WithBlockingActions, {});
|
|
|
@ -1,4 +0,0 @@
|
||||||
import Mixin from '@ember/object/mixin';
|
|
||||||
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
|
||||||
|
|
||||||
export default Mixin.create(WithBlockingActions, {});
|
|
|
@ -1,4 +0,0 @@
|
||||||
import Mixin from '@ember/object/mixin';
|
|
||||||
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
|
||||||
|
|
||||||
export default Mixin.create(WithBlockingActions, {});
|
|
|
@ -1,48 +0,0 @@
|
||||||
import Mixin from '@ember/object/mixin';
|
|
||||||
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
|
||||||
import { get } from '@ember/object';
|
|
||||||
import { inject as service } from '@ember/service';
|
|
||||||
|
|
||||||
export default Mixin.create(WithBlockingActions, {
|
|
||||||
settings: service('settings'),
|
|
||||||
actions: {
|
|
||||||
use: function(item) {
|
|
||||||
return this.repo
|
|
||||||
.findBySlug({
|
|
||||||
dc: this.modelFor('dc').dc.Name,
|
|
||||||
ns: get(item, 'Namespace'),
|
|
||||||
id: get(item, 'AccessorID'),
|
|
||||||
})
|
|
||||||
.then(item => {
|
|
||||||
return this.settings.persist({
|
|
||||||
token: {
|
|
||||||
AccessorID: get(item, 'AccessorID'),
|
|
||||||
SecretID: get(item, 'SecretID'),
|
|
||||||
Namespace: get(item, 'Namespace'),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
logout: function(item) {
|
|
||||||
return this.settings.delete('token');
|
|
||||||
},
|
|
||||||
clone: function(item) {
|
|
||||||
let cloned;
|
|
||||||
return this.feedback.execute(() => {
|
|
||||||
return this.repo
|
|
||||||
.clone(item)
|
|
||||||
.then(item => {
|
|
||||||
cloned = item;
|
|
||||||
// cloning is similar to delete in that
|
|
||||||
// if you clone from the listing page, stay on the listing page
|
|
||||||
// whereas if you clone from another token, take me back to the listing page
|
|
||||||
// so I can see it
|
|
||||||
return this.afterDelete(...arguments);
|
|
||||||
})
|
|
||||||
.then(function() {
|
|
||||||
return cloned;
|
|
||||||
});
|
|
||||||
}, 'clone');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Mixin from '@ember/object/mixin';
|
import Mixin from '@ember/object/mixin';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { set } from '@ember/object';
|
import { set, get } from '@ember/object';
|
||||||
/** With Blocking Actions
|
/** With Blocking Actions
|
||||||
* This mixin contains common write actions (Create Update Delete) for routes.
|
* This mixin contains common write actions (Create Update Delete) for routes.
|
||||||
* It could also be an Route to extend but decoration seems to be more sense right now.
|
* It could also be an Route to extend but decoration seems to be more sense right now.
|
||||||
|
@ -18,6 +18,7 @@ import { set } from '@ember/object';
|
||||||
*/
|
*/
|
||||||
export default Mixin.create({
|
export default Mixin.create({
|
||||||
_feedback: service('feedback'),
|
_feedback: service('feedback'),
|
||||||
|
settings: service('settings'),
|
||||||
init: function() {
|
init: function() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
const feedback = this._feedback;
|
const feedback = this._feedback;
|
||||||
|
@ -107,5 +108,45 @@ export default Mixin.create({
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
use: function(item) {
|
||||||
|
return this.repo
|
||||||
|
.findBySlug({
|
||||||
|
dc: get(item, 'Datacenter'),
|
||||||
|
ns: get(item, 'Namespace'),
|
||||||
|
partition: get(item, 'Partition'),
|
||||||
|
id: get(item, 'AccessorID'),
|
||||||
|
})
|
||||||
|
.then(item => {
|
||||||
|
return this.settings.persist({
|
||||||
|
token: {
|
||||||
|
AccessorID: get(item, 'AccessorID'),
|
||||||
|
SecretID: get(item, 'SecretID'),
|
||||||
|
Namespace: get(item, 'Namespace'),
|
||||||
|
Partition: get(item, 'Partition'),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
logout: function(item) {
|
||||||
|
return this.settings.delete('token');
|
||||||
|
},
|
||||||
|
clone: function(item) {
|
||||||
|
let cloned;
|
||||||
|
return this.feedback.execute(() => {
|
||||||
|
return this.repo
|
||||||
|
.clone(item)
|
||||||
|
.then(item => {
|
||||||
|
cloned = item;
|
||||||
|
// cloning is similar to delete in that
|
||||||
|
// if you clone from the listing page, stay on the listing page
|
||||||
|
// whereas if you clone from another token, take me back to the listing page
|
||||||
|
// so I can see it
|
||||||
|
return this.afterDelete(...arguments);
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
return cloned;
|
||||||
|
});
|
||||||
|
}, 'clone');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,6 +12,7 @@ export default class AuthMethod extends Model {
|
||||||
|
|
||||||
@attr('string') Datacenter;
|
@attr('string') Datacenter;
|
||||||
@attr('string') Namespace;
|
@attr('string') Namespace;
|
||||||
|
@attr('string') Partition;
|
||||||
@attr('string', { defaultValue: () => '' }) Description;
|
@attr('string', { defaultValue: () => '' }) Description;
|
||||||
@attr('string', { defaultValue: () => '' }) DisplayName;
|
@attr('string', { defaultValue: () => '' }) DisplayName;
|
||||||
@attr('string', { defaultValue: () => 'local' }) TokenLocality;
|
@attr('string', { defaultValue: () => 'local' }) TokenLocality;
|
||||||
|
|
|
@ -9,6 +9,7 @@ export default class BindingRule extends Model {
|
||||||
|
|
||||||
@attr('string') Datacenter;
|
@attr('string') Datacenter;
|
||||||
@attr('string') Namespace;
|
@attr('string') Namespace;
|
||||||
|
@attr('string') Partition;
|
||||||
@attr('string', { defaultValue: () => '' }) Description;
|
@attr('string', { defaultValue: () => '' }) Description;
|
||||||
@attr('string') AuthMethod;
|
@attr('string') AuthMethod;
|
||||||
@attr('string', { defaultValue: () => '' }) Selector;
|
@attr('string', { defaultValue: () => '' }) Selector;
|
||||||
|
|
|
@ -10,5 +10,6 @@ export default class Coordinate extends Model {
|
||||||
@attr() Coord; // {Vec, Error, Adjustment, Height}
|
@attr() Coord; // {Vec, Error, Adjustment, Height}
|
||||||
@attr('string') Segment;
|
@attr('string') Segment;
|
||||||
@attr('string') Datacenter;
|
@attr('string') Datacenter;
|
||||||
|
@attr('string') Partition;
|
||||||
@attr('number') SyncTime;
|
@attr('number') SyncTime;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ export default class DiscoveryChain extends Model {
|
||||||
@attr('string') ServiceName;
|
@attr('string') ServiceName;
|
||||||
|
|
||||||
@attr('string') Datacenter;
|
@attr('string') Datacenter;
|
||||||
|
// FIXME: Does this need partition?
|
||||||
@attr() Chain; // {}
|
@attr() Chain; // {}
|
||||||
@attr() meta; // {}
|
@attr() meta; // {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ export default class Intention extends Model {
|
||||||
|
|
||||||
@attr('string') Datacenter;
|
@attr('string') Datacenter;
|
||||||
@attr('string') Description;
|
@attr('string') Description;
|
||||||
|
// FIXME: Will we have Source/DestinationPartition?
|
||||||
@attr('string', { defaultValue: () => 'default' }) SourceNS;
|
@attr('string', { defaultValue: () => 'default' }) SourceNS;
|
||||||
@attr('string', { defaultValue: () => '*' }) SourceName;
|
@attr('string', { defaultValue: () => '*' }) SourceName;
|
||||||
@attr('string', { defaultValue: () => 'default' }) DestinationNS;
|
@attr('string', { defaultValue: () => 'default' }) DestinationNS;
|
||||||
|
|
|
@ -14,6 +14,7 @@ export default class Kv extends Model {
|
||||||
|
|
||||||
@attr('string') Datacenter;
|
@attr('string') Datacenter;
|
||||||
@attr('string') Namespace;
|
@attr('string') Namespace;
|
||||||
|
@attr('string') Partition;
|
||||||
@attr('number') LockIndex;
|
@attr('number') LockIndex;
|
||||||
@attr('number') Flags;
|
@attr('number') Flags;
|
||||||
@nullValue(undefined) @attr('string') Value;
|
@nullValue(undefined) @attr('string') Value;
|
||||||
|
|
|
@ -11,6 +11,7 @@ export default class Node extends Model {
|
||||||
@attr('string') ID;
|
@attr('string') ID;
|
||||||
|
|
||||||
@attr('string') Datacenter;
|
@attr('string') Datacenter;
|
||||||
|
@attr('string') Partition;
|
||||||
@attr('string') Address;
|
@attr('string') Address;
|
||||||
@attr('string') Node;
|
@attr('string') Node;
|
||||||
@attr('number') SyncTime;
|
@attr('number') SyncTime;
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
import Model, { attr } from '@ember-data/model';
|
import Model, { attr } from '@ember-data/model';
|
||||||
|
|
||||||
export const PRIMARY_KEY = 'Name';
|
export const PRIMARY_KEY = 'uid';
|
||||||
export const SLUG_KEY = 'Name';
|
export const SLUG_KEY = 'Name';
|
||||||
export const NSPACE_KEY = 'Namespace';
|
export const NSPACE_KEY = 'Namespace';
|
||||||
|
|
||||||
export default class Nspace extends Model {
|
export default class Nspace extends Model {
|
||||||
@attr('string') uid;
|
@attr('string') uid;
|
||||||
@attr('string') Name;
|
@attr('string') Name;
|
||||||
|
@attr('string') Datacenter;
|
||||||
|
@attr('string') Partition;
|
||||||
|
// Namespace is the same as Name but please don't alias as we want to keep
|
||||||
|
// mutating the response here instead
|
||||||
|
@attr('string') Namespace;
|
||||||
|
|
||||||
@attr('number') SyncTime;
|
@attr('number') SyncTime;
|
||||||
@attr('string', { defaultValue: () => '' }) Description;
|
@attr('string', { defaultValue: () => '' }) Description;
|
||||||
|
|
|
@ -9,6 +9,7 @@ export default class OidcProvider extends Model {
|
||||||
|
|
||||||
@attr('string') Datacenter;
|
@attr('string') Datacenter;
|
||||||
@attr('string') Namespace;
|
@attr('string') Namespace;
|
||||||
|
@attr('string') Partition;
|
||||||
@attr('string') Kind;
|
@attr('string') Kind;
|
||||||
@attr('string') AuthURL;
|
@attr('string') AuthURL;
|
||||||
@attr('string') DisplayName;
|
@attr('string') DisplayName;
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import Model from 'ember-data/model';
|
||||||
|
import attr from 'ember-data/attr';
|
||||||
|
|
||||||
|
export const PRIMARY_KEY = 'uid';
|
||||||
|
export const SLUG_KEY = 'Name';
|
||||||
|
export const PARTITION_KEY = 'Partition';
|
||||||
|
|
||||||
|
export default class PartitionModel extends Model {
|
||||||
|
@attr('string') uid;
|
||||||
|
@attr('string') Name;
|
||||||
|
@attr('string') Description;
|
||||||
|
@attr('string') Datacenter;
|
||||||
|
|
||||||
|
@attr('string') Namespace; // always ""
|
||||||
|
// Partition is the same as Name but please don't alias as we want to keep
|
||||||
|
// mutating the response here instead
|
||||||
|
@attr('string') Partition;
|
||||||
|
|
||||||
|
@attr('number') SyncTime;
|
||||||
|
@attr() meta;
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ export default class Policy extends Model {
|
||||||
|
|
||||||
@attr('string') Datacenter;
|
@attr('string') Datacenter;
|
||||||
@attr('string') Namespace;
|
@attr('string') Namespace;
|
||||||
|
@attr('string') Partition;
|
||||||
@attr('string', { defaultValue: () => '' }) Name;
|
@attr('string', { defaultValue: () => '' }) Name;
|
||||||
@attr('string', { defaultValue: () => '' }) Description;
|
@attr('string', { defaultValue: () => '' }) Description;
|
||||||
@attr('string', { defaultValue: () => '' }) Rules;
|
@attr('string', { defaultValue: () => '' }) Rules;
|
||||||
|
|
|
@ -11,6 +11,7 @@ export default class Proxy extends ServiceInstanceModel {
|
||||||
|
|
||||||
@attr('string') Datacenter;
|
@attr('string') Datacenter;
|
||||||
@attr('string') Namespace;
|
@attr('string') Namespace;
|
||||||
|
// FIXME: Does this need a partition?
|
||||||
@attr('string') ServiceName;
|
@attr('string') ServiceName;
|
||||||
@attr('string') ServiceID;
|
@attr('string') ServiceID;
|
||||||
@attr('string') NodeName;
|
@attr('string') NodeName;
|
||||||
|
|
|
@ -9,6 +9,7 @@ export default class Role extends Model {
|
||||||
|
|
||||||
@attr('string') Datacenter;
|
@attr('string') Datacenter;
|
||||||
@attr('string') Namespace;
|
@attr('string') Namespace;
|
||||||
|
@attr('string') Partition;
|
||||||
@attr('string', { defaultValue: () => '' }) Name;
|
@attr('string', { defaultValue: () => '' }) Name;
|
||||||
@attr('string', { defaultValue: () => '' }) Description;
|
@attr('string', { defaultValue: () => '' }) Description;
|
||||||
@attr({ defaultValue: () => [] }) Policies;
|
@attr({ defaultValue: () => [] }) Policies;
|
||||||
|
|
|
@ -51,6 +51,9 @@ export default class ServiceInstance extends Model {
|
||||||
@alias('Service.Meta') Meta;
|
@alias('Service.Meta') Meta;
|
||||||
@alias('Service.Namespace') Namespace;
|
@alias('Service.Namespace') Namespace;
|
||||||
|
|
||||||
|
// FIXME: Is parition top level or Service.Partition?
|
||||||
|
@attr('string') Partition;
|
||||||
|
|
||||||
@filter('Checks.@each.Kind', (item, i, arr) => item.Kind === 'service') ServiceChecks;
|
@filter('Checks.@each.Kind', (item, i, arr) => item.Kind === 'service') ServiceChecks;
|
||||||
@filter('Checks.@each.Kind', (item, i, arr) => item.Kind === 'node') NodeChecks;
|
@filter('Checks.@each.Kind', (item, i, arr) => item.Kind === 'node') NodeChecks;
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ export default class Service extends Model {
|
||||||
|
|
||||||
@attr('string') Datacenter;
|
@attr('string') Datacenter;
|
||||||
@attr('string') Namespace;
|
@attr('string') Namespace;
|
||||||
|
@attr('string') Partition;
|
||||||
@attr('string') Kind;
|
@attr('string') Kind;
|
||||||
@attr('number') ChecksPassing;
|
@attr('number') ChecksPassing;
|
||||||
@attr('number') ChecksCritical;
|
@attr('number') ChecksCritical;
|
||||||
|
|
|
@ -12,6 +12,7 @@ export default class Session extends Model {
|
||||||
@attr('string') Name;
|
@attr('string') Name;
|
||||||
@attr('string') Datacenter;
|
@attr('string') Datacenter;
|
||||||
@attr('string') Namespace;
|
@attr('string') Namespace;
|
||||||
|
@attr('string') Partition;
|
||||||
@attr('string') Node;
|
@attr('string') Node;
|
||||||
@attr('string') Behavior;
|
@attr('string') Behavior;
|
||||||
@attr('string') TTL;
|
@attr('string') TTL;
|
||||||
|
|
|
@ -11,6 +11,7 @@ export default class Token extends Model {
|
||||||
|
|
||||||
@attr('string') Datacenter;
|
@attr('string') Datacenter;
|
||||||
@attr('string') Namespace;
|
@attr('string') Namespace;
|
||||||
|
@attr('string') Partition;
|
||||||
@attr('string') IDPName;
|
@attr('string') IDPName;
|
||||||
@attr('string') SecretID;
|
@attr('string') SecretID;
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ export default class Topology extends Model {
|
||||||
|
|
||||||
@attr('string') Datacenter;
|
@attr('string') Datacenter;
|
||||||
@attr('string') Namespace;
|
@attr('string') Namespace;
|
||||||
|
@attr('string') Partition;
|
||||||
@attr('string') Protocol;
|
@attr('string') Protocol;
|
||||||
@attr('boolean') FilteredByACLs;
|
@attr('boolean') FilteredByACLs;
|
||||||
@attr('boolean') TransparentProxy;
|
@attr('boolean') TransparentProxy;
|
||||||
|
|
|
@ -133,7 +133,7 @@ export const routes = {
|
||||||
abilities: ['access acls'],
|
abilities: ['access acls'],
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
_options: { path: '/:id' },
|
_options: { path: '/:acl' },
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
_options: {
|
_options: {
|
||||||
|
@ -174,7 +174,7 @@ export const routes = {
|
||||||
tokens: {
|
tokens: {
|
||||||
_options: {
|
_options: {
|
||||||
path: '/tokens',
|
path: '/tokens',
|
||||||
abilities: ['read tokens'],
|
abilities: env('CONSUL_ACLS_ENABLED') ? ['read tokens'] : ['access acls'],
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
_options: { path: '/:id' },
|
_options: { path: '/:id' },
|
||||||
|
@ -219,7 +219,7 @@ export const routes = {
|
||||||
_options: { path: '/setting' },
|
_options: { path: '/setting' },
|
||||||
},
|
},
|
||||||
notfound: {
|
notfound: {
|
||||||
_options: { path: '/*path' },
|
_options: { path: '/*notfound' },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if (env('CONSUL_NSPACES_ENABLED')) {
|
if (env('CONSUL_NSPACES_ENABLED')) {
|
||||||
|
|
|
@ -1,93 +1,24 @@
|
||||||
import Route from 'consul-ui/routing/route';
|
import Route from 'consul-ui/routing/route';
|
||||||
import { inject as service } from '@ember/service';
|
import { action } from '@ember/object';
|
||||||
import { hash } from 'rsvp';
|
|
||||||
|
|
||||||
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
||||||
|
|
||||||
export default Route.extend(WithBlockingActions, {
|
export default class ApplicationRoute extends Route.extend(WithBlockingActions) {
|
||||||
router: service('router'),
|
@action
|
||||||
nspacesRepo: service('repository/nspace/disabled'),
|
error(e, transition) {
|
||||||
repo: service('repository/dc'),
|
// TODO: Normalize all this better
|
||||||
settings: service('settings'),
|
let error = {
|
||||||
model: function() {
|
status: e.code || e.statusCode || '',
|
||||||
return hash({
|
message: e.message || e.detail || 'Error',
|
||||||
router: this.router,
|
};
|
||||||
dcs: this.repo.findAll(),
|
if (e.errors && e.errors[0]) {
|
||||||
nspaces: this.nspacesRepo.findAll().catch(function() {
|
error = e.errors[0];
|
||||||
return [];
|
error.message = error.message || error.title || error.detail || 'Error';
|
||||||
}),
|
}
|
||||||
|
if (error.status === '') {
|
||||||
// these properties are added to the controller from route/dc
|
error.message = 'Error';
|
||||||
// as we don't have access to the dc and nspace params in the URL
|
}
|
||||||
// until we get to the route/dc route
|
this.controllerFor('application').setProperties({ error: error });
|
||||||
// permissions also requires the dc param
|
return true;
|
||||||
|
}
|
||||||
// dc: null,
|
}
|
||||||
// nspace: null
|
|
||||||
// token: null
|
|
||||||
// permissions: null
|
|
||||||
});
|
|
||||||
},
|
|
||||||
setupController: function(controller, model) {
|
|
||||||
this._super(...arguments);
|
|
||||||
controller.setProperties(model);
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
error: function(e, transition) {
|
|
||||||
// TODO: Normalize all this better
|
|
||||||
let error = {
|
|
||||||
status: e.code || e.statusCode || '',
|
|
||||||
message: e.message || e.detail || 'Error',
|
|
||||||
};
|
|
||||||
if (e.errors && e.errors[0]) {
|
|
||||||
error = e.errors[0];
|
|
||||||
error.message = error.message || error.title || error.detail || 'Error';
|
|
||||||
}
|
|
||||||
if (error.status === '') {
|
|
||||||
error.message = 'Error';
|
|
||||||
}
|
|
||||||
// Try and get the currently attempted dc, whereever that may be
|
|
||||||
let model = this.modelFor('dc') || this.modelFor('nspace.dc');
|
|
||||||
if (!model) {
|
|
||||||
const path = new URL(location.href).pathname
|
|
||||||
.substr(this.router.rootURL.length - 1)
|
|
||||||
.split('/')
|
|
||||||
.slice(1, 3);
|
|
||||||
model = {
|
|
||||||
nspace: { Name: 'default' },
|
|
||||||
};
|
|
||||||
if (path[0].startsWith('~')) {
|
|
||||||
model.nspace = {
|
|
||||||
Name: path.shift(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
model.dc = {
|
|
||||||
Name: path[0],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const app = this.modelFor('application') || {};
|
|
||||||
const dcs = app.dcs || [model.dc];
|
|
||||||
const nspaces = app.nspaces || [model.nspace];
|
|
||||||
hash({
|
|
||||||
dc:
|
|
||||||
error.status.toString().indexOf('5') !== 0
|
|
||||||
? this.repo.getActive(model.dc.Name, dcs)
|
|
||||||
: { Name: 'Error' },
|
|
||||||
dcs: dcs,
|
|
||||||
nspace: model.nspace,
|
|
||||||
nspaces: nspaces,
|
|
||||||
})
|
|
||||||
.then(model => Promise.all([model, this.repo.clearActive()]))
|
|
||||||
.then(([model]) => {
|
|
||||||
// we can't use setupController as we received an error
|
|
||||||
// so we do it manually instead
|
|
||||||
this.controllerFor('application').setProperties(model);
|
|
||||||
this.controllerFor('error').setProperties({ error: error });
|
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
this.controllerFor('error').setProperties({ error: error });
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,27 +1,17 @@
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import Route from 'consul-ui/routing/route';
|
import Route from 'consul-ui/routing/route';
|
||||||
import { get, action } from '@ember/object';
|
|
||||||
|
|
||||||
// TODO: We should potentially move all these nspace related things
|
// TODO: We should potentially move all these nspace related things
|
||||||
// up a level to application.js
|
// up a level to application.js
|
||||||
|
|
||||||
export default class DcRoute extends Route {
|
export default class DcRoute extends Route {
|
||||||
@service('repository/dc') repo;
|
|
||||||
@service('repository/permission') permissionsRepo;
|
@service('repository/permission') permissionsRepo;
|
||||||
@service('repository/nspace/disabled') nspacesRepo;
|
|
||||||
@service('settings') settingsRepo;
|
|
||||||
|
|
||||||
async model(params) {
|
async model(params) {
|
||||||
let [token, nspace, dc] = await Promise.all([
|
|
||||||
this.settingsRepo.findBySlug('token'),
|
|
||||||
this.nspacesRepo.getActive(this.optionalParams().nspace),
|
|
||||||
this.repo.findBySlug(params.dc),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// When disabled nspaces is [], so nspace is undefined
|
// When disabled nspaces is [], so nspace is undefined
|
||||||
const permissions = await this.permissionsRepo.findAll({
|
const permissions = await this.permissionsRepo.findAll({
|
||||||
dc: params.dc,
|
dc: params.dc,
|
||||||
ns: get(nspace || {}, 'Name'),
|
ns: this.optionalParams().nspace,
|
||||||
});
|
});
|
||||||
// the model here is actually required for the entire application
|
// the model here is actually required for the entire application
|
||||||
// but we need to wait until we are in this route so we know what the dc
|
// but we need to wait until we are in this route so we know what the dc
|
||||||
|
@ -30,49 +20,10 @@ export default class DcRoute extends Route {
|
||||||
// We do this here instead of in setupController to prevent timing issues
|
// We do this here instead of in setupController to prevent timing issues
|
||||||
// in lower routes
|
// in lower routes
|
||||||
this.controllerFor('application').setProperties({
|
this.controllerFor('application').setProperties({
|
||||||
dc,
|
|
||||||
nspace,
|
|
||||||
token,
|
|
||||||
permissions,
|
permissions,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
dc,
|
|
||||||
nspace,
|
|
||||||
token,
|
|
||||||
permissions,
|
permissions,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This will eventually be deprecated please see
|
|
||||||
// https://deprecations.emberjs.com/v3.x/#toc_deprecate-router-events
|
|
||||||
@action
|
|
||||||
willTransition(transition) {
|
|
||||||
if (
|
|
||||||
typeof transition !== 'undefined' &&
|
|
||||||
(transition.from.name.endsWith('nspaces.create') ||
|
|
||||||
transition.from.name.startsWith('nspace.dc.acls.tokens'))
|
|
||||||
) {
|
|
||||||
// Only when we create, reload the nspaces in the main menu to update them
|
|
||||||
// as we don't block for those
|
|
||||||
// And also when we [Use] a token reload the nspaces that you are able to see,
|
|
||||||
// including your permissions for being able to manage namespaces
|
|
||||||
// Potentially we should just do this on every single transition
|
|
||||||
// but then we would need to check to see if nspaces are enabled
|
|
||||||
const controller = this.controllerFor('application');
|
|
||||||
Promise.all([
|
|
||||||
this.nspacesRepo.findAll(),
|
|
||||||
this.permissionsRepo.findAll({
|
|
||||||
dc: get(controller, 'dc.Name'),
|
|
||||||
nspace: get(controller, 'nspace.Name'),
|
|
||||||
}),
|
|
||||||
]).then(([nspaces, permissions]) => {
|
|
||||||
if (typeof controller !== 'undefined') {
|
|
||||||
controller.setProperties({
|
|
||||||
nspaces: nspaces,
|
|
||||||
permissions: permissions,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
import { inject as service } from '@ember/service';
|
|
||||||
import Route from 'consul-ui/routing/route';
|
import Route from 'consul-ui/routing/route';
|
||||||
import { hash } from 'rsvp';
|
|
||||||
|
|
||||||
export default class IndexRoute extends Route {
|
export default class IndexRoute extends Route {
|
||||||
@service('repository/auth-method') repo;
|
|
||||||
|
|
||||||
queryParams = {
|
queryParams = {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
source: 'source',
|
source: 'source',
|
||||||
|
@ -18,21 +14,4 @@ export default class IndexRoute extends Route {
|
||||||
replace: true,
|
replace: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
model(params) {
|
|
||||||
return hash({
|
|
||||||
...this.repo.status({
|
|
||||||
items: this.repo.findAllByDatacenter({
|
|
||||||
dc: this.modelFor('dc').dc.Name,
|
|
||||||
ns: this.optionalParams().nspace,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
searchProperties: this.queryParams.searchproperty.empty[0],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setupController(controller, model) {
|
|
||||||
super.setupController(...arguments);
|
|
||||||
controller.setProperties(model);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
import { inject as service } from '@ember/service';
|
|
||||||
import SingleRoute from 'consul-ui/routing/single';
|
|
||||||
import { hash } from 'rsvp';
|
|
||||||
|
|
||||||
export default class ShowRoute extends SingleRoute {
|
|
||||||
@service('repository/auth-method') repo;
|
|
||||||
@service('repository/binding-rule') bindingRuleRepo;
|
|
||||||
|
|
||||||
model(params) {
|
|
||||||
const dc = this.modelFor('dc').dc;
|
|
||||||
const nspace = this.optionalParams().nspace;
|
|
||||||
|
|
||||||
return super.model(...arguments).then(model => {
|
|
||||||
return hash({
|
|
||||||
...model,
|
|
||||||
...{
|
|
||||||
item: this.repo.findBySlug({
|
|
||||||
id: params.id,
|
|
||||||
dc: dc.Name,
|
|
||||||
ns: nspace,
|
|
||||||
}),
|
|
||||||
bindingRules: this.bindingRuleRepo.findAllByDatacenter({
|
|
||||||
ns: nspace,
|
|
||||||
dc: dc.Name,
|
|
||||||
authmethod: params.id,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setupController(controller, model) {
|
|
||||||
super.setupController(...arguments);
|
|
||||||
controller.setProperties(model);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
import Route from 'consul-ui/routing/route';
|
|
||||||
|
|
||||||
export default class AuthMethodRoute extends Route {
|
|
||||||
model(params) {
|
|
||||||
const parent = this.routeName
|
|
||||||
.split('.')
|
|
||||||
.slice(0, -1)
|
|
||||||
.join('.');
|
|
||||||
return this.modelFor(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
setupController(controller, model) {
|
|
||||||
super.setupController(...arguments);
|
|
||||||
controller.setProperties(model);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
import Route from 'consul-ui/routing/route';
|
|
||||||
|
|
||||||
export default class BindingRulesRoute extends Route {
|
|
||||||
model() {
|
|
||||||
const parent = this.routeName
|
|
||||||
.split('.')
|
|
||||||
.slice(0, -1)
|
|
||||||
.join('.');
|
|
||||||
return this.modelFor(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
setupController(controller, model) {
|
|
||||||
super.setupController(...arguments);
|
|
||||||
controller.setProperties(model);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Route from 'consul-ui/routing/route';
|
import Route from 'consul-ui/routing/route';
|
||||||
import to from 'consul-ui/utils/routing/redirect-to';
|
import to from 'consul-ui/utils/routing/redirect-to';
|
||||||
|
|
||||||
export default Route.extend({
|
export default class AuthMethodShowIndexRoute extends Route {
|
||||||
redirect: to('auth-method'),
|
redirect = to('auth-method');
|
||||||
});
|
}
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
import Route from 'consul-ui/routing/route';
|
|
||||||
|
|
||||||
export default class NspaceRulesRoute extends Route {
|
|
||||||
model() {
|
|
||||||
const parent = this.routeName
|
|
||||||
.split('.')
|
|
||||||
.slice(0, -1)
|
|
||||||
.join('.');
|
|
||||||
return this.modelFor(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
setupController(controller, model) {
|
|
||||||
super.setupController(...arguments);
|
|
||||||
controller.setProperties(model);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,5 @@
|
||||||
import Route from './edit';
|
import Route from './edit';
|
||||||
import CreatingRoute from 'consul-ui/mixins/creating-route';
|
|
||||||
|
|
||||||
export default class CreateRoute extends Route.extend(CreatingRoute) {
|
export default class CreateRoute extends Route {
|
||||||
templateName = 'dc/acls/policies/edit';
|
templateName = 'dc/acls/policies/edit';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,48 +1,8 @@
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import SingleRoute from 'consul-ui/routing/single';
|
import Route from 'consul-ui/routing/route';
|
||||||
import { hash } from 'rsvp';
|
|
||||||
import { get } from '@ember/object';
|
|
||||||
|
|
||||||
import WithPolicyActions from 'consul-ui/mixins/policy/with-actions';
|
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
||||||
|
|
||||||
export default class EditRoute extends SingleRoute.extend(WithPolicyActions) {
|
export default class EditRoute extends Route.extend(WithBlockingActions) {
|
||||||
@service('repository/policy')
|
@service('repository/policy') repo;
|
||||||
repo;
|
|
||||||
|
|
||||||
@service('repository/token')
|
|
||||||
tokenRepo;
|
|
||||||
|
|
||||||
model(params) {
|
|
||||||
const dc = this.modelFor('dc').dc.Name;
|
|
||||||
const nspace = this.optionalParams().nspace;
|
|
||||||
const tokenRepo = this.tokenRepo;
|
|
||||||
return super.model(...arguments).then(model => {
|
|
||||||
return hash({
|
|
||||||
...model,
|
|
||||||
...{
|
|
||||||
routeName: this.routeName,
|
|
||||||
items: tokenRepo
|
|
||||||
.findByPolicy({
|
|
||||||
ns: nspace,
|
|
||||||
dc: dc,
|
|
||||||
id: get(model.item, 'ID'),
|
|
||||||
})
|
|
||||||
.catch(function(e) {
|
|
||||||
switch (get(e, 'errors.firstObject.status')) {
|
|
||||||
case '403':
|
|
||||||
case '401':
|
|
||||||
// do nothing the SingleRoute will have caught it already
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setupController(controller, model) {
|
|
||||||
super.setupController(...arguments);
|
|
||||||
controller.setProperties(model);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import { inject as service } from '@ember/service';
|
|
||||||
import Route from 'consul-ui/routing/route';
|
import Route from 'consul-ui/routing/route';
|
||||||
import { hash } from 'rsvp';
|
import { inject as service } from '@ember/service';
|
||||||
|
|
||||||
import WithPolicyActions from 'consul-ui/mixins/policy/with-actions';
|
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
||||||
|
|
||||||
export default class IndexRoute extends Route.extend(WithPolicyActions) {
|
export default class IndexRoute extends Route.extend(WithBlockingActions) {
|
||||||
@service('repository/policy') repo;
|
@service('repository/policy') repo;
|
||||||
|
|
||||||
queryParams = {
|
queryParams = {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
datacenter: {
|
datacenter: {
|
||||||
|
@ -22,21 +20,4 @@ export default class IndexRoute extends Route.extend(WithPolicyActions) {
|
||||||
replace: true,
|
replace: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
model(params) {
|
|
||||||
return hash({
|
|
||||||
...this.repo.status({
|
|
||||||
items: this.repo.findAllByDatacenter({
|
|
||||||
ns: this.optionalParams().nspace,
|
|
||||||
dc: this.modelFor('dc').dc.Name,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
searchProperties: this.queryParams.searchproperty.empty[0],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setupController(controller, model) {
|
|
||||||
super.setupController(...arguments);
|
|
||||||
controller.setProperties(model);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import Route from './edit';
|
import Route from './edit';
|
||||||
import CreatingRoute from 'consul-ui/mixins/creating-route';
|
|
||||||
|
|
||||||
export default class CreateRoute extends Route.extend(CreatingRoute) {
|
export default class CreateRoute extends Route {
|
||||||
templateName = 'dc/acls/roles/edit';
|
templateName = 'dc/acls/roles/edit';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,47 +1,8 @@
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import SingleRoute from 'consul-ui/routing/single';
|
import Route from 'consul-ui/routing/route';
|
||||||
import { hash } from 'rsvp';
|
|
||||||
import { get } from '@ember/object';
|
|
||||||
|
|
||||||
import WithRoleActions from 'consul-ui/mixins/role/with-actions';
|
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
||||||
|
|
||||||
export default class EditRoute extends SingleRoute.extend(WithRoleActions) {
|
export default class EditRoute extends Route.extend(WithBlockingActions) {
|
||||||
@service('repository/role')
|
@service('repository/role') repo;
|
||||||
repo;
|
|
||||||
|
|
||||||
@service('repository/token')
|
|
||||||
tokenRepo;
|
|
||||||
|
|
||||||
model(params) {
|
|
||||||
const dc = this.modelFor('dc').dc.Name;
|
|
||||||
const nspace = this.optionalParams().nspace;
|
|
||||||
const tokenRepo = this.tokenRepo;
|
|
||||||
return super.model(...arguments).then(model => {
|
|
||||||
return hash({
|
|
||||||
...model,
|
|
||||||
...{
|
|
||||||
items: tokenRepo
|
|
||||||
.findByRole({
|
|
||||||
ns: nspace,
|
|
||||||
dc: dc,
|
|
||||||
id: get(model.item, 'ID'),
|
|
||||||
})
|
|
||||||
.catch(function(e) {
|
|
||||||
switch (get(e, 'errors.firstObject.status')) {
|
|
||||||
case '403':
|
|
||||||
case '401':
|
|
||||||
// do nothing the SingleRoute will have caught it already
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setupController(controller, model) {
|
|
||||||
super.setupController(...arguments);
|
|
||||||
controller.setProperties(model);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import { inject as service } from '@ember/service';
|
|
||||||
import Route from 'consul-ui/routing/route';
|
import Route from 'consul-ui/routing/route';
|
||||||
import { hash } from 'rsvp';
|
import { inject as service } from '@ember/service';
|
||||||
|
|
||||||
import WithRoleActions from 'consul-ui/mixins/role/with-actions';
|
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
||||||
|
|
||||||
export default class IndexRoute extends Route.extend(WithRoleActions) {
|
export default class IndexRoute extends Route.extend(WithBlockingActions) {
|
||||||
@service('repository/role') repo;
|
@service('repository/role') repo;
|
||||||
|
|
||||||
queryParams = {
|
queryParams = {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
searchproperty: {
|
searchproperty: {
|
||||||
|
@ -18,21 +16,4 @@ export default class IndexRoute extends Route.extend(WithRoleActions) {
|
||||||
replace: true,
|
replace: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
model(params) {
|
|
||||||
return hash({
|
|
||||||
...this.repo.status({
|
|
||||||
items: this.repo.findAllByDatacenter({
|
|
||||||
ns: this.optionalParams().nspace,
|
|
||||||
dc: this.modelFor('dc').dc.Name,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
searchProperties: this.queryParams.searchproperty.empty[0],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setupController(controller, model) {
|
|
||||||
super.setupController(...arguments);
|
|
||||||
controller.setProperties(model);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import Route from './edit';
|
import Route from './edit';
|
||||||
import CreatingRoute from 'consul-ui/mixins/creating-route';
|
|
||||||
|
|
||||||
export default class CreateRoute extends Route.extend(CreatingRoute) {
|
export default class CreateRoute extends Route {
|
||||||
templateName = 'dc/acls/tokens/edit';
|
templateName = 'dc/acls/tokens/edit';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,15 @@
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import SingleRoute from 'consul-ui/routing/single';
|
import Route from 'consul-ui/routing/route';
|
||||||
import { hash } from 'rsvp';
|
|
||||||
|
|
||||||
import WithTokenActions from 'consul-ui/mixins/token/with-actions';
|
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
||||||
|
|
||||||
export default class EditRoute extends SingleRoute.extend(WithTokenActions) {
|
export default class EditRoute extends Route.extend(WithBlockingActions) {
|
||||||
@service('repository/token')
|
@service('repository/token') repo;
|
||||||
repo;
|
@service('settings') settings;
|
||||||
|
|
||||||
@service('settings')
|
async model(params, transition) {
|
||||||
settings;
|
return {
|
||||||
|
token: await this.settings.findBySlug('token'),
|
||||||
model(params, transition) {
|
};
|
||||||
return super.model(...arguments).then(model => {
|
|
||||||
return hash({
|
|
||||||
...model,
|
|
||||||
...{
|
|
||||||
routeName: this.routeName,
|
|
||||||
token: this.settings.findBySlug('token'),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setupController(controller, model) {
|
|
||||||
super.setupController(...arguments);
|
|
||||||
controller.setProperties(model);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
import { inject as service } from '@ember/service';
|
|
||||||
import Route from 'consul-ui/routing/route';
|
import Route from 'consul-ui/routing/route';
|
||||||
import { hash } from 'rsvp';
|
import { inject as service } from '@ember/service';
|
||||||
import { get } from '@ember/object';
|
|
||||||
import WithTokenActions from 'consul-ui/mixins/token/with-actions';
|
|
||||||
|
|
||||||
export default class IndexRoute extends Route.extend(WithTokenActions) {
|
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
||||||
|
|
||||||
|
export default class IndexRoute extends Route.extend(WithBlockingActions) {
|
||||||
@service('repository/token') repo;
|
@service('repository/token') repo;
|
||||||
@service('settings') settings;
|
|
||||||
|
|
||||||
queryParams = {
|
queryParams = {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
kind: 'kind',
|
kind: 'kind',
|
||||||
|
@ -20,35 +17,4 @@ export default class IndexRoute extends Route.extend(WithTokenActions) {
|
||||||
replace: true,
|
replace: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
async beforeModel(transition) {
|
|
||||||
const token = await this.settings.findBySlug('token');
|
|
||||||
// If you have a token set with AccessorID set to null (legacy mode)
|
|
||||||
// then rewrite to the old acls
|
|
||||||
if (token && get(token, 'AccessorID') === null) {
|
|
||||||
// If you return here, you get a TransitionAborted error in the tests only
|
|
||||||
// everything works fine either way checking things manually
|
|
||||||
this.replaceWith('dc.acls');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
model(params) {
|
|
||||||
const nspace = this.optionalParams().nspace;
|
|
||||||
return hash({
|
|
||||||
...this.repo.status({
|
|
||||||
items: this.repo.findAllByDatacenter({
|
|
||||||
ns: nspace,
|
|
||||||
dc: this.modelFor('dc').dc.Name,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
nspace: nspace,
|
|
||||||
token: this.settings.findBySlug('token'),
|
|
||||||
searchProperties: this.queryParams.searchproperty.empty[0],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setupController(controller, model) {
|
|
||||||
super.setupController(...arguments);
|
|
||||||
controller.setProperties(model);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import Route from 'consul-ui/routing/route';
|
import Route from 'consul-ui/routing/route';
|
||||||
|
import to from 'consul-ui/utils/routing/redirect-to';
|
||||||
|
|
||||||
export default class IndexRoute extends Route {
|
export default class IndexRoute extends Route {
|
||||||
beforeModel() {
|
redirect = to('services');
|
||||||
this.transitionTo('dc.services');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import Route from './edit';
|
import Route from 'consul-ui/routing/route';
|
||||||
|
|
||||||
export default class CreateRoute extends Route {
|
export default class CreateRoute extends Route {
|
||||||
templateName = 'dc/intentions/edit';
|
templateName = 'dc/intentions/edit';
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
import { inject as service } from '@ember/service';
|
|
||||||
import Route from 'consul-ui/routing/route';
|
|
||||||
|
|
||||||
export default class EditRoute extends Route {
|
|
||||||
@service('repository/intention') repo;
|
|
||||||
@service('env') env;
|
|
||||||
|
|
||||||
async model(params, transition) {
|
|
||||||
const dc = this.modelFor('dc').dc.Name;
|
|
||||||
const nspace = this.optionalParams().nspace;
|
|
||||||
|
|
||||||
let item;
|
|
||||||
if (typeof params.intention_id !== 'undefined') {
|
|
||||||
item = await this.repo.findBySlug({
|
|
||||||
ns: nspace,
|
|
||||||
dc: dc,
|
|
||||||
id: params.intention_id,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const defaultNspace = this.env.var('CONSUL_NSPACES_ENABLED') ? '*' : 'default';
|
|
||||||
item = await this.repo.create({
|
|
||||||
SourceNS: nspace || defaultNspace,
|
|
||||||
DestinationNS: nspace || defaultNspace,
|
|
||||||
Datacenter: dc,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
dc,
|
|
||||||
nspace,
|
|
||||||
item,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
setupController(controller, model) {
|
|
||||||
super.setupController(...arguments);
|
|
||||||
controller.setProperties(model);
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue