mirror of https://github.com/status-im/consul.git
ui: Move nspace CRUD to use the same approach as partitions (#11633)
This sounds a bit 'backwards' as the end goal here is to add an improved UX to partitions, not namespaces. The reason for doing it this way is that Namespaces already has a type of 'improved UX' CRUD in that it has one to many relationship in the form when saving your namespaces (the end goal for partitions). In moving Namespaces to use the same approach as partitions we: - Ensure the new approach works with one-to-many forms. - Test the new approach without writing a single test (we already have a bunch of tests for namespaces which are now testing the approach used by both namespaces and partitions) Additionally: - Fixes issue with missing default nspace in the nspace selector - In doing when checking to see that things where consistent between the two, I found a few little minor problems with the Admin Partition CRUD so fixed those up here also. - Removed the old style Nspace notifications
This commit is contained in:
parent
cef938e620
commit
26341f0b05
|
@ -22,7 +22,7 @@ references:
|
|||
test-results: &TEST_RESULTS_DIR /tmp/test-results
|
||||
|
||||
cache:
|
||||
yarn: &YARN_CACHE_KEY consul-ui-v5-{{ checksum "ui/yarn.lock" }}
|
||||
yarn: &YARN_CACHE_KEY consul-ui-v6-{{ checksum "ui/yarn.lock" }}
|
||||
rubygem: &RUBYGEM_CACHE_KEY static-site-gems-v1-{{ checksum "Gemfile.lock" }}
|
||||
|
||||
environment: &ENVIRONMENT
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
"scripts": {
|
||||
"doc:toc": "doctoc README.md",
|
||||
"compliance": "npm-run-all compliance:*",
|
||||
"compliance:licenses": "license-checker --summary --onlyAllow 'Python-2.0;Apache*;Apache License, Version 2.0;Apache-2.0;Apache 2.0;Artistic-2.0;BSD;BSD-3-Clause;CC-BY-3.0;CC-BY-4.0;CC0-1.0;ISC;MIT;MPL-2.0;Public Domain;Unicode-TOU;Unlicense;WTFPL' --excludePackages 'consul-ui@2.2.0;consul-acls@0.1.0;consul-partitions@0.1.0'"
|
||||
"compliance:licenses": "license-checker --summary --onlyAllow 'Python-2.0;Apache*;Apache License, Version 2.0;Apache-2.0;Apache 2.0;Artistic-2.0;BSD;BSD-3-Clause;CC-BY-3.0;CC-BY-4.0;CC0-1.0;ISC;MIT;MPL-2.0;Public Domain;Unicode-TOU;Unlicense;WTFPL' --excludePackages 'consul-ui@2.2.0;consul-acls@0.1.0;consul-partitions@0.1.0;consul-nspaces@0.1.0'"
|
||||
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# Consul::Nspace::Form
|
||||
|
||||
```hbs preview-template
|
||||
<DataLoader @src={{
|
||||
uri '/${partition}/${nspace}/${dc}/namespace/${id}'
|
||||
(hash
|
||||
partition='partition'
|
||||
nspace='nspace'
|
||||
dc='dc'
|
||||
id=''
|
||||
)
|
||||
}}
|
||||
as |loader|>
|
||||
<BlockSlot @name="loaded">
|
||||
<Consul::Nspace::Form
|
||||
@item={{loader.data}}
|
||||
@dc={{'dc-1'}}
|
||||
@nspace={{'nspace'}}
|
||||
@partition={{'partition'}}
|
||||
@onsubmit={{noop}}
|
||||
/>
|
||||
</BlockSlot>
|
||||
</DataLoader>
|
||||
```
|
|
@ -0,0 +1,169 @@
|
|||
<div
|
||||
class="consul-nspace-form"
|
||||
...attributes
|
||||
>
|
||||
<DataWriter
|
||||
@sink={{uri
|
||||
'/${partition}/${nspace}/${dc}/nspace'
|
||||
(hash
|
||||
partition=''
|
||||
nspace=''
|
||||
dc=@item.Datacenter
|
||||
)
|
||||
}}
|
||||
@type={{'nspace'}}
|
||||
@label={{"Namespace"}}
|
||||
@ondelete={{fn (if @ondelete @ondelete @onsubmit) @item}}
|
||||
@onchange={{fn (optional @onsubmit) @item}}
|
||||
as |writer|>
|
||||
<BlockSlot @name="removed" as |after|>
|
||||
<Consul::Nspace::Notifications
|
||||
{{notification
|
||||
after=(action after)
|
||||
}}
|
||||
@type="remove"
|
||||
/>
|
||||
</BlockSlot>
|
||||
|
||||
<BlockSlot @name="content">
|
||||
|
||||
{{#let
|
||||
|
||||
(not (can "write nspaces"))
|
||||
|
||||
@item
|
||||
|
||||
(hash
|
||||
help='Must be a valid DNS hostname. Must contain 1-64 characters (numbers, letters, and hyphens), and must begin with a letter. Once created, this cannot be changed.'
|
||||
Name=(array
|
||||
(hash
|
||||
test='^[a-zA-Z0-9]([a-zA-Z0-9-]{0,62}[a-zA-Z0-9])?$'
|
||||
error='Name must be a valid DNS hostname.'
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(hash
|
||||
Description=(array)
|
||||
)
|
||||
|
||||
as |readOnly item Name Description|}}
|
||||
<form
|
||||
{{on 'submit' (fn writer.persist item)}}
|
||||
{{disabled readOnly}}
|
||||
>
|
||||
|
||||
<StateChart
|
||||
@src={{state-chart 'validate'}}
|
||||
as |State Guard ChartAction dispatch state|>
|
||||
|
||||
<fieldset>
|
||||
{{#if (is "new nspace" item=item)}}
|
||||
<TextInput
|
||||
@name="Name"
|
||||
@placeholder="Name"
|
||||
@item={{item}}
|
||||
@validations={{Name}}
|
||||
@chart={{hash
|
||||
state=state
|
||||
dispatch=dispatch
|
||||
}}
|
||||
/>
|
||||
{{/if}}
|
||||
<TextInput
|
||||
@expanded={{true}}
|
||||
@name="Description"
|
||||
@label="Description (Optional)"
|
||||
@item={{item}}
|
||||
@validations={{Description}}
|
||||
@chart={{hash
|
||||
state=state
|
||||
dispatch=dispatch
|
||||
}}
|
||||
/>
|
||||
</fieldset>
|
||||
{{#if (can 'use acls')}}
|
||||
<fieldset id="roles">
|
||||
<h2>Roles</h2>
|
||||
<p>
|
||||
{{#if (can "write nspace" item=item)}}
|
||||
By adding roles to this namespaces, you will apply them to all tokens created within this namespace.
|
||||
{{else}}
|
||||
The following roles are applied to all tokens created within this namespace.
|
||||
{{/if}}
|
||||
</p>
|
||||
<RoleSelector
|
||||
@dc={{@dc}}
|
||||
@nspace="default"
|
||||
@partition={{@partition}}
|
||||
@disabled={{readOnly}}
|
||||
@items={{item.ACLs.RoleDefaults}}
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset id="policies">
|
||||
<h2>Policies</h2>
|
||||
<p>
|
||||
{{#if (not readOnly)}}
|
||||
By adding policies to this namespace, you will apply them to all tokens created within this namespace.
|
||||
{{else}}
|
||||
The following policies are applied to all tokens created within this namespace.
|
||||
{{/if}}
|
||||
</p>
|
||||
<PolicySelector
|
||||
@dc={{@dc}}
|
||||
@nspace="default"
|
||||
@partition={{@partition}}
|
||||
@disabled={{readOnly}}
|
||||
@allowIdentity={{false}}
|
||||
@items={{item.ACLs.PolicyDefaults}}
|
||||
/>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
<div>
|
||||
{{#if (and (is "new nspace" item=item) (can "create nspaces"))}}
|
||||
<Action
|
||||
@type="submit"
|
||||
{{disabled (or (is "pristine nspace" item=item) (state-matches state "error"))}}
|
||||
>
|
||||
Save
|
||||
</Action>
|
||||
{{else if (can "write nspace" item=item)}}
|
||||
<Action @type="submit">Save</Action>
|
||||
{{/if}}
|
||||
|
||||
<Action
|
||||
@type="reset"
|
||||
{{on 'click' (if @oncancel (fn @oncancel item) (fn @onsubmit item))}}
|
||||
>
|
||||
Cancel
|
||||
</Action>
|
||||
|
||||
{{#if (and (not (is "new nspace" item=item)) (can "delete nspace" item=item))}}
|
||||
<ConfirmationDialog @message="Are you sure you want to delete this Namespace?">
|
||||
<BlockSlot @name="action" as |confirm|>
|
||||
<Action
|
||||
data-test-delete
|
||||
class="type-delete"
|
||||
{{on 'click' (fn confirm (fn writer.delete item))}}
|
||||
>
|
||||
Delete
|
||||
</Action>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="dialog" as |execute cancel message|>
|
||||
<DeleteConfirmation
|
||||
@message={{message}}
|
||||
@execute={{execute}}
|
||||
@cancel={{cancel}}
|
||||
/>
|
||||
</BlockSlot>
|
||||
</ConfirmationDialog>
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
</StateChart>
|
||||
</form>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</DataWriter>
|
||||
</div>
|
||||
|
|
@ -1,29 +1,30 @@
|
|||
---
|
||||
class: ember
|
||||
---
|
||||
## Consul::Nspace::List
|
||||
# Consul::Nspace::List
|
||||
|
||||
```hbs
|
||||
<DataSource @src="/partition/default/dc-1/namespaces" as |source|>
|
||||
A presentational component for rendering Consul Namespaces
|
||||
|
||||
Please note:
|
||||
|
||||
- For the moment, make sure you have enabled nspaces using developer debug
|
||||
cookies.
|
||||
|
||||
```hbs preview-template
|
||||
<DataSource @src={{uri '/partition/default/dc-1/namespaces'}} as |source|>
|
||||
<Consul::Nspace::List
|
||||
@items={{source.data}}
|
||||
@ondelete={{action (noop)}}
|
||||
@ondelete={{noop}}
|
||||
/>
|
||||
</DataSource>
|
||||
```
|
||||
|
||||
A presentational component for rendering Consul Namespaces
|
||||
|
||||
### Arguments
|
||||
## Arguments
|
||||
|
||||
| Argument/Attribute | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `items` | `array` | | An array of Namespaces |
|
||||
| `ondelete` | `function` | | An action to execute when the `Delete` action is clicked |
|
||||
|
||||
### See
|
||||
## See
|
||||
|
||||
- [Component Source Code](./index.js)
|
||||
- [Template Source Code](./index.hbs)
|
||||
|
||||
---
|
|
@ -0,0 +1,19 @@
|
|||
# Consul::Nspace::Notifications
|
||||
|
||||
A Notification component specifically for namespaces. This is only a component as we currently use this in two places and if we need to add more types we can do so in one place.
|
||||
|
||||
We currently only have one 'remove' type due to the fact that namespaces can't use the default 'delete' notification as they get 'marked for deletion' instead.
|
||||
|
||||
```hbs preview-template
|
||||
<Consul::Nspace::Notifications
|
||||
@type={{'remove'}}
|
||||
/>
|
||||
```
|
||||
|
||||
|
||||
|
||||
## See
|
||||
|
||||
- [Template Source Code](./index.hbs)
|
||||
|
||||
---
|
|
@ -0,0 +1,16 @@
|
|||
{{#if (eq @type 'remove')}}
|
||||
<Notice
|
||||
class="notification-delete"
|
||||
@type="success"
|
||||
...attributes
|
||||
as |notice|>
|
||||
<notice.Header>
|
||||
<strong>Success!</strong>
|
||||
</notice.Header>
|
||||
<notice.Body>
|
||||
<p>
|
||||
Your Namespace has been marked for deletion.
|
||||
</p>
|
||||
</notice.Body>
|
||||
</Notice>
|
||||
{{/if}}
|
|
@ -0,0 +1,30 @@
|
|||
# Consul::Nspace::SearchBar
|
||||
|
||||
Searchbar tailored for searching namespaces. Follows our more generic
|
||||
'*::SearchBar' component interface.
|
||||
|
||||
```hbs preview-template
|
||||
<Consul::Nspace::SearchBar
|
||||
@search={{this.search}}
|
||||
@onsearch={{fn (mut this.search) value="target.value"}}
|
||||
|
||||
@sort={{hash
|
||||
value='Name:asc'
|
||||
change=(noop)
|
||||
}}
|
||||
|
||||
@filter={{hash
|
||||
searchproperty=(hash
|
||||
value=(array)
|
||||
change=(noop)
|
||||
default=(array)
|
||||
)
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
## See
|
||||
|
||||
- [Template Source Code](./index.hbs)
|
||||
|
||||
---
|
|
@ -0,0 +1,40 @@
|
|||
# Consul::Nspace::Selector
|
||||
|
||||
A conditional, autoloading, menu component specifically for making it easy to select namespaces.
|
||||
|
||||
Please note:
|
||||
|
||||
- Currently at least, you must add this inside of a `<ul>` element, as the `<li>` elements output by this component are intended to be mixed with other sibling `<li>`s from other components or template code.
|
||||
- For the moment, make sure you have enabled nspaces using developer debug
|
||||
cookies.
|
||||
|
||||
```hbs preview-template
|
||||
<ul>
|
||||
<Consul::Nspace::Selector
|
||||
@dc={{hash
|
||||
Name='dc-1'
|
||||
}}
|
||||
@nspace='default'
|
||||
@partition='default'
|
||||
@nspaces={{or this.nspaces (array)}}
|
||||
@onchange={{action (mut this.nspaces) value="data"}}
|
||||
/>
|
||||
</ul>
|
||||
```
|
||||
|
||||
|
||||
## Arguments
|
||||
|
||||
| Argument/Attribute | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `dc` | `object` | | The current datacenter |
|
||||
| `nspace` | `string` | | The name of the current namespace |
|
||||
| `partition` | `string` | | The name of the current partition |
|
||||
| `nspaces` | `array` | | A list of nspace models/objects to use for the selector |
|
||||
| `onchange` | `function` | | An event handler, for when nspaces are loaded. You probably want to update `@nspaces` using this. |
|
||||
|
||||
## See
|
||||
|
||||
- [Template Source Code](./index.hbs)
|
||||
|
||||
---
|
|
@ -0,0 +1,74 @@
|
|||
{{#if (can "use nspaces")}}
|
||||
{{#if (can "choose nspaces")}}
|
||||
{{#let
|
||||
(or @nspace 'default')
|
||||
as |nspace|}}
|
||||
<li
|
||||
class="nspaces"
|
||||
data-test-nspace-menu
|
||||
>
|
||||
<PopoverMenu
|
||||
aria-label="Namespace"
|
||||
@position="left"
|
||||
as |components api|>
|
||||
<BlockSlot @name="trigger">
|
||||
{{nspace}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="menu">
|
||||
{{#let components.MenuItem components.MenuSeparator as |MenuItem MenuSeparator|}}
|
||||
{{#if (gt @nspaces.length 0)}}
|
||||
<DataSource
|
||||
@src={{uri
|
||||
'/${partition}/*/${dc}/namespaces'
|
||||
(hash
|
||||
partition=@partition
|
||||
dc=@dc.Name
|
||||
)
|
||||
}}
|
||||
@onchange={{fn (optional @onchange)}}
|
||||
@loading="lazy"
|
||||
/>
|
||||
{{else}}
|
||||
<DataSource
|
||||
@src={{uri
|
||||
'/${partition}/*/${dc}/namespaces'
|
||||
(hash
|
||||
partition=@partition
|
||||
dc=@dc.Name
|
||||
)
|
||||
}}
|
||||
@onchange={{fn (optional @onchange)}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{#each (reject-by 'DeletedAt' @nspaces) as |item|}}
|
||||
<MenuItem
|
||||
class={{if (eq nspace item.Name) 'is-active'}}
|
||||
@href={{href-to '.' params=(hash
|
||||
partition=(if (gt @partition.length 0) @partition undefined)
|
||||
nspace=item.Name
|
||||
)}}
|
||||
>
|
||||
<BlockSlot @name="label">
|
||||
{{item.Name}}
|
||||
</BlockSlot>
|
||||
</MenuItem>
|
||||
{{/each}}
|
||||
{{#if (can 'manage nspaces')}}
|
||||
<MenuSeparator />
|
||||
<MenuItem
|
||||
data-test-main-nav-nspaces
|
||||
@href={{href-to 'dc.nspaces' @dc.Name}}
|
||||
>
|
||||
<BlockSlot @name="label">
|
||||
Manage Namespaces
|
||||
</BlockSlot>
|
||||
</MenuItem>
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverMenu>
|
||||
</li>
|
||||
{{/let}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
|
@ -30,13 +30,6 @@ as |route|>
|
|||
loader.data.isNew
|
||||
as |dc partition nspace item create|}}
|
||||
<AppView>
|
||||
<BlockSlot @name="notification" as |status type item error|>
|
||||
<Consul::Nspace::Notifications
|
||||
@type={{type}}
|
||||
@status={{status}}
|
||||
@error={{error}}
|
||||
/>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="breadcrumbs">
|
||||
<ol>
|
||||
<li><a data-test-back href={{href-to 'dc.nspaces'}}>All Namespaces</a></li>
|
||||
|
@ -50,7 +43,13 @@ as |dc partition nspace item create|}}
|
|||
<BlockSlot @name="actions">
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{ partial 'dc/nspaces/form'}}
|
||||
<Consul::Nspace::Form
|
||||
@item={{item}}
|
||||
@dc={{route.params.dc}}
|
||||
@nspace={{route.params.nspace}}
|
||||
@partition={{route.params.partition}}
|
||||
@onsubmit={{transition-to 'dc.nspaces.index'}}
|
||||
/>
|
||||
</BlockSlot>
|
||||
</AppView>
|
||||
{{/let}}
|
|
@ -42,13 +42,6 @@ as |route|>
|
|||
as |sort filters items|}}
|
||||
|
||||
<AppView>
|
||||
<BlockSlot @name="notification" as |status type item error|>
|
||||
<Consul::Nspace::Notifications
|
||||
@type={{type}}
|
||||
@status={{status}}
|
||||
@error={{error}}
|
||||
/>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="header">
|
||||
<h1>
|
||||
<route.Title @title="Namespaces" />
|
||||
|
@ -72,6 +65,27 @@ as |route|>
|
|||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
<DataWriter
|
||||
@sink={{uri '/${partition}/${dc}/${nspace}/nspace/'
|
||||
(hash
|
||||
partition=route.params.partition
|
||||
nspace=route.params.nspace
|
||||
dc=route.params.dc
|
||||
)
|
||||
}}
|
||||
@type="nspace"
|
||||
@label="Namespace"
|
||||
@ondelete={{refresh-route}}
|
||||
as |writer|>
|
||||
<BlockSlot @name="removed" as |after|>
|
||||
<Consul::Nspace::Notifications
|
||||
{{notification
|
||||
after=(action after)
|
||||
}}
|
||||
@type="remove"
|
||||
/>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
<DataCollection
|
||||
@type="nspace"
|
||||
@sort={{sort.value}}
|
||||
|
@ -82,7 +96,7 @@ as |route|>
|
|||
<collection.Collection>
|
||||
<Consul::Nspace::List
|
||||
@items={{collection.items}}
|
||||
@ondelete={{route-action 'delete'}}
|
||||
@ondelete={{writer.delete}}
|
||||
/>
|
||||
</collection.Collection>
|
||||
<collection.Empty>
|
||||
|
@ -118,6 +132,8 @@ as |route|>
|
|||
</EmptyState>
|
||||
</collection.Empty>
|
||||
</DataCollection>
|
||||
</BlockSlot>
|
||||
</DataWriter>
|
||||
</BlockSlot>
|
||||
</AppView>
|
||||
{{/let}}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "consul-nspaces",
|
||||
"version": "0.1.0",
|
||||
"private": true
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
(routes => routes({
|
||||
dc: {
|
||||
nspaces: {
|
||||
_options: {
|
||||
path: '/namespaces',
|
||||
queryParams: {
|
||||
sortBy: 'sort',
|
||||
searchproperty: {
|
||||
as: 'searchproperty',
|
||||
empty: [['Name', 'Description', 'Role', 'Policy']],
|
||||
},
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
},
|
||||
abilities: ['read nspaces'],
|
||||
},
|
||||
edit: {
|
||||
_options: { path: '/:name' },
|
||||
},
|
||||
create: {
|
||||
_options: {
|
||||
template: 'dc/nspaces/edit',
|
||||
path: '/create',
|
||||
abilities: ['create nspaces'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}))(
|
||||
(json, data = document.currentScript.dataset) => {
|
||||
const appNameJS = data.appName.split('-')
|
||||
.map((item, i) => i ? `${item.substr(0, 1).toUpperCase()}${item.substr(1)}` : item)
|
||||
.join('');
|
||||
data[`${appNameJS}Routes`] = JSON.stringify(json);
|
||||
}
|
||||
);
|
|
@ -12,15 +12,25 @@
|
|||
)
|
||||
}}
|
||||
@type={{'partition'}}
|
||||
@label={{label}}
|
||||
@label={{'Admin Partition'}}
|
||||
@ondelete={{fn (if @ondelete @ondelete @onsubmit) @item}}
|
||||
@onchange={{fn (optional @onsubmit) @item}}
|
||||
as |writer|>
|
||||
<BlockSlot @name="removed" as |after|>
|
||||
<Consul::Partition::Notifications
|
||||
{{notification
|
||||
after=(action after)
|
||||
}}
|
||||
@type="remove"
|
||||
/>
|
||||
</BlockSlot>
|
||||
|
||||
<BlockSlot @name="content">
|
||||
|
||||
{{#let
|
||||
|
||||
(not (can "write partition"))
|
||||
|
||||
@item
|
||||
|
||||
(hash
|
||||
|
@ -37,19 +47,18 @@
|
|||
Description=(array)
|
||||
)
|
||||
|
||||
as |item Name Description|}}
|
||||
|
||||
as |readOnly item Name Description|}}
|
||||
<form
|
||||
{{on 'submit' (fn writer.persist item)}}
|
||||
{{disabled (not (can "write partition" item=item))}}
|
||||
{{disabled readOnly}}
|
||||
>
|
||||
|
||||
<StateChart
|
||||
@src={{state-chart 'validate'}}
|
||||
as |State Guard Action dispatch state|>
|
||||
as |State Guard ChartAction dispatch state|>
|
||||
|
||||
<fieldset>
|
||||
{{#if (is "new partition" item=item)}}
|
||||
{{#if (is "new partition" item=item)}}
|
||||
<TextInput
|
||||
@name="Name"
|
||||
@placeholder="Name"
|
||||
|
@ -76,34 +85,33 @@ as |State Guard Action dispatch state|>
|
|||
|
||||
<div>
|
||||
{{#if (and (is "new partition" item=item) (can "create partitions")) }}
|
||||
<button
|
||||
type="submit"
|
||||
<Action
|
||||
@type="submit"
|
||||
{{disabled (or (is "pristine partition" item=item) (state-matches state "error"))}}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
{{else if (can "write partition" item=item)}}
|
||||
<button type="submit">Save</button>
|
||||
</Action>
|
||||
{{else if (not readOnly)}}
|
||||
<Action @type="submit">Save</Action>
|
||||
{{/if}}
|
||||
|
||||
<button
|
||||
type="reset"
|
||||
<Action
|
||||
@type="reset"
|
||||
{{on 'click' (if @oncancel (fn @oncancel item) (fn @onsubmit item))}}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</Action>
|
||||
|
||||
{{#if (and (not (is "new partition" item=item)) (can "delete partition" item=item))}}
|
||||
<ConfirmationDialog @message="Are you sure you want to delete this Partition?">
|
||||
<BlockSlot @name="action" as |confirm|>
|
||||
<button
|
||||
<Action
|
||||
data-test-delete
|
||||
type="button"
|
||||
class="type-delete"
|
||||
{{on 'click' (fn confirm (fn writer.delete item))}}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</Action>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="dialog" as |execute cancel message|>
|
||||
<DeleteConfirmation
|
||||
|
|
|
@ -17,16 +17,15 @@ Please note:
|
|||
```
|
||||
|
||||
|
||||
### Arguments
|
||||
## Arguments
|
||||
|
||||
| Argument/Attribute | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `items` | `array` | | An array of Partitions |
|
||||
| `ondelete` | `function` | | An action to execute when the `Delete` action is clicked |
|
||||
|
||||
### See
|
||||
## See
|
||||
|
||||
- [Component Source Code](./index.js)
|
||||
- [Template Source Code](./index.hbs)
|
||||
|
||||
---
|
||||
|
|
|
@ -1,40 +1,13 @@
|
|||
# Consul::Partition::Notifications
|
||||
|
||||
A Notification component specifically for Partitions (at some point will be replaced with just using `ember-intl`/`t`.
|
||||
A Notification component specifically for Partitions. This is only a component as we currently use this in two places and if we need to add more types we can do so in one place.
|
||||
|
||||
We currently one have one 'remove' type due to the fact that Admin Partions can't use the default 'delete' notification as they get 'marked for deletion' instead.
|
||||
|
||||
```hbs preview-template
|
||||
<figure>
|
||||
<figcaption>Provide a widget to change the <code>@type</code></figcaption>
|
||||
|
||||
<select
|
||||
{{on 'change' (action (mut this.type) value="target.value")}}
|
||||
>
|
||||
<option>create</option>
|
||||
<option>update</option>
|
||||
<option>delete</option>
|
||||
</select>
|
||||
</figure>
|
||||
<figure>
|
||||
<figcaption>Provide a widget to change the <code>@status</code></figcaption>
|
||||
|
||||
<select
|
||||
{{on 'change' (action (mut this.success) value="target.value")}}
|
||||
>
|
||||
<option>success</option>
|
||||
<option>error</option>
|
||||
</select>
|
||||
</figure>
|
||||
<figure>
|
||||
<figcaption>Show the notification text</figcaption>
|
||||
<p>
|
||||
<Consul::Partition::Notifications
|
||||
@type={{or this.type 'create'}}
|
||||
@status={{or this.success 'success'}}
|
||||
@error={{undefined}}
|
||||
/>
|
||||
</p>
|
||||
</figure>
|
||||
|
||||
<Consul::Partition::Notifications
|
||||
@type={{'remove'}}
|
||||
/>
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -1,24 +1,16 @@
|
|||
{{#if (eq @type 'create')}}
|
||||
{{#if (eq @status 'success') }}
|
||||
Your partition has been added.
|
||||
{{else}}
|
||||
There was an error adding your partition.
|
||||
{{/if}}
|
||||
{{else if (eq @type 'update') }}
|
||||
{{#if (eq @status 'success') }}
|
||||
Your partition has been saved.
|
||||
{{else}}
|
||||
There was an error saving your partition.
|
||||
{{/if}}
|
||||
{{ else if (eq @type 'delete')}}
|
||||
{{#if (eq @status 'success') }}
|
||||
Your partition has been marked for deletion.
|
||||
{{else}}
|
||||
There was an error deleting your partition.
|
||||
{{/if}}
|
||||
{{#if (eq @type 'remove')}}
|
||||
<Notice
|
||||
class="notification-delete"
|
||||
@type="success"
|
||||
...attributes
|
||||
as |notice|>
|
||||
<notice.Header>
|
||||
<strong>Success!</strong>
|
||||
</notice.Header>
|
||||
<notice.Body>
|
||||
<p>
|
||||
Your Admin Partition has been marked for deletion.
|
||||
</p>
|
||||
</notice.Body>
|
||||
</Notice>
|
||||
{{/if}}
|
||||
{{#let @error.errors.firstObject as |error|}}
|
||||
{{#if error.detail }}
|
||||
<br />{{concat '(' (if error.status (concat error.status ': ')) error.detail ')'}}
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
|
|
|
@ -27,28 +27,24 @@ as |route|>
|
|||
route.params.nspace
|
||||
|
||||
loader.data
|
||||
loader.data.isNew
|
||||
as |dc partition nspace item create|}}
|
||||
as |dc partition nspace item|}}
|
||||
<AppView>
|
||||
<BlockSlot @name="notification" as |status type item error|>
|
||||
<Consul::Partition::Notifications
|
||||
@type={{type}}
|
||||
@status={{status}}
|
||||
@error={{error}}
|
||||
/>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="breadcrumbs">
|
||||
<ol>
|
||||
<li><a data-test-back href={{href-to 'dc.partitions'}}>All Partitions</a></li>
|
||||
<li><a data-test-back href={{href-to 'dc.partitions'}}>All Admin Partitions</a></li>
|
||||
</ol>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="header">
|
||||
<h1>
|
||||
<route.Title @title={{if create "New Partition" (concat "Edit " item.Name)}} />
|
||||
<route.Title
|
||||
@title={{if
|
||||
(is "new partition" item=item)
|
||||
"New Admin Partition"
|
||||
(concat "Edit " item.Name)
|
||||
}}
|
||||
/>
|
||||
</h1>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
|
||||
<Consul::Partition::Form
|
||||
|
@ -59,7 +55,6 @@ as |dc partition nspace item create|}}
|
|||
@onsubmit={{transition-to 'dc.partitions.index'}}
|
||||
/>
|
||||
|
||||
|
||||
</BlockSlot>
|
||||
</AppView>
|
||||
{{/let}}
|
||||
|
|
|
@ -42,13 +42,6 @@ as |route|>
|
|||
as |sort filters items|}}
|
||||
|
||||
<AppView>
|
||||
<BlockSlot @name="notification" as |status type item error|>
|
||||
<Consul::Partition::Notifications
|
||||
@type={{type}}
|
||||
@status={{status}}
|
||||
@error={{error}}
|
||||
/>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="header">
|
||||
<h1>
|
||||
<route.Title @title="Admin Partitions" />
|
||||
|
@ -79,8 +72,17 @@ as |route|>
|
|||
)
|
||||
}}
|
||||
@type="partition"
|
||||
@label="Admin Partition"
|
||||
@ondelete={{refresh-route}}
|
||||
as |writer|>
|
||||
<BlockSlot @name="removed" as |after|>
|
||||
<Consul::Partition::Notifications
|
||||
{{notification
|
||||
after=(action after)
|
||||
}}
|
||||
@type="remove"
|
||||
/>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
<DataCollection
|
||||
@type="nspace"
|
||||
|
|
|
@ -91,6 +91,12 @@ module.exports = {
|
|||
pattern: '**/README.mdx',
|
||||
urlSchema: 'auto',
|
||||
urlPrefix: 'docs/consul-partitions',
|
||||
},
|
||||
{
|
||||
root: `${path.dirname(require.resolve('consul-nspaces/package.json'))}/app/components`,
|
||||
pattern: '**/README.mdx',
|
||||
urlSchema: 'auto',
|
||||
urlPrefix: 'docs/consul-nspaces',
|
||||
}
|
||||
].concat(user.sources),
|
||||
labels: {
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
{{#if (eq @type 'create')}}
|
||||
{{#if (eq @status 'success') }}
|
||||
Your namespace has been added.
|
||||
{{else}}
|
||||
There was an error adding your namespace.
|
||||
{{/if}}
|
||||
{{else if (eq @type 'update') }}
|
||||
{{#if (eq @status 'success') }}
|
||||
Your namespace has been saved.
|
||||
{{else}}
|
||||
There was an error saving your namespace.
|
||||
{{/if}}
|
||||
{{ else if (eq @type 'delete')}}
|
||||
{{#if (eq @status 'success') }}
|
||||
Your namespace has been marked for deletion.
|
||||
{{else}}
|
||||
There was an error deleting your namespace.
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{#let @error.errors.firstObject as |error|}}
|
||||
{{#if error.detail }}
|
||||
<br />{{concat '(' (if error.status (concat error.status ': ')) error.detail ')'}}
|
||||
{{/if}}
|
||||
{{/let}}
|
|
@ -60,13 +60,6 @@
|
|||
@item={{flash.item}}
|
||||
@error={{flash.error}}
|
||||
/>
|
||||
{{else if (eq flash.model 'nspace')}}
|
||||
<Consul::Nspace::Notifications
|
||||
@type={{type}}
|
||||
@status={{status}}
|
||||
@item={{flash.item}}
|
||||
@error={{flash.error}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</p>
|
||||
|
@ -140,60 +133,13 @@
|
|||
@partitions={{this.partitions}}
|
||||
@onchange={{action (mut this.partitions) value="data"}}
|
||||
/>
|
||||
{{#if (can "choose nspaces")}}
|
||||
<li
|
||||
class="nspaces"
|
||||
data-test-nspace-menu
|
||||
>
|
||||
<PopoverMenu
|
||||
aria-label="Namespace"
|
||||
@position="left"
|
||||
as |components api|>
|
||||
<BlockSlot @name="trigger">
|
||||
{{@nspace}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="menu">
|
||||
{{#let components.MenuItem components.MenuSeparator as |MenuItem MenuSeparator|}}
|
||||
<DataSource
|
||||
@src={{uri
|
||||
'/${partition}/*/${dc}/namespaces'
|
||||
(hash
|
||||
partition=@partition
|
||||
dc=@dc.Name
|
||||
)
|
||||
}}
|
||||
@onchange={{action (mut this.nspaces) value="data"}}
|
||||
@loading="lazy"
|
||||
/>
|
||||
{{#each (reject-by 'DeletedAt' this.nspaces) as |item|}}
|
||||
<MenuItem
|
||||
class={{if (eq @nspace item.Name) 'is-active'}}
|
||||
@href={{href-to '.' params=(hash
|
||||
partition=(if (gt @partition.length 0) @partition undefined)
|
||||
nspace=item.Name
|
||||
)}}
|
||||
>
|
||||
<BlockSlot @name="label">
|
||||
{{item.Name}}
|
||||
</BlockSlot>
|
||||
</MenuItem>
|
||||
{{/each}}
|
||||
{{#if (can 'manage nspaces')}}
|
||||
<MenuSeparator />
|
||||
<MenuItem
|
||||
data-test-main-nav-nspaces
|
||||
@href={{href-to 'dc.nspaces' @dc.Name}}
|
||||
>
|
||||
<BlockSlot @name="label">
|
||||
Manage Namespaces
|
||||
</BlockSlot>
|
||||
</MenuItem>
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverMenu>
|
||||
</li>
|
||||
{{/if}}
|
||||
<Consul::Nspace::Selector
|
||||
@dc={{@dc}}
|
||||
@partition={{@partition}}
|
||||
@nspace={{@nspace}}
|
||||
@nspaces={{this.nspaces}}
|
||||
@onchange={{action (mut this.nspaces) value="data"}}
|
||||
/>
|
||||
{{#if (can "read services")}}
|
||||
<li data-test-main-nav-services class={{if (is-href 'dc.services' @dc.Name) 'is-active'}}>
|
||||
<a href={{href-to 'dc.services' @dc.Name}}>Services</a>
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
import Controller from './edit';
|
||||
export default class CreateController extends Controller {}
|
|
@ -1,24 +0,0 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
export default Controller.extend({
|
||||
dom: service('dom'),
|
||||
builder: service('form'),
|
||||
init: function() {
|
||||
this._super(...arguments);
|
||||
this.form = this.builder.form('nspace');
|
||||
},
|
||||
actions: {
|
||||
change: function(e, value, item) {
|
||||
const event = this.dom.normalizeEvent(e, value);
|
||||
try {
|
||||
this.form.handleEvent(event);
|
||||
} catch (err) {
|
||||
const target = event.target;
|
||||
switch (target.name) {
|
||||
default:
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
import validations from 'consul-ui/validations/nspace';
|
||||
import builderFactory from 'consul-ui/utils/form/builder';
|
||||
const builder = builderFactory();
|
||||
export default function(container, name = '', v = validations, form = builder) {
|
||||
return form(name, {})
|
||||
.setValidators(v)
|
||||
.add(container.form('policy'))
|
||||
.add(container.form('role'));
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import validations from 'consul-ui/validations/nspace';
|
||||
import builderFactory from 'consul-ui/utils/form/builder';
|
||||
const builder = builderFactory();
|
||||
export default function(container, name = '', v = validations, form = builder) {
|
||||
return form(name, {})
|
||||
.setValidators(v);
|
||||
}
|
|
@ -231,23 +231,6 @@ export const routes = merge.all(
|
|||
)
|
||||
);
|
||||
|
||||
if (env('CONSUL_NSPACES_ENABLED')) {
|
||||
routes.dc.nspaces = {
|
||||
_options: {
|
||||
path: '/namespaces',
|
||||
abilities: ['read nspaces'],
|
||||
},
|
||||
edit: {
|
||||
_options: { path: '/:name' },
|
||||
},
|
||||
create: {
|
||||
_options: {
|
||||
path: '/create',
|
||||
abilities: ['create nspaces'],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
runInDebug(() => {
|
||||
// check to see if we are running docfy and if so add its routes to our
|
||||
// route config
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
import Route from './edit';
|
||||
|
||||
export default class CreateRoute extends Route {
|
||||
templateName = 'dc/nspaces/edit';
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
import { inject as service } from '@ember/service';
|
||||
import Route from 'consul-ui/routing/route';
|
||||
|
||||
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
||||
|
||||
export default class EditRoute extends Route.extend(WithBlockingActions) {
|
||||
@service('repository/nspace') repo;
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
import { inject as service } from '@ember/service';
|
||||
import Route from 'consul-ui/routing/route';
|
||||
|
||||
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
||||
export default class IndexRoute extends Route.extend(WithBlockingActions) {
|
||||
@service('repository/nspace') repo;
|
||||
|
||||
queryParams = {
|
||||
sortBy: 'sort',
|
||||
searchproperty: {
|
||||
as: 'searchproperty',
|
||||
empty: [['Name', 'Description', 'Role', 'Policy']],
|
||||
},
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
};
|
||||
}
|
|
@ -22,11 +22,14 @@ export default class BaseRoute extends Route {
|
|||
.filter(item => item !== 'index')
|
||||
.join('.');
|
||||
const template = get(routes, `${routeName}._options.template`);
|
||||
if(template) {
|
||||
if (template) {
|
||||
this.templateName = template;
|
||||
}
|
||||
const queryParams = get(routes, `${routeName}._options.queryParams`);
|
||||
if(queryParams && (this.routeName === 'dc.partitions.index' || this.routeName === 'oauth-provider-debug')) {
|
||||
if (
|
||||
queryParams &&
|
||||
['dc.partitions.index', 'dc.nspaces.index', 'oauth-provider-debug'].includes(this.routeName)
|
||||
) {
|
||||
this.queryParams = queryParams;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ export default class NspaceSerializer extends Serializer {
|
|||
cb(
|
||||
headers,
|
||||
body.map(function(item) {
|
||||
item.Namespace = item.Name;
|
||||
item.Namespace = '*';
|
||||
item.Datacenter = query.dc;
|
||||
if (get(item, 'ACLs.PolicyDefaults')) {
|
||||
item.ACLs.PolicyDefaults = item.ACLs.PolicyDefaults.map(function(item) {
|
||||
|
@ -45,7 +45,7 @@ export default class NspaceSerializer extends Serializer {
|
|||
cb =>
|
||||
respond((headers, body) => {
|
||||
body.Datacenter = serialized.dc;
|
||||
body.Namespace = body.Name;
|
||||
body.Namespace = '*';
|
||||
return cb(headers, body);
|
||||
}),
|
||||
serialized,
|
||||
|
@ -58,7 +58,7 @@ export default class NspaceSerializer extends Serializer {
|
|||
cb =>
|
||||
respond((headers, body) => {
|
||||
body.Datacenter = serialized.dc;
|
||||
body.Namespace = body.Name;
|
||||
body.Namespace = '*';
|
||||
return cb(headers, body);
|
||||
}),
|
||||
serialized,
|
||||
|
|
|
@ -12,8 +12,8 @@ export default class PartitionSerializer extends Serializer {
|
|||
return cb(
|
||||
headers,
|
||||
body.Partitions.map(item => {
|
||||
item.Partition = item.Name;
|
||||
item.Namespace = '';
|
||||
item.Partition = '*';
|
||||
item.Namespace = '*';
|
||||
return item;
|
||||
})
|
||||
);
|
||||
|
|
|
@ -5,6 +5,7 @@ export default class HttpService extends Service {
|
|||
@service('settings') settings;
|
||||
@service('repository/intention') intention;
|
||||
@service('repository/kv') kv;
|
||||
@service('repository/nspace') nspace;
|
||||
@service('repository/partition') partition;
|
||||
@service('repository/session') session;
|
||||
|
||||
|
|
|
@ -6,8 +6,6 @@ import token from 'consul-ui/forms/token';
|
|||
import policy from 'consul-ui/forms/policy';
|
||||
import role from 'consul-ui/forms/role';
|
||||
import intention from 'consul-ui/forms/intention';
|
||||
import nspace from 'consul-ui/forms/nspace';
|
||||
import partition from 'consul-ui/forms/partition';
|
||||
|
||||
const builder = builderFactory();
|
||||
|
||||
|
@ -17,8 +15,6 @@ const forms = {
|
|||
policy: policy,
|
||||
role: role,
|
||||
intention: intention,
|
||||
nspace: nspace,
|
||||
partition: partition,
|
||||
};
|
||||
|
||||
export default class FormService extends Service {
|
||||
|
|
|
@ -4,6 +4,8 @@ import RepositoryService, { softDelete } from 'consul-ui/services/repository';
|
|||
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/nspace';
|
||||
import dataSource from 'consul-ui/decorators/data-source';
|
||||
|
||||
import { defaultChangeset as changeset } from 'consul-ui/utils/form/builder';
|
||||
|
||||
const findActiveNspace = function(nspaces, nspace) {
|
||||
let found = nspaces.find(function(item) {
|
||||
return item.Name === nspace.Name;
|
||||
|
@ -24,7 +26,7 @@ const findActiveNspace = function(nspaces, nspace) {
|
|||
return found;
|
||||
};
|
||||
const modelName = 'nspace';
|
||||
export default class NspaceEnabledService extends RepositoryService {
|
||||
export default class NspaceService extends RepositoryService {
|
||||
@service('router') router;
|
||||
@service('container') container;
|
||||
@service('env') env;
|
||||
|
@ -68,10 +70,7 @@ export default class NspaceEnabledService extends RepositoryService {
|
|||
} else {
|
||||
item = await super.findBySlug(...arguments);
|
||||
}
|
||||
return this.form
|
||||
.form(this.getModelName())
|
||||
.setData(item)
|
||||
.getData();
|
||||
return changeset(item);
|
||||
}
|
||||
|
||||
remove(item) {
|
||||
|
|
|
@ -4,6 +4,8 @@ import RepositoryService, { softDelete } from 'consul-ui/services/repository';
|
|||
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/partition';
|
||||
import dataSource from 'consul-ui/decorators/data-source';
|
||||
|
||||
import { defaultChangeset as changeset } from 'consul-ui/utils/form/builder';
|
||||
|
||||
const findActive = function(items, item) {
|
||||
let found = items.find(function(i) {
|
||||
return i.Name === item.Name;
|
||||
|
@ -61,10 +63,7 @@ export default class PartitionRepository extends RepositoryService {
|
|||
} else {
|
||||
item = await super.findBySlug(...arguments);
|
||||
}
|
||||
return this.form
|
||||
.form(this.getModelName())
|
||||
.setData(item)
|
||||
.getData();
|
||||
return changeset(item);
|
||||
}
|
||||
|
||||
remove(item) {
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
<form>
|
||||
<fieldset
|
||||
disabled={{if (not (can "write nspace" item=item)) "disabled"}}
|
||||
>
|
||||
{{#if create }}
|
||||
<label class="type-text{{if item.error.Name ' has-error'}}">
|
||||
<span>Name</span>
|
||||
<input autofocus="autofocus" type="text" value={{item.Name}} name="Name" oninput={{action 'change'}} placeholder="Name" />
|
||||
<em>
|
||||
Must be a valid DNS hostname. Must contain 1-64 characters (numbers, letters, and hyphens), and must begin with a letter. Once created, this cannot be changed.
|
||||
</em>
|
||||
{{#if item.error.Name}}
|
||||
<strong>{{item.error.Name.validation}}</strong>
|
||||
{{/if}}
|
||||
</label>
|
||||
{{/if}}
|
||||
<label class="type-text validate-optional">
|
||||
<span>Description (Optional)</span>
|
||||
<textarea name="Description" oninput={{action 'change'}}>{{item.Description}}</textarea>
|
||||
</label>
|
||||
</fieldset>
|
||||
{{#if (env 'CONSUL_ACLS_ENABLED')}}
|
||||
<fieldset id="roles">
|
||||
<h2>Roles</h2>
|
||||
<p>
|
||||
{{#if (can "write nspace" item=item)}}
|
||||
By adding roles to this namespaces, you will apply them to all tokens created within this namespace.
|
||||
{{else}}
|
||||
The following roles are applied to all tokens created within this namespace.
|
||||
{{/if}}
|
||||
</p>
|
||||
<RoleSelector
|
||||
@disabled={{not (can "write nspace" item=item)}}
|
||||
@dc={{dc}}
|
||||
@nspace="default"
|
||||
@partition={{partition}}
|
||||
@items={{item.ACLs.RoleDefaults}}
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset id="policies">
|
||||
<h2>Policies</h2>
|
||||
<p>
|
||||
{{#if (can "write nspace" item=item)}}
|
||||
By adding policies to this namespaces, you will apply them to all tokens created within this namespace.
|
||||
{{else}}
|
||||
The following policies are applied to all tokens created within this namespace.
|
||||
{{/if}}
|
||||
</p>
|
||||
<PolicySelector
|
||||
@disabled={{not (can "write nspace" item=item)}}
|
||||
@dc={{dc}}
|
||||
@nspace="default"
|
||||
@partition={{partition}}
|
||||
@allowIdentity={{false}}
|
||||
@items={{item.ACLs.PolicyDefaults}}
|
||||
/>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
<div>
|
||||
{{#if (and create (can "create nspaces")) }}
|
||||
<button type="submit" {{ action "create" item}} disabled={{if (or item.isPristine item.isInvalid) 'disabled'}}>Save</button>
|
||||
{{else}}
|
||||
{{#if (can "write nspace" item=item)}}
|
||||
<button type="submit" {{ action "update" item}} disabled={{if item.isInvalid 'disabled'}}>Save</button>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
<button type="reset" {{ action "cancel" item}}>Cancel</button>
|
||||
{{# if (and (not create) (can "delete nspace" item=item) ) }}
|
||||
<ConfirmationDialog @message="Are you sure you want to delete this Namespace?">
|
||||
<BlockSlot @name="action" as |confirm|>
|
||||
<button data-test-delete type="button" class="type-delete" {{action confirm 'delete' item parent}}>Delete</button>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="dialog" as |execute cancel message|>
|
||||
<DeleteConfirmation @message={{message}} @execute={{execute}} @cancel={{cancel}} />
|
||||
</BlockSlot>
|
||||
</ConfirmationDialog>
|
||||
{{/if}}
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -1,6 +1,16 @@
|
|||
{{page-title 'Engineering Docs - Consul' separator=' - '}}
|
||||
|
||||
{{document-attrs class="is-debug"}}
|
||||
{{! Tell CSS what we have enabled }}
|
||||
{{#if (can "use acls")}}
|
||||
{{document-attrs class="has-acls"}}
|
||||
{{/if}}
|
||||
{{#if (can "use nspaces")}}
|
||||
{{document-attrs class="has-nspaces"}}
|
||||
{{/if}}
|
||||
{{#if (can "use partitions")}}
|
||||
{{document-attrs class="has-partitions"}}
|
||||
{{/if}}
|
||||
<App class="docs" id="wrapper">
|
||||
|
||||
<:notifications as |app|>
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
<Route
|
||||
@name={{routeName}}
|
||||
as |route|>
|
||||
<Outlet
|
||||
@name={{routeName}}
|
||||
@model={{route.model}}
|
||||
as |o|>
|
||||
{{outlet}}
|
||||
</Outlet>
|
||||
</Route>
|
|
@ -6,7 +6,7 @@ import lookupValidator from 'ember-changeset-validations';
|
|||
// Keep these here for now so forms are easy to make
|
||||
// TODO: Probably move this to utils/form/parse-element-name
|
||||
import parseElementName from 'consul-ui/utils/get-form-name-property';
|
||||
const defaultChangeset = function(data, validators) {
|
||||
export const defaultChangeset = function(data, validators) {
|
||||
return createChangeset(data, lookupValidator(validators), validators, { changeset: Changeset });
|
||||
};
|
||||
/**
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
import { validateFormat } from 'ember-changeset-validations/validators';
|
||||
export default {
|
||||
Name: validateFormat({ regex: /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,62}[a-zA-Z0-9])?$/ }),
|
||||
};
|
|
@ -28,7 +28,8 @@ module.exports = function(defaults, $ = process.env) {
|
|||
|
||||
const apps = [
|
||||
'consul-acls',
|
||||
'consul-partitions'
|
||||
'consul-partitions',
|
||||
'consul-nspaces'
|
||||
].map(item => {
|
||||
return {
|
||||
name: item,
|
||||
|
|
|
@ -54,6 +54,11 @@ module.exports = {
|
|||
environment: config.environment,
|
||||
rootURL: config.environment === 'production' ? '{{.ContentPath}}' : config.rootURL,
|
||||
config: config,
|
||||
env: function(key) {
|
||||
if (process.env[key]) {
|
||||
return process.env[key];
|
||||
}
|
||||
},
|
||||
};
|
||||
switch (type) {
|
||||
case 'head':
|
||||
|
|
|
@ -15,7 +15,7 @@ const hbs = (path, attrs = {}) =>
|
|||
const BrandLoader = attrs => hbs('brand-loader/index.hbs', attrs);
|
||||
const Enterprise = attrs => hbs('brand-loader/enterprise.hbs', attrs);
|
||||
|
||||
module.exports = ({ appName, environment, rootURL, config }) => `
|
||||
module.exports = ({ appName, environment, rootURL, config, env }) => `
|
||||
<noscript>
|
||||
<div style="margin: 0 auto;">
|
||||
<h2>JavaScript Required</h2>
|
||||
|
@ -47,7 +47,9 @@ ${
|
|||
? `
|
||||
<script data-app-name="${appName}" data-${appName}-services src="${rootURL}assets/consul-ui/services-debug.js"></script>
|
||||
<script data-app-name="${appName}" data-${appName}-routing src="${rootURL}assets/consul-ui/routes-debug.js"></script>
|
||||
` : ``}
|
||||
`
|
||||
: ``
|
||||
}
|
||||
${
|
||||
environment === 'production'
|
||||
? `
|
||||
|
@ -57,13 +59,18 @@ ${
|
|||
{{if .PartitionsEnabled}}
|
||||
<script data-app-name="${appName}" data-${appName}-routing src="${rootURL}assets/consul-partitions/routes.js"></script>
|
||||
{{end}}
|
||||
{{if .NamespacesEnabled}}
|
||||
<script data-app-name="${appName}" data-${appName}-routing src="${rootURL}assets/consul-nspaces/routes.js"></script>
|
||||
{{end}}
|
||||
`
|
||||
: `
|
||||
<script>
|
||||
(
|
||||
function(get, obj) {
|
||||
Object.entries(obj).forEach(([key, value]) => {
|
||||
if(get(key)) {
|
||||
if(get(key) || (key === 'CONSUL_NSPACES_ENABLE' && ${
|
||||
env('CONSUL_NSPACES_ENABLED') === '1' ? `true` : `false`
|
||||
})) {
|
||||
document.write(\`\\x3Cscript data-app-name="${appName}" data-${appName}-routing src="${rootURL}assets/\${value}/routes.js">\\x3C/script>\`);
|
||||
}
|
||||
});
|
||||
|
@ -72,7 +79,8 @@ ${
|
|||
key => document.cookie.split('; ').find(item => item.startsWith(\`\${key}=\`)),
|
||||
{
|
||||
'CONSUL_ACLS_ENABLE': 'consul-acls',
|
||||
'CONSUL_PARTITIONS_ENABLE': 'consul-partitions'
|
||||
'CONSUL_PARTITIONS_ENABLE': 'consul-partitions',
|
||||
'CONSUL_NSPACES_ENABLE': 'consul-nspaces'
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
"clipboard": "^2.0.4",
|
||||
"consul-acls": "*",
|
||||
"consul-partitions": "*",
|
||||
"consul-nspaces": "*",
|
||||
"css.escape": "^1.5.1",
|
||||
"d3-array": "^2.8.0",
|
||||
"d3-scale": "^3.2.3",
|
||||
|
|
|
@ -26,17 +26,18 @@ Feature: dc / nspaces / delete: Deleting items with confirmations, success and e
|
|||
| Edit | Listing | Method | URL | Data |
|
||||
| nspace | nspaces | DELETE | /v1/namespace/a-namespace?dc=datacenter | {"Name": "a-namespace"} |
|
||||
--------------------------------------------------------------------------------------------------------
|
||||
Scenario: Deleting a [Model] from the [Model] detail page
|
||||
When I visit the [Model] page for yaml
|
||||
Scenario: Deleting a nspace from the nspace detail page with success
|
||||
When I visit the nspace page for yaml
|
||||
---
|
||||
dc: datacenter
|
||||
[Slug]
|
||||
namespace: a-namespace
|
||||
---
|
||||
And I click delete
|
||||
And I click confirmDelete
|
||||
Then a [Method] request was made to "[URL]"
|
||||
Then a DELETE request was made to "/v1/namespace/a-namespace?dc=datacenter"
|
||||
And "[data-notification]" has the "notification-delete" class
|
||||
And "[data-notification]" has the "success" class
|
||||
Scenario: Deleting a [Model] from the [Model] detail page with error
|
||||
When I visit the [Model] page for yaml
|
||||
---
|
||||
dc: datacenter
|
||||
|
@ -45,7 +46,7 @@ Feature: dc / nspaces / delete: Deleting items with confirmations, success and e
|
|||
Given the url "[URL]" responds with a 500 status
|
||||
And I click delete
|
||||
And I click confirmDelete
|
||||
And "[data-notification]" has the "notification-delete" class
|
||||
And "[data-notification]" has the "notification-update" class
|
||||
And "[data-notification]" has the "error" class
|
||||
Where:
|
||||
-------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -24,8 +24,8 @@ module('Integration | Serializer | nspace', function(hooks) {
|
|||
Object.assign({}, item, {
|
||||
Datacenter: dc,
|
||||
Partition: item.Partition || undefinedPartition,
|
||||
Namespace: item.Name,
|
||||
uid: `["${item.Partition}","${item.Name}","${dc}","${item.Name}"]`,
|
||||
Namespace: '*',
|
||||
uid: `["${item.Partition}","*","${dc}","${item.Name}"]`,
|
||||
})
|
||||
);
|
||||
const actual = serializer.respondForQuery(
|
||||
|
|
|
@ -16,9 +16,9 @@ module('Integration | Serializer | partition', function(hooks) {
|
|||
const expected = payload.Partitions.map(item =>
|
||||
Object.assign({}, item, {
|
||||
Datacenter: dc,
|
||||
Namespace: '',
|
||||
Partition: item.Name,
|
||||
uid: `["${item.Name}","","${dc}","${item.Name}"]`,
|
||||
Namespace: '*',
|
||||
Partition: '*',
|
||||
uid: `["*","*","${dc}","${item.Name}"]`,
|
||||
})
|
||||
);
|
||||
const actual = serializer.respondForQuery(
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Controller | dc/nspaces/create', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let controller = this.owner.lookup('controller:dc/nspaces/create');
|
||||
assert.ok(controller);
|
||||
});
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Controller | dc/nspaces/edit', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let controller = this.owner.lookup('controller:dc/nspaces/edit');
|
||||
assert.ok(controller);
|
||||
});
|
||||
});
|
|
@ -1,11 +0,0 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Route | dc/nspaces/create', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it exists', function(assert) {
|
||||
let route = this.owner.lookup('route:dc/nspaces/create');
|
||||
assert.ok(route);
|
||||
});
|
||||
});
|
|
@ -1,11 +0,0 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Route | dc/nspaces/edit', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it exists', function(assert) {
|
||||
let route = this.owner.lookup('route:dc/nspaces/edit');
|
||||
assert.ok(route);
|
||||
});
|
||||
});
|
|
@ -1,11 +0,0 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Route | dc/nspaces/index', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it exists', function(assert) {
|
||||
let route = this.owner.lookup('route:dc/nspaces/index');
|
||||
assert.ok(route);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue