UI: Move legacy ACLs, KVs and Intentions to use `form` functionality (#4936)

Change legacy acls, kvs and intentions to use `form`s
This commit is contained in:
John Cowen 2018-11-19 14:53:08 +00:00 committed by John Cowen
parent 104e6bac71
commit 4368040a4a
19 changed files with 133 additions and 84 deletions

View File

@ -1,30 +1,31 @@
import Controller from '@ember/controller'; import Controller from '@ember/controller';
import { set } from '@ember/object'; import { inject as service } from '@ember/service';
import Changeset from 'ember-changeset'; import { get } from '@ember/object';
import validations from 'consul-ui/validations/acl';
import lookupValidator from 'ember-changeset-validations';
export default Controller.extend({ export default Controller.extend({
builder: service('form'),
dom: service('dom'),
init: function() {
this._super(...arguments);
this.form = get(this, 'builder').form('acl');
},
setProperties: function(model) { setProperties: function(model) {
this.changeset = new Changeset(model.item, lookupValidator(validations), validations); // essentially this replaces the data with changesets
this._super({ this._super(
...model, Object.keys(model).reduce((prev, key, i) => {
...{ switch (key) {
item: this.changeset, case 'item':
}, prev[key] = this.form.setData(prev[key]).getData();
}); break;
}
return prev;
}, model)
);
}, },
actions: { actions: {
change: function(e) { change: function(e, value, item) {
const target = e.target || { name: 'Rules', value: e }; const event = get(this, 'dom').normalizeEvent(e, value);
switch (target.name) { get(this, 'form').handleEvent(event);
case 'Type':
set(this.changeset, target.name, target.value);
break;
case 'Rules':
set(this, 'item.Rules', target.value);
break;
}
}, },
}, },
}); });

View File

@ -25,8 +25,8 @@ export default Controller.extend({
}, },
actions: { actions: {
change: function(e, value, item) { change: function(e, value, item) {
const form = get(this, 'form');
const event = get(this, 'dom').normalizeEvent(e, value); const event = get(this, 'dom').normalizeEvent(e, value);
const form = get(this, 'form');
try { try {
form.handleEvent(event); form.handleEvent(event);
} catch (err) { } catch (err) {

View File

@ -45,8 +45,8 @@ export default Controller.extend({
.didAppear(); .didAppear();
}, },
change: function(e, value, item) { change: function(e, value, item) {
const form = get(this, 'form');
const event = get(this, 'dom').normalizeEvent(e, value); const event = get(this, 'dom').normalizeEvent(e, value);
const form = get(this, 'form');
try { try {
form.handleEvent(event); form.handleEvent(event);
} catch (err) { } catch (err) {

View File

@ -1,13 +1,15 @@
import Controller from '@ember/controller'; import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { get, set } from '@ember/object'; import { get, set } from '@ember/object';
import Changeset from 'ember-changeset';
import lookupValidator from 'ember-changeset-validations';
import validations from 'consul-ui/validations/intention';
export default Controller.extend({ export default Controller.extend({
dom: service('dom'),
builder: service('form'),
init: function() {
this._super(...arguments);
this.form = get(this, 'builder').form('intention');
},
setProperties: function(model) { setProperties: function(model) {
this.changeset = new Changeset(model.item, lookupValidator(validations), validations);
const sourceName = get(model.item, 'SourceName'); const sourceName = get(model.item, 'SourceName');
const destinationName = get(model.item, 'DestinationName'); const destinationName = get(model.item, 'DestinationName');
let source = model.items.findBy('Name', sourceName); let source = model.items.findBy('Name', sourceName);
@ -23,7 +25,7 @@ export default Controller.extend({
this._super({ this._super({
...model, ...model,
...{ ...{
item: this.changeset, item: this.form.setData(model.item).getData(),
SourceName: source, SourceName: source,
DestinationName: destination, DestinationName: destination,
}, },
@ -36,37 +38,44 @@ export default Controller.extend({
isUnique: function(term) { isUnique: function(term) {
return !get(this, 'items').findBy('Name', term); return !get(this, 'items').findBy('Name', term);
}, },
change: function(e, value, _target) { change: function(e, value, item) {
// normalize back to standard event const event = get(this, 'dom').normalizeEvent(e, value);
const target = e.target || { ..._target, ...{ name: e, value: value } }; const form = get(this, 'form');
let name, selected; const target = event.target;
name = selected = target.value;
// TODO: let name;
// linter needs this here? let selected;
let match; let match;
switch (target.name) { switch (target.name) {
case 'Description':
case 'Action':
set(this.changeset, target.name, target.value);
break;
case 'SourceName': case 'SourceName':
case 'DestinationName': case 'DestinationName':
name = selected = target.value;
// Names can be selected Service EmberObjects or typed in strings
// if its not a string, use the `Name` from the Service EmberObject
if (typeof name !== 'string') { if (typeof name !== 'string') {
name = get(target.value, 'Name'); name = get(target.value, 'Name');
} }
// linter doesn't like const here // see if the name is already in the list
match = get(this, 'items').filterBy('Name', name); match = get(this, 'items').filterBy('Name', name);
if (match.length === 0) { if (match.length === 0) {
// if its not make a new 'fake' Service that doesn't exist yet
// and add it to the possible services to make an intention between
selected = { Name: name }; selected = { Name: name };
// linter doesn't mind const here?
const items = [selected].concat(this.items.toArray()); const items = [selected].concat(this.items.toArray());
set(this, 'items', items); set(this, 'items', items);
} }
set(this.changeset, target.name, name); // mutate the value with the string name
// which will be handled by the form
target.value = name;
// these are 'non-form' variables so not on `item`
// these variables also exist in the template so we know
// the current selection
// basically the difference between
// `item.DestinationName` and just `DestinationName`
set(this, target.name, selected); set(this, target.name, selected);
break; break;
} }
this.changeset.validate(); form.handleEvent(event);
}, },
}, },
}); });

View File

@ -2,41 +2,56 @@ import Controller from '@ember/controller';
import { get, set } from '@ember/object'; import { get, set } from '@ember/object';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import Changeset from 'ember-changeset';
import validations from 'consul-ui/validations/kv';
import lookupValidator from 'ember-changeset-validations';
export default Controller.extend({ export default Controller.extend({
json: true, dom: service('dom'),
builder: service('form'),
encoder: service('btoa'), encoder: service('btoa'),
json: true,
init: function() {
this._super(...arguments);
this.form = get(this, 'builder').form('kv');
},
setProperties: function(model) { setProperties: function(model) {
// TODO: Potentially save whether json has been clicked to the model, // essentially this replaces the data with changesets
// setting set(this, 'json', true) here will force the form to always default to code=on this._super(
// even if the user has selected code=off on another KV Object.keys(model).reduce((prev, key, i) => {
// ideally we would save the value per KV, but I'd like to not do that on the model switch (key) {
// a set(this, 'json', valueFromSomeStorageJustForThisKV) would be added here case 'item':
this.changeset = new Changeset(model.item, lookupValidator(validations), validations); prev[key] = this.form.setData(prev[key]).getData();
this._super({ break;
...model, }
...{ return prev;
item: this.changeset, }, model)
}, );
});
}, },
actions: { actions: {
change: function(e) { change: function(e, value, item) {
const target = e.target || { name: 'value', value: e }; const event = get(this, 'dom').normalizeEvent(e, value);
var parent; const form = get(this, 'form');
switch (target.name) { try {
case 'additional': form.handleEvent(event);
parent = get(this, 'parent.Key'); } catch (err) {
set(this.changeset, 'Key', `${parent !== '/' ? parent : ''}${target.value}`); const target = event.target;
break; let parent;
case 'json': switch (target.name) {
set(this, 'json', !get(this, 'json')); case 'value':
break; set(this.item, 'Value', get(this, 'encoder').execute(target.value));
case 'value': break;
set(this, 'item.Value', get(this, 'encoder').execute(target.value)); case 'additional':
break; parent = get(this, 'parent.Key');
set(this.item, 'Key', `${parent !== '/' ? parent : ''}${target.value}`);
break;
case 'json':
// TODO: Potentially save whether json has been clicked to the model,
// setting set(this, 'json', true) here will force the form to always default to code=on
// even if the user has selected code=off on another KV
// ideally we would save the value per KV, but I'd like to not do that on the model
// a set(this, 'json', valueFromSomeStorageJustForThisKV) would be added here
set(this, 'json', !get(this, 'json'));
break;
default:
throw err;
}
} }
}, },
}, },

6
ui-v2/app/forms/acl.js Normal file
View File

@ -0,0 +1,6 @@
import validations from 'consul-ui/validations/acl';
import builderFactory from 'consul-ui/utils/form/builder';
const builder = builderFactory();
export default function(name = '', v = validations, form = builder) {
return form(name, {}).setValidators(v);
}

View File

@ -0,0 +1,6 @@
import validations from 'consul-ui/validations/intention';
import builderFactory from 'consul-ui/utils/form/builder';
const builder = builderFactory();
export default function(name = '', v = validations, form = builder) {
return form(name, {}).setValidators(v);
}

6
ui-v2/app/forms/kv.js Normal file
View File

@ -0,0 +1,6 @@
import validations from 'consul-ui/validations/kv';
import builderFactory from 'consul-ui/utils/form/builder';
const builder = builderFactory();
export default function(name = '', v = validations, form = builder) {
return form(name, {}).setValidators(v);
}

View File

@ -1,6 +1,6 @@
import builderFactory from 'consul-ui/utils/form/builder';
import validations from 'consul-ui/validations/token'; import validations from 'consul-ui/validations/token';
import policy from 'consul-ui/forms/policy'; import policy from 'consul-ui/forms/policy';
import builderFactory from 'consul-ui/utils/form/builder';
const builder = builderFactory(); const builder = builderFactory();
export default function(name = '', v = validations, form = builder) { export default function(name = '', v = validations, form = builder) {
return form(name, {}) return form(name, {})

View File

@ -1,11 +1,17 @@
import kv from 'consul-ui/forms/kv';
import acl from 'consul-ui/forms/acl';
import token from 'consul-ui/forms/token'; import token from 'consul-ui/forms/token';
import policy from 'consul-ui/forms/policy'; import policy from 'consul-ui/forms/policy';
import intention from 'consul-ui/forms/intention';
export function initialize(application) { export function initialize(application) {
// Service-less injection using private properties at a per-project level // Service-less injection using private properties at a per-project level
const FormBuilder = application.resolveRegistration('service:form'); const FormBuilder = application.resolveRegistration('service:form');
const forms = { const forms = {
kv: kv(),
acl: acl(),
token: token(), token: token(),
policy: policy(), policy: policy(),
intention: intention(),
}; };
FormBuilder.reopen({ FormBuilder.reopen({
form: function(name) { form: function(name) {

View File

@ -14,7 +14,7 @@
</div> </div>
<label class="type-text"> <label class="type-text">
<span>Policy <a href="{{env 'CONSUL_DOCUMENTATION_URL'}}/guides/acl.html#rule-specification" rel="help noopener noreferrer" target="_blank">(HCL Format)</a></span> <span>Policy <a href="{{env 'CONSUL_DOCUMENTATION_URL'}}/guides/acl.html#rule-specification" rel="help noopener noreferrer" target="_blank">(HCL Format)</a></span>
{{code-editor class=(if item.error.Rules 'error') name='Rules' value=item.Rules syntax="hcl" onkeyup=(action 'change')}} {{code-editor class=(if item.error.Rules 'error') name='Rules' value=item.Rules syntax="hcl" onkeyup=(action 'change' 'Rules')}}
</label> </label>
{{#if create }} {{#if create }}
<label class="type-text"> <label class="type-text">

View File

@ -18,7 +18,7 @@
<label class="type-text{{if item.error.Value ' has-error'}}"> <label class="type-text{{if item.error.Value ' has-error'}}">
<span>Value</span> <span>Value</span>
{{#if json}} {{#if json}}
{{ code-editor value=(atob item.Value) onkeyup=(action 'change') }} {{code-editor value=(atob item.Value) onkeyup=(action 'change' 'value')}}
{{else}} {{else}}
<textarea autofocus={{not create}} name="value" oninput={{action 'change'}}>{{atob item.Value}}</textarea> <textarea autofocus={{not create}} name="value" oninput={{action 'change'}}>{{atob item.Value}}</textarea>
{{/if}} {{/if}}

View File

@ -2,7 +2,7 @@ import { moduleFor, test } from 'ember-qunit';
moduleFor('controller:dc/acls/create', 'Unit | Controller | dc/acls/create', { moduleFor('controller:dc/acls/create', 'Unit | Controller | dc/acls/create', {
// Specify the other units that are required for this test. // Specify the other units that are required for this test.
// needs: ['controller:foo'] needs: ['service:dom', 'service:form'],
}); });
// Replace this with your real tests. // Replace this with your real tests.

View File

@ -2,7 +2,7 @@ import { moduleFor, test } from 'ember-qunit';
moduleFor('controller:dc/acls/edit', 'Unit | Controller | dc/acls/edit', { moduleFor('controller:dc/acls/edit', 'Unit | Controller | dc/acls/edit', {
// Specify the other units that are required for this test. // Specify the other units that are required for this test.
// needs: ['controller:foo'] needs: ['service:dom', 'service:form'],
}); });
// Replace this with your real tests. // Replace this with your real tests.

View File

@ -2,7 +2,7 @@ import { moduleFor, test } from 'ember-qunit';
moduleFor('controller:dc/intentions/create', 'Unit | Controller | dc/intentions/create', { moduleFor('controller:dc/intentions/create', 'Unit | Controller | dc/intentions/create', {
// Specify the other units that are required for this test. // Specify the other units that are required for this test.
// needs: ['controller:foo'] needs: ['service:dom', 'service:form'],
}); });
// Replace this with your real tests. // Replace this with your real tests.

View File

@ -2,7 +2,7 @@ import { moduleFor, test } from 'ember-qunit';
moduleFor('controller:dc/intentions/edit', 'Unit | Controller | dc/intentions/edit', { moduleFor('controller:dc/intentions/edit', 'Unit | Controller | dc/intentions/edit', {
// Specify the other units that are required for this test. // Specify the other units that are required for this test.
// needs: ['controller:foo'] needs: ['service:dom', 'service:form'],
}); });
// Replace this with your real tests. // Replace this with your real tests.

View File

@ -2,7 +2,7 @@ import { moduleFor, test } from 'ember-qunit';
moduleFor('controller:dc/kv/create', 'Unit | Controller | dc/kv/create', { moduleFor('controller:dc/kv/create', 'Unit | Controller | dc/kv/create', {
// Specify the other units that are required for this test. // Specify the other units that are required for this test.
needs: ['service:btoa'], needs: ['service:btoa', 'service:dom', 'service:form'],
}); });
// Replace this with your real tests. // Replace this with your real tests.

View File

@ -2,7 +2,7 @@ import { moduleFor, test } from 'ember-qunit';
moduleFor('controller:dc/kv/edit', 'Unit | Controller | dc/kv/edit', { moduleFor('controller:dc/kv/edit', 'Unit | Controller | dc/kv/edit', {
// Specify the other units that are required for this test. // Specify the other units that are required for this test.
needs: ['service:btoa'], needs: ['service:btoa', 'service:dom', 'service:form'],
}); });
// Replace this with your real tests. // Replace this with your real tests.

View File

@ -2,7 +2,7 @@ import { moduleFor, test } from 'ember-qunit';
moduleFor('controller:dc/kv/root-create', 'Unit | Controller | dc/kv/root-create', { moduleFor('controller:dc/kv/root-create', 'Unit | Controller | dc/kv/root-create', {
// Specify the other units that are required for this test. // Specify the other units that are required for this test.
needs: ['service:btoa'], needs: ['service:btoa', 'service:dom', 'service:form'],
}); });
// Replace this with your real tests. // Replace this with your real tests.