diff --git a/ui-v2/app/adapters/intention.js b/ui-v2/app/adapters/intention.js
index 9f76f58be2..73553ba46a 100644
--- a/ui-v2/app/adapters/intention.js
+++ b/ui-v2/app/adapters/intention.js
@@ -1,4 +1,5 @@
import Adapter, { DATACENTER_QUERY_PARAM as API_DATACENTER_KEY } from './application';
+import { get } from '@ember/object';
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
// Intentions use SourceNS and DestinationNS properties for namespacing
// so we don't need to add the `?ns=` anywhere here
@@ -25,55 +26,66 @@ export default Adapter.extend({
if (typeof id === 'undefined') {
throw new Error('You must specify an id');
}
+
+ // get the information we need from the id, which has been previously encoded
const [SourceNS, SourceName, DestinationNS, DestinationName] = id
.split(':')
.map(decodeURIComponent);
+
return request`
- GET /v1/connect/intentions/exact?source=${SourceNS +
- '/' +
- SourceName}&destination=${DestinationNS + '/' + DestinationName}&${{ dc }}
+ GET /v1/connect/intentions/exact?${{
+ source: `${SourceNS}/${SourceName}`,
+ destination: `${DestinationNS}/${DestinationName}`,
+ dc: dc,
+ }}
Cache-Control: no-store
${{ index }}
`;
},
requestForCreateRecord: function(request, serialized, data) {
- // TODO: need to make sure we remove dc
- return request`
- POST /v1/connect/intentions?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
+ const body = {
+ SourceNS: serialized.SourceNS,
+ DestinationNS: serialized.DestinationNS,
+ SourceName: serialized.SourceName,
+ DestinationName: serialized.DestinationName,
+ SourceType: serialized.SourceType,
+ Meta: serialized.Meta,
+ Description: serialized.Description,
+ };
- ${{
- SourceNS: serialized.SourceNS,
- DestinationNS: serialized.DestinationNS,
- SourceName: serialized.SourceName,
- DestinationName: serialized.DestinationName,
- SourceType: serialized.SourceType,
- Action: serialized.Action,
- Description: serialized.Description,
+ // only send the Action if we have one
+ if (get(serialized, 'Action.length')) {
+ body.Action = serialized.Action;
+ } else {
+ // otherwise only send Permissions if we have them
+ if (serialized.Permissions) {
+ body.Permissions = serialized.Permissions;
+ }
+ }
+ return request`
+ PUT /v1/connect/intentions/exact?${{
+ source: `${data.SourceNS}/${data.SourceName}`,
+ destination: `${data.DestinationNS}/${data.DestinationName}`,
+ [API_DATACENTER_KEY]: data[DATACENTER_KEY],
}}
+
+ ${body}
`;
},
requestForUpdateRecord: function(request, serialized, data) {
- return request`
- PUT /v1/connect/intentions/${data.LegacyID}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
-
- ${{
- SourceNS: serialized.SourceNS,
- DestinationNS: serialized.DestinationNS,
- SourceName: serialized.SourceName,
- DestinationName: serialized.DestinationName,
- SourceType: serialized.SourceType,
- Action: serialized.Action,
- Meta: serialized.Meta,
- Description: serialized.Description,
- }}
- `;
+ // you can no longer save Destinations
+ delete serialized.DestinationNS;
+ delete serialized.DestinationName;
+ return this.requestForCreateRecord(...arguments);
},
requestForDeleteRecord: function(request, serialized, data) {
return request`
- DELETE /v1/connect/intentions/${data.LegacyID}?${{
- [API_DATACENTER_KEY]: data[DATACENTER_KEY],
- }}
+ DELETE /v1/connect/intentions/exact?${{
+ source: `${data.SourceNS}/${data.SourceName}`,
+ destination: `${data.DestinationNS}/${data.DestinationName}`,
+ [API_DATACENTER_KEY]: data[DATACENTER_KEY],
+ }}
`;
},
});
diff --git a/ui-v2/app/components/consul-intention-form/fieldsets/index.hbs b/ui-v2/app/components/consul-intention-form/fieldsets/index.hbs
index 867b33d135..4a47457514 100644
--- a/ui-v2/app/components/consul-intention-form/fieldsets/index.hbs
+++ b/ui-v2/app/components/consul-intention-form/fieldsets/index.hbs
@@ -92,7 +92,9 @@
{{nspace.Name}}
{{/if}}
+ {{#if create}}
For the destination, you may choose any namespace for which you have access.
+ {{/if}}
{{/if}}
@@ -112,12 +114,17 @@
header="Deny"
body="The source service will not be allowed to connect to the destination."
)
+ (hash
+ intent=""
+ header="L7 Permissions"
+ body="The source service may or may not connect to the destination service via unique permissions based on L7 criteria: path, header, or method."
+ )
)
- as |_action|}}
+ as |_action|}}
@@ -131,13 +138,10 @@
{{/each}}
-
-{{#if (not item.Legacy)}}
-
-
+
{{/if}}
diff --git a/ui-v2/app/components/list-collection/index.hbs b/ui-v2/app/components/list-collection/index.hbs
index 80c75b66f2..ce7fbe30cc 100644
--- a/ui-v2/app/components/list-collection/index.hbs
+++ b/ui-v2/app/components/list-collection/index.hbs
@@ -1,34 +1,69 @@
-{{on-window 'resize' (action "resize") }}
-{{yield}}
-
-
- {{~#each _cells as |cell|~}}
-
-
- {{yield cell.item cell.index}}
-
+
+ {{~#each _cells as |cell|~}}
+
-
- {{yield cell.item cell.index}}
-
-
-
- {{~/each~}}
-
\ No newline at end of file
+
+ {{yield cell.item cell.index}}
+
+
+ {{yield cell.item cell.index}}
+
+
+
+ {{~/each~}}
+
+{{else}}
+
+
+ {{~#each items as |item index|~}}
+ -
+
+
{{yield item index}}
+
+
+ {{yield item index}}
+
+
+
+ {{~/each~}}
+
+{{/if}}
+
\ No newline at end of file
diff --git a/ui-v2/app/components/list-collection/index.js b/ui-v2/app/components/list-collection/index.js
index 6ef8cd0495..05226d77f8 100644
--- a/ui-v2/app/components/list-collection/index.js
+++ b/ui-v2/app/components/list-collection/index.js
@@ -9,20 +9,23 @@ const formatItemStyle = PercentageColumns.prototype.formatItemStyle;
export default Component.extend(Slotted, {
dom: service('dom'),
- tagName: 'div',
- attributeBindings: ['style'],
+ tagName: '',
height: 500,
cellHeight: 70,
style: style('getStyle'),
- classNames: ['list-collection'],
checked: null,
+ scroll: 'virtual',
init: function() {
this._super(...arguments);
this.columns = [100];
+ this.guid = this.dom.guid(this);
},
didInsertElement: function() {
this._super(...arguments);
- this.actions.resize.apply(this, [{ target: this.dom.viewport() }]);
+ this.$element = this.dom.element(`#${this.guid}`);
+ if (this.scroll === 'virtual') {
+ this.actions.resize.apply(this, [{ target: this.dom.viewport() }]);
+ }
},
didReceiveAttrs: function() {
this._super(...arguments);
@@ -41,6 +44,9 @@ export default Component.extend(Slotted, {
};
},
getStyle: computed('height', function() {
+ if (this.scroll !== 'virtual') {
+ return {};
+ }
return {
height: get(this, 'height'),
};
@@ -49,12 +55,11 @@ export default Component.extend(Slotted, {
resize: function(e) {
// TODO: This top part is very similar to resize in tabular-collection
// see if it make sense to DRY out
- const dom = get(this, 'dom');
- const $appContent = dom.element('main > div');
+ const $appContent = this.dom.element('main > div');
if ($appContent) {
const border = 1;
- const rect = this.element.getBoundingClientRect();
- const $footer = dom.element('footer[role="contentinfo"]');
+ const rect = this.$element.getBoundingClientRect();
+ const $footer = this.dom.element('footer[role="contentinfo"]');
const space = rect.top + $footer.clientHeight + border;
const height = e.target.innerHeight - space;
this.set('height', Math.max(0, height));
diff --git a/ui-v2/app/components/popover-menu/index.hbs b/ui-v2/app/components/popover-menu/index.hbs
index 769f944060..b72f25d661 100644
--- a/ui-v2/app/components/popover-menu/index.hbs
+++ b/ui-v2/app/components/popover-menu/index.hbs
@@ -36,6 +36,7 @@ as |components|}}
+
{{#each submenus as |sub|}}
diff --git a/ui-v2/app/components/popover-menu/menu-item/index.hbs b/ui-v2/app/components/popover-menu/menu-item/index.hbs
index 7cce6c7cf3..ef42ac9dc9 100644
--- a/ui-v2/app/components/popover-menu/menu-item/index.hbs
+++ b/ui-v2/app/components/popover-menu/menu-item/index.hbs
@@ -32,7 +32,10 @@
role="menuitem"
aria-selected={{if selected 'true'}}
tabindex="-1"
- onclick={{action (or this.onclick (noop))}}>
+ onclick={{queue
+ (action (or this.onclick (noop)))
+ (action (if this.close menu.clickTrigger (noop)))
+ }}>
{{yield}}
diff --git a/ui-v2/app/components/radio-card/index.js b/ui-v2/app/components/radio-card/index.js
index a7be4db131..4798652642 100644
--- a/ui-v2/app/components/radio-card/index.js
+++ b/ui-v2/app/components/radio-card/index.js
@@ -1,6 +1,5 @@
import Component from '@ember/component';
-import Slotted from 'block-slots';
-export default Component.extend(Slotted, {
+export default Component.extend({
tagName: '',
});
diff --git a/ui-v2/app/helpers/route-match.js b/ui-v2/app/helpers/route-match.js
index e1fee0031c..3705462454 100644
--- a/ui-v2/app/helpers/route-match.js
+++ b/ui-v2/app/helpers/route-match.js
@@ -1,17 +1,20 @@
import { helper } from '@ember/component/helper';
export default helper(function routeMatch([item], hash) {
- const keys = Object.keys(item.data || item);
- switch (true) {
- case keys.includes('Present'):
+ const prop = ['Present', 'Exact', 'Prefix', 'Suffix', 'Regex'].find(
+ prop => typeof item[prop] !== 'undefined'
+ );
+
+ switch (prop) {
+ case 'Present':
return `${item.Invert ? `NOT ` : ``}present`;
- case keys.includes('Exact'):
+ case 'Exact':
return `${item.Invert ? `NOT ` : ``}exactly matching "${item.Exact}"`;
- case keys.includes('Prefix'):
+ case 'Prefix':
return `${item.Invert ? `NOT ` : ``}prefixed by "${item.Prefix}"`;
- case keys.includes('Suffix'):
+ case 'Suffix':
return `${item.Invert ? `NOT ` : ``}suffixed by "${item.Suffix}"`;
- case keys.includes('Regex'):
+ case 'Regex':
return `${item.Invert ? `NOT ` : ``}matching the regex "${item.Regex}"`;
}
return '';
diff --git a/ui-v2/app/models/intention-permission-http-header.js b/ui-v2/app/models/intention-permission-http-header.js
index 0060904d28..d663bea810 100644
--- a/ui-v2/app/models/intention-permission-http-header.js
+++ b/ui-v2/app/models/intention-permission-http-header.js
@@ -1,7 +1,18 @@
+import { computed } from '@ember/object';
+import { or } from '@ember/object/computed';
import attr from 'ember-data/attr';
import Fragment from 'ember-data-model-fragments/fragment';
+export const schema = {
+ Name: {
+ required: true,
+ },
+ HeaderType: {
+ allowedValues: ['Exact', 'Prefix', 'Suffix', 'Regex', 'Present'],
+ },
+};
+
export default Fragment.extend({
Name: attr('string'),
@@ -9,6 +20,10 @@ export default Fragment.extend({
Prefix: attr('string'),
Suffix: attr('string'),
Regex: attr('string'),
- Present: attr('boolean'),
- Invert: attr('boolean'),
+ Present: attr(), // this is a boolean but we don't want it to automatically be set to false
+
+ Value: or(...schema.HeaderType.allowedValues),
+ HeaderType: computed(...schema.HeaderType.allowedValues, function() {
+ return schema.HeaderType.allowedValues.find(prop => typeof this[prop] !== 'undefined');
+ }),
});
diff --git a/ui-v2/app/models/intention-permission-http.js b/ui-v2/app/models/intention-permission-http.js
index 8a988aefac..7e551eaa0f 100644
--- a/ui-v2/app/models/intention-permission-http.js
+++ b/ui-v2/app/models/intention-permission-http.js
@@ -5,7 +5,15 @@ import { or } from '@ember/object/computed';
import Fragment from 'ember-data-model-fragments/fragment';
import { fragmentArray, array } from 'ember-data-model-fragments/attributes';
-const pathProps = ['PathPrefix', 'PathExact', 'PathRegex'];
+export const schema = {
+ PathType: {
+ allowedValues: ['PathPrefix', 'PathExact', 'PathRegex'],
+ },
+ Methods: {
+ allowedValues: ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', 'PATCH'],
+ },
+};
+
export default Fragment.extend({
PathExact: attr('string'),
PathPrefix: attr('string'),
@@ -14,8 +22,8 @@ export default Fragment.extend({
Header: fragmentArray('intention-permission-http-header'),
Methods: array('string'),
- Path: or(...pathProps),
- PathType: computed(...pathProps, function() {
- return pathProps.find(prop => typeof this[prop] === 'string');
+ Path: or(...schema.PathType.allowedValues),
+ PathType: computed(...schema.PathType.allowedValues, function() {
+ return schema.PathType.allowedValues.find(prop => typeof this[prop] === 'string');
}),
});
diff --git a/ui-v2/app/models/intention-permission.js b/ui-v2/app/models/intention-permission.js
index 4786d23eac..af1e8e4cec 100644
--- a/ui-v2/app/models/intention-permission.js
+++ b/ui-v2/app/models/intention-permission.js
@@ -3,7 +3,16 @@ import attr from 'ember-data/attr';
import Fragment from 'ember-data-model-fragments/fragment';
import { fragment } from 'ember-data-model-fragments/attributes';
+export const schema = {
+ Action: {
+ defaultValue: 'allow',
+ allowedValues: ['allow', 'deny'],
+ },
+};
+
export default Fragment.extend({
- Action: attr('string', { defaultValue: 'allow' }),
- Http: fragment('intention-permission-http'),
+ Action: attr('string', {
+ defaultValue: schema.Action.defaultValue,
+ }),
+ HTTP: fragment('intention-permission-http'),
});
diff --git a/ui-v2/app/models/intention.js b/ui-v2/app/models/intention.js
index 5a2b1c874d..d2e04b452a 100644
--- a/ui-v2/app/models/intention.js
+++ b/ui-v2/app/models/intention.js
@@ -10,17 +10,17 @@ export default Model.extend({
[PRIMARY_KEY]: attr('string'),
[SLUG_KEY]: attr('string'),
Description: attr('string'),
- SourceNS: attr('string'),
+ SourceNS: attr('string', { defaultValue: 'default' }),
SourceName: attr('string', { defaultValue: '*' }),
DestinationName: attr('string', { defaultValue: '*' }),
- DestinationNS: attr('string'),
+ DestinationNS: attr('string', { defaultValue: 'default' }),
Precedence: attr('number'),
Permissions: fragmentArray('intention-permission'),
SourceType: attr('string', { defaultValue: 'consul' }),
Action: attr('string'),
Meta: attr(),
- Legacy: attr('boolean', { defaultValue: true }),
LegacyID: attr('string'),
+ Legacy: attr('boolean', { defaultValue: true }),
IsManagedByCRD: computed('Meta', function() {
const meta = Object.entries(this.Meta || {}).find(
@@ -29,7 +29,7 @@ export default Model.extend({
return typeof meta !== 'undefined';
}),
IsEditable: computed('Legacy', 'IsManagedByCRD', function() {
- return this.Legacy && !this.IsManagedByCRD;
+ return !this.IsManagedByCRD;
}),
SyncTime: attr('number'),
Datacenter: attr('string'),
diff --git a/ui-v2/app/routes/dc/services/show/intentions/edit.js b/ui-v2/app/routes/dc/services/show/intentions/edit.js
index b044b155c1..af43c7987b 100644
--- a/ui-v2/app/routes/dc/services/show/intentions/edit.js
+++ b/ui-v2/app/routes/dc/services/show/intentions/edit.js
@@ -6,7 +6,7 @@ export default Route.extend({
nspace: '*',
dc: this.paramsFor('dc').dc,
service: this.paramsFor('dc.services.show').name,
- src: params.intention,
+ src: params.intention_id,
};
},
setupController: function(controller, model) {
diff --git a/ui-v2/app/serializers/intention.js b/ui-v2/app/serializers/intention.js
index d35b79a49f..c85057fdeb 100644
--- a/ui-v2/app/serializers/intention.js
+++ b/ui-v2/app/serializers/intention.js
@@ -44,16 +44,24 @@ export default Serializer.extend({
query
);
},
+ respondForCreateRecord: function(respond, serialized, data) {
+ const slugKey = this.slugKey;
+ const primaryKey = this.primaryKey;
+ return respond((headers, body) => {
+ body = data;
+ body.ID = this
+ .uri`${serialized.SourceNS}:${serialized.SourceName}:${serialized.DestinationNS}:${serialized.DestinationName}`;
+ return this.fingerprint(primaryKey, slugKey, body.Datacenter)(body);
+ });
+ },
respondForUpdateRecord: function(respond, serialized, data) {
- return this._super(
- cb =>
- respond((headers, body) => {
- body.LegacyID = body.ID;
- body.ID = serialized.ID;
- return cb(headers, body);
- }),
- serialized,
- data
- );
+ const slugKey = this.slugKey;
+ const primaryKey = this.primaryKey;
+ return respond((headers, body) => {
+ body = data;
+ body.LegacyID = body.ID;
+ body.ID = serialized.ID;
+ return this.fingerprint(primaryKey, slugKey, body.Datacenter)(body);
+ });
},
});
diff --git a/ui-v2/app/services/change.js b/ui-v2/app/services/change.js
new file mode 100644
index 0000000000..a5150464d8
--- /dev/null
+++ b/ui-v2/app/services/change.js
@@ -0,0 +1,51 @@
+import Service, { inject as service } from '@ember/service';
+
+import lookupValidator from 'ember-changeset-validations';
+import { Changeset as createChangeset } from 'ember-changeset';
+
+import Changeset from 'consul-ui/utils/form/changeset';
+
+import intentionPermissionValidator from 'consul-ui/validations/intention-permission';
+import intentionPermissionHttpHeaderValidator from 'consul-ui/validations/intention-permission-http-header';
+
+const validators = {
+ 'intention-permission': intentionPermissionValidator,
+ 'intention-permission-http-header': intentionPermissionHttpHeaderValidator,
+};
+
+export default Service.extend({
+ schema: service('schema'),
+
+ init: function() {
+ this._super(...arguments);
+ this._validators = new Map();
+ },
+ willDestroy: function() {
+ this._validators = null;
+ },
+ changesetFor: function(modelName, model, options = {}) {
+ const validator = this.validatorFor(modelName, options);
+ let changeset;
+ if (validator) {
+ let validatorFunc = validator;
+ if (typeof validator !== 'function') {
+ validatorFunc = lookupValidator(validator);
+ }
+ changeset = createChangeset(model, validatorFunc, validator, { changeset: Changeset });
+ } else {
+ changeset = createChangeset(model);
+ }
+ return changeset;
+ },
+ validatorFor: function(modelName, options = {}) {
+ if (!this._validators.has(modelName)) {
+ const factory = validators[modelName];
+ let validator;
+ if (typeof factory !== 'undefined') {
+ validator = factory(this.schema);
+ }
+ this._validators.set(modelName, validator);
+ }
+ return this._validators.get(modelName);
+ },
+});
diff --git a/ui-v2/app/services/repository.js b/ui-v2/app/services/repository.js
index a48b4f13e4..195022b88d 100644
--- a/ui-v2/app/services/repository.js
+++ b/ui-v2/app/services/repository.js
@@ -2,6 +2,7 @@ import Service, { inject as service } from '@ember/service';
import { assert } from '@ember/debug';
import { typeOf } from '@ember/utils';
import { get } from '@ember/object';
+import { isChangeset } from 'validated-changeset';
export default Service.extend({
getModelName: function() {
@@ -67,6 +68,13 @@ export default Service.extend({
return this.store.createRecord(this.getModelName(), obj);
},
persist: function(item) {
+ // workaround for saving changesets that contain fragments
+ // firstly commit the changes down onto the object if
+ // its a changeset, then save as a normal object
+ if (isChangeset(item)) {
+ item.execute();
+ item = item.data;
+ }
return item.save();
},
remove: function(obj) {
diff --git a/ui-v2/app/services/repository/intention-permission-http-header.js b/ui-v2/app/services/repository/intention-permission-http-header.js
new file mode 100644
index 0000000000..7fb1ee0932
--- /dev/null
+++ b/ui-v2/app/services/repository/intention-permission-http-header.js
@@ -0,0 +1,14 @@
+import RepositoryService from 'consul-ui/services/repository';
+
+const modelName = 'intention-permission-http-header';
+export default RepositoryService.extend({
+ getModelName: function() {
+ return modelName;
+ },
+ create: function(obj = {}) {
+ return this.store.createFragment(this.getModelName(), obj);
+ },
+ persist: function(item) {
+ return item.execute();
+ },
+});
diff --git a/ui-v2/app/services/repository/intention-permission.js b/ui-v2/app/services/repository/intention-permission.js
new file mode 100644
index 0000000000..79e2c906fc
--- /dev/null
+++ b/ui-v2/app/services/repository/intention-permission.js
@@ -0,0 +1,18 @@
+import RepositoryService from 'consul-ui/services/repository';
+const modelName = 'intention-permission';
+export default RepositoryService.extend({
+ getModelName: function() {
+ return modelName;
+ },
+ create: function(obj = {}) {
+ // intention-permission and intention-permission-http
+ // are currently treated as one and the same
+ return this.store.createFragment(this.getModelName(), {
+ ...obj,
+ HTTP: this.store.createFragment('intention-permission-http', obj.HTTP || {}),
+ });
+ },
+ persist: function(item) {
+ return item.execute();
+ },
+});
diff --git a/ui-v2/app/services/repository/intention.js b/ui-v2/app/services/repository/intention.js
index 8217ed07ee..3e1033571b 100644
--- a/ui-v2/app/services/repository/intention.js
+++ b/ui-v2/app/services/repository/intention.js
@@ -1,3 +1,4 @@
+import { set, get } from '@ember/object';
import RepositoryService from 'consul-ui/services/repository';
import { PRIMARY_KEY } from 'consul-ui/models/intention';
const modelName = 'intention';
@@ -15,6 +16,19 @@ export default RepositoryService.extend({
...obj,
});
},
+ persist: function(obj) {
+ return this._super(...arguments).then(res => {
+ // if Action is set it means we are an l4 type intention
+ // we don't delete these at a UI level incase the user
+ // would like to switch backwards and forwards between
+ // allow/deny/l7 in the forms, but once its been saved
+ // to the backend we then delete them
+ if (get(res, 'Action.length')) {
+ set(res, 'Permissions', []);
+ }
+ return res;
+ });
+ },
findByService: function(slug, dc, nspace, configuration = {}) {
const query = {
dc: dc,
diff --git a/ui-v2/app/services/schema.js b/ui-v2/app/services/schema.js
new file mode 100644
index 0000000000..bf4e9c22ff
--- /dev/null
+++ b/ui-v2/app/services/schema.js
@@ -0,0 +1,10 @@
+import Service from '@ember/service';
+import { schema as intentionPermissionSchema } from 'consul-ui/models/intention-permission';
+import { schema as intentionPermissionHttpSchema } from 'consul-ui/models/intention-permission-http';
+import { schema as intentionPermissionHttpHeaderSchema } from 'consul-ui/models/intention-permission-http-header';
+
+export default Service.extend({
+ 'intention-permission': intentionPermissionSchema,
+ 'intention-permission-http': intentionPermissionHttpSchema,
+ 'intention-permission-http-header': intentionPermissionHttpHeaderSchema,
+});
diff --git a/ui-v2/app/styles/components.scss b/ui-v2/app/styles/components.scss
index fb2b3d5d0b..11bd086a94 100644
--- a/ui-v2/app/styles/components.scss
+++ b/ui-v2/app/styles/components.scss
@@ -54,6 +54,15 @@
@import './components/footer';
/**/
-@import './components/consul-intention-list';
-@import './components/consul-intention-permission-list';
-@import './components/consul-intention-fieldsets';
+
+/**
+ * Migration: We are migrating our consul-* styles to use colocated styles
+ * consul-* component styles should be moved or added under here
+ * when convienient
+ **/
+
+@import 'consul-ui/components/consul-intention-list';
+@import 'consul-ui/components/consul-intention-form/fieldsets';
+@import 'consul-ui/components/consul-intention-permission-list';
+@import 'consul-ui/components/consul-intention-permission-form';
+@import 'consul-ui/components/consul-intention-permission-header-list';
diff --git a/ui-v2/app/styles/components/buttons.scss b/ui-v2/app/styles/components/buttons.scss
index 765df2edf0..5b1077de44 100644
--- a/ui-v2/app/styles/components/buttons.scss
+++ b/ui-v2/app/styles/components/buttons.scss
@@ -1,4 +1,5 @@
button[type='submit'],
+button.type-submit,
a.type-create {
@extend %primary-button;
}
diff --git a/ui-v2/app/styles/components/consul-intention-fieldsets.scss b/ui-v2/app/styles/components/consul-intention-fieldsets.scss
deleted file mode 100644
index aa137a88da..0000000000
--- a/ui-v2/app/styles/components/consul-intention-fieldsets.scss
+++ /dev/null
@@ -1,4 +0,0 @@
-@import './consul-intention-fieldsets/index';
-.consul-intention-fieldsets {
- @extend %consul-intention-fieldsets;
-}
diff --git a/ui-v2/app/styles/components/consul-intention-fieldsets/layout.scss b/ui-v2/app/styles/components/consul-intention-fieldsets/layout.scss
deleted file mode 100644
index 4b4713d8a1..0000000000
--- a/ui-v2/app/styles/components/consul-intention-fieldsets/layout.scss
+++ /dev/null
@@ -1,22 +0,0 @@
-%consul-intention-fieldsets [role='radiogroup'] {
- overflow: visible !important;
- display: grid;
- grid-gap: 12px;
- grid-template-columns: repeat(auto-fill, minmax(270px, 370px));
-}
-%consul-intention-fieldsets .radio-card {
- @extend %radio-card-with-icon;
-}
-%consul-intention-fieldsets .radio-card header > * {
- display: inline;
-}
-%consul-intention-fieldsets .value-allow > :last-child::before {
- @extend %with-arrow-right-color-icon, %as-pseudo;
-}
-%consul-intention-fieldsets .value-deny > :last-child::before {
- @extend %with-deny-color-icon, %as-pseudo;
-}
-%consul-intention-fieldsets .radio-card header span.code::before {
- @extend %with-deny-color-icon, %as-pseudo;
- margin-right: 5px;
-}
diff --git a/ui-v2/app/styles/components/consul-intention-list.scss b/ui-v2/app/styles/components/consul-intention-list.scss
deleted file mode 100644
index 642c10286a..0000000000
--- a/ui-v2/app/styles/components/consul-intention-list.scss
+++ /dev/null
@@ -1,4 +0,0 @@
-@import './consul-intention-list/index';
-.consul-intention-list {
- @extend %consul-intention-list;
-}
diff --git a/ui-v2/app/styles/components/consul-intention-list/index.scss b/ui-v2/app/styles/components/consul-intention-list/index.scss
deleted file mode 100644
index d049a9e8fa..0000000000
--- a/ui-v2/app/styles/components/consul-intention-list/index.scss
+++ /dev/null
@@ -1,19 +0,0 @@
-@import './skin';
-@import './layout';
-
-%consul-intention-list td.source,
-%consul-intention-list td.destination {
- @extend %tbody-th;
-}
-%consul-intention-list td strong {
- @extend %pill-700;
-}
-%consul-intention-list td.intent-allow strong {
- @extend %pill-allow;
-}
-%consul-intention-list td.intent-deny strong {
- @extend %pill-deny;
-}
-%consul-intention-list td.intent-l7-rules strong {
- @extend %pill-l7;
-}
diff --git a/ui-v2/app/styles/components/consul-intention-list/layout.scss b/ui-v2/app/styles/components/consul-intention-list/layout.scss
deleted file mode 100644
index 9b01854717..0000000000
--- a/ui-v2/app/styles/components/consul-intention-list/layout.scss
+++ /dev/null
@@ -1,27 +0,0 @@
-%consul-intention-list td {
- height: 59px;
-}
-%consul-intention-list tr > *:nth-child(1) {
- width: calc(30% - 50px);
-}
-%consul-intention-list tr > *:nth-child(2) {
- width: 100px;
-}
-%consul-intention-list tr > *:nth-child(3) {
- width: calc(30% - 50px);
-}
-%consul-intention-list tr > *:nth-child(4) {
- width: calc(40% - 220px);
-}
-%consul-intention-list tr > *:nth-child(5) {
- width: 160px;
-}
-%consul-intention-list tr > *:last-child {
- width: 60px;
-}
-
-@media #{$--lt-horizontal-nav} {
- %consul-intention-list tr > :not(.source):not(.destination):not(.intent) {
- display: none;
- }
-}
diff --git a/ui-v2/app/styles/components/consul-intention-list/skin.scss b/ui-v2/app/styles/components/consul-intention-list/skin.scss
deleted file mode 100644
index c96a4e7af7..0000000000
--- a/ui-v2/app/styles/components/consul-intention-list/skin.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-%consul-intention-list td.permissions {
- color: $blue-500;
-}
diff --git a/ui-v2/app/styles/components/consul-intention-permission-list.scss b/ui-v2/app/styles/components/consul-intention-permission-list.scss
deleted file mode 100644
index 139c5be691..0000000000
--- a/ui-v2/app/styles/components/consul-intention-permission-list.scss
+++ /dev/null
@@ -1,4 +0,0 @@
-@import './consul-intention-permission-list/index';
-.consul-intention-permission-list {
- @extend %consul-intention-permission-list;
-}
diff --git a/ui-v2/app/styles/components/consul-intention-permission-list/index.scss b/ui-v2/app/styles/components/consul-intention-permission-list/index.scss
deleted file mode 100644
index a46573bbd9..0000000000
--- a/ui-v2/app/styles/components/consul-intention-permission-list/index.scss
+++ /dev/null
@@ -1,14 +0,0 @@
-@import './skin';
-@import './layout';
-%consul-intention-permission-list > ul > li {
- @extend %list-row, %list-row-detail;
-}
-%consul-intention-permission-list strong {
- @extend %pill-500;
-}
-%consul-intention-permission-list .intent-allow {
- @extend %pill-allow;
-}
-%consul-intention-permission-list .intent-deny {
- @extend %pill-deny;
-}
diff --git a/ui-v2/app/styles/components/consul-intention-permission-list/layout.scss b/ui-v2/app/styles/components/consul-intention-permission-list/layout.scss
deleted file mode 100644
index 589ba10ca4..0000000000
--- a/ui-v2/app/styles/components/consul-intention-permission-list/layout.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-%consul-intention-permission-list strong {
- margin-right: 8px;
-}
diff --git a/ui-v2/app/styles/components/consul-intention-permission-list/skin.scss b/ui-v2/app/styles/components/consul-intention-permission-list/skin.scss
deleted file mode 100644
index cb5b17de24..0000000000
--- a/ui-v2/app/styles/components/consul-intention-permission-list/skin.scss
+++ /dev/null
@@ -1,9 +0,0 @@
-%consul-intention-permission-list dt::before {
- @extend %with-glyph-icon;
-}
-%consul-intention-permission-list dl.route-path dt::before {
- content: 'P';
-}
-%consul-intention-permission-list dl.route-header dt::before {
- content: 'H';
-}
diff --git a/ui-v2/app/styles/components/flash-message/layout.scss b/ui-v2/app/styles/components/flash-message/layout.scss
index 8239a88caf..02ea5d922b 100644
--- a/ui-v2/app/styles/components/flash-message/layout.scss
+++ b/ui-v2/app/styles/components/flash-message/layout.scss
@@ -1,7 +1,7 @@
%flash-message {
display: flex;
position: relative;
- z-index: 2;
+ z-index: 6;
justify-content: center;
margin: 0 15%;
}
diff --git a/ui-v2/app/styles/components/form-elements.scss b/ui-v2/app/styles/components/form-elements.scss
index 40081f33b1..14cf5ef320 100644
--- a/ui-v2/app/styles/components/form-elements.scss
+++ b/ui-v2/app/styles/components/form-elements.scss
@@ -13,7 +13,7 @@ label span {
%main-content form {
@extend %form;
}
-%form span.label {
+span.label {
@extend %form-element-label;
}
%form table,
diff --git a/ui-v2/app/styles/components/icon-definition/layout.scss b/ui-v2/app/styles/components/icon-definition/layout.scss
index 2bfc368a50..923a730d45 100644
--- a/ui-v2/app/styles/components/icon-definition/layout.scss
+++ b/ui-v2/app/styles/components/icon-definition/layout.scss
@@ -6,13 +6,13 @@
display: inline-flex;
flex-wrap: nowrap;
}
-%icon-definition > dt {
+%icon-definition > * {
align-self: center;
}
+%icon-definition > dd {
+ white-space: nowrap;
+ margin-left: 4px;
+}
%icon-definition > dt > * {
display: none;
}
-%icon-definition > dd {
- margin-left: 4px;
- white-space: nowrap;
-}
diff --git a/ui-v2/app/styles/components/list-collection.scss b/ui-v2/app/styles/components/list-collection.scss
index 0e39f15707..4b365b7d2b 100644
--- a/ui-v2/app/styles/components/list-collection.scss
+++ b/ui-v2/app/styles/components/list-collection.scss
@@ -1,13 +1,21 @@
.list-collection {
@extend %list-collection;
}
-%list-collection {
+.list-collection-scroll-virtual {
+ @extend %list-collection-scroll-virtual;
+}
+%list-collection-scroll-virtual {
height: 500px;
position: relative;
}
%list-collection > ul {
border-top: 1px solid $gray-200;
- overflow-x: hidden !important;
+}
+%list-collection > ul > li {
+ position: relative;
+}
+%list-collection-scroll-virtual > ul {
+ overflow-x: hidden;
}
%list-collection > ul > li:nth-child(2) .with-feedback p {
bottom: auto;
diff --git a/ui-v2/app/utils/form/builder.js b/ui-v2/app/utils/form/builder.js
index a0f1e4128a..b0091ee564 100644
--- a/ui-v2/app/utils/form/builder.js
+++ b/ui-v2/app/utils/form/builder.js
@@ -1,20 +1,13 @@
-import { get, set, computed } from '@ember/object';
-import Changeset from 'ember-changeset';
+import { get, set } from '@ember/object';
+import { Changeset as createChangeset } from 'ember-changeset';
+import Changeset from 'consul-ui/utils/form/changeset';
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';
-// TODO: Currently supporting ember-data nicely like this
-// Unfortunately since post-ember 2.18, the only way to get this to work
-// is to hang stuff off the prototype (which then makes it available everywhere)
-// we should either try and figure out another way of doing this, or move this code
-// somewhere where it is more 'global' like an initializer
-Changeset.prototype.isSaving = computed('data.isSaving', function() {
- return this.data.isSaving;
-});
const defaultChangeset = function(data, validators) {
- return new Changeset(data, lookupValidator(validators), validators);
+ return createChangeset(data, lookupValidator(validators), validators, { changeset: Changeset });
};
/**
* Form builder/Form factory
diff --git a/ui-v2/app/utils/form/changeset.js b/ui-v2/app/utils/form/changeset.js
new file mode 100644
index 0000000000..8073a68f79
--- /dev/null
+++ b/ui-v2/app/utils/form/changeset.js
@@ -0,0 +1,38 @@
+import { get } from '@ember/object';
+import { EmberChangeset as Changeset } from 'ember-changeset';
+const CHANGES = '_changes';
+export default class extends Changeset {
+ pushObject(prop, value) {
+ let val;
+ if (typeof get(this, `${CHANGES}.${prop}`) === 'undefined') {
+ val = get(this, `data.${prop}`);
+ if (!val) {
+ val = [];
+ } else {
+ val = val.toArray();
+ }
+ } else {
+ val = this.get(prop).slice(0);
+ }
+ val.push(value);
+ this.set(`${prop}`, val);
+ }
+ removeObject(prop, value) {
+ let val;
+ if (typeof get(this, `${CHANGES}.${prop}`) === 'undefined') {
+ val = get(this, `data.${prop}`);
+ if (typeof val === 'undefined') {
+ val = [];
+ } else {
+ val = val.toArray();
+ }
+ } else {
+ val = this.get(prop).slice(0);
+ }
+ const pos = val.indexOf(value);
+ if (pos !== -1) {
+ val.splice(pos, 1);
+ }
+ this.set(`${prop}`, val);
+ }
+}
diff --git a/ui-v2/app/validations/intention-permission-http-header.js b/ui-v2/app/validations/intention-permission-http-header.js
new file mode 100644
index 0000000000..de67f06192
--- /dev/null
+++ b/ui-v2/app/validations/intention-permission-http-header.js
@@ -0,0 +1,8 @@
+import { validatePresence } from 'ember-changeset-validations/validators';
+import validateSometimes from 'ember-changeset-conditional-validations/validators/sometimes';
+export default schema => ({
+ Name: [validatePresence(true)],
+ Value: validateSometimes([validatePresence(true)], function() {
+ return this.get('HeaderType') !== 'Present';
+ }),
+});
diff --git a/ui-v2/app/validations/intention-permission.js b/ui-v2/app/validations/intention-permission.js
new file mode 100644
index 0000000000..ad855130e6
--- /dev/null
+++ b/ui-v2/app/validations/intention-permission.js
@@ -0,0 +1,29 @@
+import {
+ validateInclusion,
+ validatePresence,
+ validateFormat,
+} from 'ember-changeset-validations/validators';
+import validateSometimes from 'ember-changeset-conditional-validations/validators/sometimes';
+
+const name = 'intention-permission';
+export default schema => ({
+ '*': validateSometimes([validatePresence(true)], function() {
+ const methods = this.get('HTTP.Methods') || [];
+ const headers = this.get('HTTP.Header') || [];
+ const pathType = this.get('HTTP.PathType') || 'NoPath';
+ const path = this.get('HTTP.Path') || '';
+ const isValid = [
+ methods.length !== 0,
+ headers.length !== 0,
+ pathType !== 'NoPath' && path !== '',
+ ].includes(true);
+ return !isValid;
+ }),
+ Action: [validateInclusion({ in: schema[name].Action.allowedValues })],
+ HTTP: {
+ Path: validateSometimes([validateFormat({ regex: /^\// })], function() {
+ const pathType = this.get('HTTP.PathType');
+ return typeof pathType !== 'undefined' && pathType !== 'NoPath';
+ }),
+ },
+});
diff --git a/ui-v2/app/validations/intention.js b/ui-v2/app/validations/intention.js
index 922ba9e413..12e3115b27 100644
--- a/ui-v2/app/validations/intention.js
+++ b/ui-v2/app/validations/intention.js
@@ -1,6 +1,19 @@
import { validatePresence, validateLength } from 'ember-changeset-validations/validators';
+import validateSometimes from 'ember-changeset-conditional-validations/validators/sometimes';
export default {
+ '*': validateSometimes([validatePresence(true)], function() {
+ const action = this.get('Action') || '';
+ const permissions = this.get('Permissions') || [];
+ if (action === '' && permissions.length === 0) {
+ return true;
+ }
+ return false;
+ }),
SourceName: [validatePresence(true), validateLength({ min: 1 })],
DestinationName: [validatePresence(true), validateLength({ min: 1 })],
- Action: validatePresence(true),
+ Permissions: [
+ validateSometimes([validateLength({ min: 1 })], function(changes, content) {
+ return !this.get('Action');
+ }),
+ ],
};
diff --git a/ui-v2/package.json b/ui-v2/package.json
index c81988f4bc..1c105e9a69 100644
--- a/ui-v2/package.json
+++ b/ui-v2/package.json
@@ -68,7 +68,8 @@
"css.escape": "^1.5.1",
"dart-sass": "^1.25.0",
"ember-auto-import": "^1.5.3",
- "ember-changeset-validations": "^3.0.2",
+ "ember-changeset-conditional-validations": "^0.6.0",
+ "ember-changeset-validations": "^3.9.0",
"ember-cli": "~3.20.2",
"ember-cli-app-version": "^3.2.0",
"ember-cli-autoprefixer": "^0.8.1",
diff --git a/ui-v2/tests/acceptance/dc/intentions/create.feature b/ui-v2/tests/acceptance/dc/intentions/create.feature
index 5c29e704eb..ab46d96131 100644
--- a/ui-v2/tests/acceptance/dc/intentions/create.feature
+++ b/ui-v2/tests/acceptance/dc/intentions/create.feature
@@ -33,7 +33,7 @@ Feature: dc / intentions / create: Intention Create
# Specifically set deny
And I click "[value=deny]"
And I submit
- Then a POST request was made to "/v1/connect/intentions?dc=datacenter" from yaml
+ Then a PUT request was made to "/v1/connect/intentions/exact?source=default%2Fweb&destination=default%2Fdb&dc=datacenter" from yaml
---
body:
SourceName: web
diff --git a/ui-v2/tests/acceptance/dc/intentions/delete.feature b/ui-v2/tests/acceptance/dc/intentions/delete.feature
index dd8feec099..58202d686e 100644
--- a/ui-v2/tests/acceptance/dc/intentions/delete.feature
+++ b/ui-v2/tests/acceptance/dc/intentions/delete.feature
@@ -2,13 +2,16 @@
Feature: dc / intentions / deleting: Deleting items with confirmations, success and error notifications
Background:
Given 1 datacenter model with the value "datacenter"
- Scenario: Deleting a intention model from the intention listing page
- Given 1 intention model from yaml
+ And 1 intention model from yaml
---
+ SourceNS: default
SourceName: name
+ DestinationNS: default
+ DestinationName: destination
ID: ee52203d-989f-4f7a-ab5a-2bef004164ca
Meta: ~
---
+ Scenario: Deleting a intention model from the intention listing page
When I visit the intentions page for yaml
---
dc: datacenter
@@ -16,7 +19,7 @@ Feature: dc / intentions / deleting: Deleting items with confirmations, success
And I click actions on the intentions
And I click delete on the intentions
And I click confirmDelete on the intentions
- Then a DELETE request was made to "/v1/connect/intentions/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=datacenter"
+ Then a DELETE request was made to "/v1/connect/intentions/exact?source=default%2Fname&destination=default%2Fdestination&dc=datacenter"
And "[data-notification]" has the "notification-delete" class
And "[data-notification]" has the "success" class
Scenario: Deleting an intention from the intention detail page
@@ -27,7 +30,7 @@ Feature: dc / intentions / deleting: Deleting items with confirmations, success
---
And I click delete
And I click confirmDelete
- Then a DELETE request was made to "/v1/connect/intentions/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=datacenter"
+ Then a DELETE request was made to "/v1/connect/intentions/exact?source=default%2Fname&destination=default%2Fdestination&dc=datacenter"
And "[data-notification]" has the "notification-delete" class
And "[data-notification]" has the "success" class
Scenario: Deleting an intention from the intention detail page and getting an error
@@ -36,7 +39,7 @@ Feature: dc / intentions / deleting: Deleting items with confirmations, success
dc: datacenter
intention: ee52203d-989f-4f7a-ab5a-2bef004164ca
---
- Given the url "/v1/connect/intentions/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=datacenter" responds with a 500 status
+ Given the url "/v1/connect/intentions/exact?source=default%2Fname&destination=default%2Fdestination&dc=datacenter" responds with a 500 status
And I click delete
And I click confirmDelete
And "[data-notification]" has the "notification-update" class
@@ -47,7 +50,7 @@ Feature: dc / intentions / deleting: Deleting items with confirmations, success
dc: datacenter
intention: ee52203d-989f-4f7a-ab5a-2bef004164ca
---
- Given the url "/v1/connect/intentions/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=datacenter" responds with from yaml
+ Given the url "/v1/connect/intentions/exact?source=default%2Fname&destination=default%2Fdestination&dc=datacenter" responds with from yaml
---
status: 500
body: "duplicate intention found:"
diff --git a/ui-v2/tests/acceptance/dc/intentions/update.feature b/ui-v2/tests/acceptance/dc/intentions/update.feature
index 7617d4580f..3c3effc24c 100644
--- a/ui-v2/tests/acceptance/dc/intentions/update.feature
+++ b/ui-v2/tests/acceptance/dc/intentions/update.feature
@@ -4,6 +4,10 @@ Feature: dc / intentions / update: Intention Update
Given 1 datacenter model with the value "datacenter"
And 1 intention model from yaml
---
+ SourceNS: default
+ SourceName: web
+ DestinationNS: default
+ DestinationName: db
ID: intention-id
---
When I visit the intention page for yaml
@@ -20,7 +24,7 @@ Feature: dc / intentions / update: Intention Update
---
And I click "[value=[Action]]"
And I submit
- Then a PUT request was made to "/v1/connect/intentions/intention-id?dc=datacenter" with the body from yaml
+ Then a PUT request was made to "/v1/connect/intentions/exact?source=default%2Fweb&destination=default%2Fdb&dc=datacenter" from yaml
---
Description: [Description]
Action: [Action]
@@ -35,7 +39,7 @@ Feature: dc / intentions / update: Intention Update
| Desc | allow |
------------------------------
Scenario: There was an error saving the intention
- Given the url "/v1/connect/intentions/intention-id" responds with a 500 status
+ Given the url "/v1/connect/intentions/exact?source=default%2Fweb&destination=default%2Fdb&dc=datacenter" responds with a 500 status
And I submit
Then the url should be /datacenter/intentions/intention-id
Then "[data-notification]" has the "notification-update" class
diff --git a/ui-v2/tests/acceptance/dc/services/show/intentions.feature b/ui-v2/tests/acceptance/dc/services/show/intentions.feature
index 3bde9f6070..58706574e4 100644
--- a/ui-v2/tests/acceptance/dc/services/show/intentions.feature
+++ b/ui-v2/tests/acceptance/dc/services/show/intentions.feature
@@ -15,6 +15,11 @@ Feature: dc / services / show / intentions: Intentions per service
- ID: 755b72bd-f5ab-4c92-90cc-bed0e7d8e9f0
Action: allow
Meta: ~
+ SourceNS: default
+ SourceName: name
+ DestinationNS: default
+ DestinationName: destination
+
- ID: 755b72bd-f5ab-4c92-90cc-bed0e7d8e9f1
Action: deny
Meta: ~
@@ -37,6 +42,6 @@ Feature: dc / services / show / intentions: Intentions per service
And I click actions on the intentions
And I click delete on the intentions
And I click confirmDelete on the intentions
- Then a DELETE request was made to "/v1/connect/intentions/755b72bd-f5ab-4c92-90cc-bed0e7d8e9f0?dc=dc1"
+ Then a DELETE request was made to "/v1/connect/intentions/exact?source=default%2Fname&destination=default%2Fdestination&dc=dc1"
And "[data-notification]" has the "notification-delete" class
And "[data-notification]" has the "success" class
diff --git a/ui-v2/tests/integration/adapters/intention-test.js b/ui-v2/tests/integration/adapters/intention-test.js
index 8262602148..fa8ceea4d9 100644
--- a/ui-v2/tests/integration/adapters/intention-test.js
+++ b/ui-v2/tests/integration/adapters/intention-test.js
@@ -3,7 +3,6 @@ import { setupTest } from 'ember-qunit';
module('Integration | Adapter | intention', function(hooks) {
setupTest(hooks);
const dc = 'dc-1';
- const legacyId = 'intention-name';
const id = 'SourceNS:SourceName:DestinationNS:DestinationName';
test('requestForQuery returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:intention');
@@ -38,14 +37,17 @@ module('Integration | Adapter | intention', function(hooks) {
test('requestForCreateRecord returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:intention');
const client = this.owner.lookup('service:client/http');
- const expected = `POST /v1/connect/intentions?dc=${dc}`;
+ const expected = `PUT /v1/connect/intentions/exact?source=SourceNS%2FSourceName&destination=DestinationNS%2FDestinationName&dc=${dc}`;
const actual = adapter
.requestForCreateRecord(
client.url,
{},
{
Datacenter: dc,
- ID: id,
+ SourceNS: 'SourceNS',
+ SourceName: 'SourceName',
+ DestinationNS: 'DestinationNS',
+ DestinationName: 'DestinationName',
}
)
.split('\n')[0];
@@ -54,15 +56,17 @@ module('Integration | Adapter | intention', function(hooks) {
test('requestForUpdateRecord returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:intention');
const client = this.owner.lookup('service:client/http');
- const expected = `PUT /v1/connect/intentions/${legacyId}?dc=${dc}`;
+ const expected = `PUT /v1/connect/intentions/exact?source=SourceNS%2FSourceName&destination=DestinationNS%2FDestinationName&dc=${dc}`;
const actual = adapter
.requestForUpdateRecord(
client.url,
{},
{
Datacenter: dc,
- ID: id,
- LegacyID: legacyId,
+ SourceNS: 'SourceNS',
+ SourceName: 'SourceName',
+ DestinationNS: 'DestinationNS',
+ DestinationName: 'DestinationName',
}
)
.split('\n')[0];
@@ -71,15 +75,17 @@ module('Integration | Adapter | intention', function(hooks) {
test('requestForDeleteRecord returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:intention');
const client = this.owner.lookup('service:client/http');
- const expected = `DELETE /v1/connect/intentions/${legacyId}?dc=${dc}`;
+ const expected = `DELETE /v1/connect/intentions/exact?source=SourceNS%2FSourceName&destination=DestinationNS%2FDestinationName&dc=${dc}`;
const actual = adapter
.requestForDeleteRecord(
client.url,
{},
{
Datacenter: dc,
- ID: id,
- LegacyID: legacyId,
+ SourceNS: 'SourceNS',
+ SourceName: 'SourceName',
+ DestinationNS: 'DestinationNS',
+ DestinationName: 'DestinationName',
}
)
.split('\n')[0];
diff --git a/ui-v2/yarn.lock b/ui-v2/yarn.lock
index 3c85be4f73..0666398c2b 100644
--- a/ui-v2/yarn.lock
+++ b/ui-v2/yarn.lock
@@ -1487,6 +1487,14 @@
"@glimmer/env" "^0.1.7"
"@glimmer/validator" "^0.44.0"
+"@glimmer/tracking@^1.0.1":
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/@glimmer/tracking/-/tracking-1.0.2.tgz#4fe9ca89e3f4a2ae8e37c8bd8e4ea0d886d9abbf"
+ integrity sha512-9Vp04TM2IDTShGFdxccfvnmcaj4NwqLrwbOXm4iju5KL/WkeB8mqoCSLtO3kUg+80DqU0pKE8tR460lQP8CutA==
+ dependencies:
+ "@glimmer/env" "^0.1.7"
+ "@glimmer/validator" "^0.44.0"
+
"@glimmer/util@^0.44.0":
version "0.44.0"
resolved "https://registry.yarnpkg.com/@glimmer/util/-/util-0.44.0.tgz#45df98d73812440206ae7bda87cfe04aaae21ed9"
@@ -1520,14 +1528,14 @@
js-yaml "^3.13.1"
"@hashicorp/consul-api-double@^5.0.0":
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-5.0.1.tgz#07880706ab26cc242332cef86b2c03b3b4ec4e56"
- integrity sha512-uptXq/XTGL5uzGqvwRqC0tzHKCJMVAaRMucPxjbMb4r9wOmOdT4Z2BUJD8GDcCSFIWE8hbWeqAlCXRrokZ3wbw==
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-5.2.1.tgz#a411139fa1afa0dfaf1b9973f21275530a39939b"
+ integrity sha512-ASQv2I8iprnFmpAvbHEoKE8MXTpOxdeBan6nkgobmz4OyvMcqu/h29CGEXZ9j63NX6+nxmE84nV5yAqADRubGQ==
"@hashicorp/ember-cli-api-double@^3.1.0":
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/@hashicorp/ember-cli-api-double/-/ember-cli-api-double-3.1.1.tgz#ba16a514131ce409054d1ae1a71483941d937d37"
- integrity sha512-VLvV/m+Sx+vG+tHK1FeVjiBXwt8KcIWqgFavglrEBTkVTA2o7uP0xN9nKOJjos49KK+h1K3fCwMK5ltz7Kt97w==
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/@hashicorp/ember-cli-api-double/-/ember-cli-api-double-3.1.2.tgz#0eaee116da1431ed63e55eea9ff9c28028cf9f8c"
+ integrity sha512-4j4JxIHBeo5KjTfcEAIrzjtiWBTxPzTTBMiigNgKAIWAtO6Hz58LQ6kDJl8MN52kSq2uSBlFJpp6aQhfwJaPtw==
dependencies:
"@hashicorp/api-double" "^1.6.1"
array-range "^1.0.1"
@@ -2632,6 +2640,11 @@ babel-plugin-htmlbars-inline-precompile@^3.0.1:
resolved "https://registry.yarnpkg.com/babel-plugin-htmlbars-inline-precompile/-/babel-plugin-htmlbars-inline-precompile-3.1.0.tgz#85085b50385277f2b331ebd54e22fa91aadc24e8"
integrity sha512-ar6c4YVX6OV7Dzpq7xRyllQrHwVEzJf41qysYULnD6tu6TS+y1FxT2VcEvMC6b9Rq9xoHMzvB79HO3av89JCGg==
+babel-plugin-htmlbars-inline-precompile@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-htmlbars-inline-precompile/-/babel-plugin-htmlbars-inline-precompile-3.2.0.tgz#c4882ea875d0f5683f0d91c1f72e29a4f14b5606"
+ integrity sha512-IUeZmgs9tMUGXYu1vfke5I18yYJFldFGdNFQOWslXTnDWXzpwPih7QFduUqvT+awDpDuNtXpdt5JAf43Q1Hhzg==
+
babel-plugin-htmlbars-inline-precompile@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/babel-plugin-htmlbars-inline-precompile/-/babel-plugin-htmlbars-inline-precompile-4.1.0.tgz#11796422e65d900a968481fa3fb37e0425c928dd"
@@ -4151,9 +4164,9 @@ caniuse-lite@^1.0.30000792, caniuse-lite@^1.0.30000805, caniuse-lite@^1.0.300010
integrity sha512-PFTw9UyVfbkcMEFs82q8XVlRayj7HKvnhu5BLcmjGpv+SNyiWasCcWXPGJuO0rK0dhLRDJmtZcJ+LHUfypbw1w==
caniuse-lite@^1.0.30000844:
- version "1.0.30001125"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001125.tgz#2a1a51ee045a0a2207474b086f628c34725e997b"
- integrity sha512-9f+r7BW8Qli917mU3j0fUaTweT3f3vnX/Lcs+1C73V+RADmFme+Ih0Br8vONQi3X0lseOe6ZHfsZLCA8MSjxUA==
+ version "1.0.30001137"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001137.tgz#6f0127b1d3788742561a25af3607a17fc778b803"
+ integrity sha512-54xKQZTqZrKVHmVz0+UvdZR6kQc7pJDgfhsMYDG19ID1BWoNnDMFm5Q3uSBSU401pBvKYMsHAt9qhEDcxmk8aw==
capture-exit@^2.0.0:
version "2.0.0"
@@ -5204,9 +5217,9 @@ electron-to-chromium@^1.3.30, electron-to-chromium@^1.3.488:
integrity sha512-EOZuaDT3L1sCIMAVN5J0nGuGWVq5dThrdl0d8XeDYf4MOzbXqZ19OLKesN8TZj0RxtpYjqHpiw/fR6BKWdMwYA==
electron-to-chromium@^1.3.47:
- version "1.3.565"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.565.tgz#8511797ab2b66b767e1aef4eb17d636bf01a2c72"
- integrity sha512-me5dGlHFd8Q7mKhqbWRLIYnKjw4i0fO6hmW0JBxa7tM87fBfNEjWokRnDF7V+Qme/9IYpwhfMn+soWs40tXWqg==
+ version "1.3.575"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.575.tgz#57065cfad7b977a817dba47b28e7eb4dbce3fc37"
+ integrity sha512-031VrjcilnE8bXivDGhEeuGjMZrjTAeyAKm3XWPY9SvGYE6Hn8003gCqoNszFu6lh1v0gDx5hrM0VE1cPSMUkQ==
elliptic@^6.0.0, elliptic@^6.5.2:
version "6.5.3"
@@ -5286,26 +5299,33 @@ ember-basic-dropdown@^3.0.3:
ember-maybe-in-element "^0.4.0"
ember-truth-helpers "2.1.0"
-ember-changeset-validations@^3.0.2:
- version "3.7.0"
- resolved "https://registry.yarnpkg.com/ember-changeset-validations/-/ember-changeset-validations-3.7.0.tgz#74875705128f56ffe22bf54fe6856838dc9caa7d"
- integrity sha512-E4Um4IV5UO72FdT1GWcI8EbNOHE7eS16XFokk6UaKpXs3MLuI1Ev6Zb8Q9Z20g/9/IieBI4XddnGd+1FpXz6aA==
+ember-changeset-conditional-validations@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/ember-changeset-conditional-validations/-/ember-changeset-conditional-validations-0.6.0.tgz#78369ad3af0aea338e00a9bdf1b622fb512d9a00"
+ integrity sha512-U9TZFhhLC+5XRqcI5sNfJwGVcVZvXJxwrRQrrTYLImHe/+tcgP/TagE0f0DBrgWV2u3lsztGHGwGUs86uc65rg==
dependencies:
- ember-changeset "^3.7.0"
+ ember-cli-babel "^6.16.0"
+
+ember-changeset-validations@^3.9.0:
+ version "3.9.1"
+ resolved "https://registry.yarnpkg.com/ember-changeset-validations/-/ember-changeset-validations-3.9.1.tgz#5f44f56ca9a55ac079f667f8cbead1dfeb85a21d"
+ integrity sha512-aTufViqh4zx8WNjiuxuQNSfYaDLDdXl7mQ6g18z2Wma55kEmGymxaltM3lrGgXK+IlWoJvINrNBd6i6AYRxaYA==
+ dependencies:
+ ember-changeset "^3.9.1"
ember-cli-babel "^7.8.0"
ember-cli-htmlbars "^4.0.5"
ember-get-config "^0.2.4"
ember-validators "^2.0.0"
-ember-changeset@^3.7.0:
- version "3.7.1"
- resolved "https://registry.yarnpkg.com/ember-changeset/-/ember-changeset-3.7.1.tgz#5826e2bb7151f85494208aedac25a00400bddf13"
- integrity sha512-vYkF9LHoFwQJb9yytfbA9J888a82/4i+NQUeYtyuLtoD4Zty6Z1NzZ5eWwVU/RbsbK45x85T5FKmsxSS1/1cgw==
+ember-changeset@^3.9.1:
+ version "3.9.1"
+ resolved "https://registry.yarnpkg.com/ember-changeset/-/ember-changeset-3.9.1.tgz#53b50be95364a71f38e68a01c33eba12808cd8a4"
+ integrity sha512-Ntf0fITb2klRZF+5s5xbBQ6HNuSn1IbwppyZPU8v7oh26QOlfjyxYE7QNWM3nZnybHkCrF1iSqN3s8xj3OoFCg==
dependencies:
- "@glimmer/tracking" "^1.0.0"
+ "@glimmer/tracking" "^1.0.1"
ember-auto-import "^1.5.2"
ember-cli-babel "^7.19.0"
- validated-changeset "~0.7.1"
+ validated-changeset "~0.9.1"
ember-cli-app-version@^3.2.0:
version "3.2.0"
@@ -5328,7 +5348,7 @@ ember-cli-babel-plugin-helpers@^1.0.0, ember-cli-babel-plugin-helpers@^1.1.0:
resolved "https://registry.yarnpkg.com/ember-cli-babel-plugin-helpers/-/ember-cli-babel-plugin-helpers-1.1.0.tgz#de3baedd093163b6c2461f95964888c1676325ac"
integrity sha512-Zr4my8Xn+CzO0gIuFNXji0eTRml5AxZUTDQz/wsNJ5AJAtyFWCY4QtKdoELNNbiCVGt1lq5yLiwTm4scGKu6xA==
-ember-cli-babel@7:
+ember-cli-babel@7, ember-cli-babel@^7.8.0:
version "7.22.1"
resolved "https://registry.yarnpkg.com/ember-cli-babel/-/ember-cli-babel-7.22.1.tgz#cad28b89cf0e184c93b863d09bc5ba4ce1d2e453"
integrity sha512-kCT8WbC1AYFtyOpU23ESm22a+gL6fWv8Nzwe8QFQ5u0piJzM9MEudfbjADEaoyKTrjMQTDsrWwEf3yjggDsOng==
@@ -5379,7 +5399,7 @@ ember-cli-babel@^6.0.0, ember-cli-babel@^6.0.0-beta.4, ember-cli-babel@^6.11.0,
ember-cli-version-checker "^2.1.2"
semver "^5.5.0"
-ember-cli-babel@^7.1.0, ember-cli-babel@^7.1.2, ember-cli-babel@^7.1.3, ember-cli-babel@^7.10.0, ember-cli-babel@^7.11.0, ember-cli-babel@^7.11.1, ember-cli-babel@^7.12.0, ember-cli-babel@^7.13.0, ember-cli-babel@^7.17.2, ember-cli-babel@^7.18.0, ember-cli-babel@^7.19.0, ember-cli-babel@^7.20.0, ember-cli-babel@^7.20.5, ember-cli-babel@^7.21.0, ember-cli-babel@^7.7.3, ember-cli-babel@^7.8.0:
+ember-cli-babel@^7.1.0, ember-cli-babel@^7.1.2, ember-cli-babel@^7.1.3, ember-cli-babel@^7.10.0, ember-cli-babel@^7.11.0, ember-cli-babel@^7.11.1, ember-cli-babel@^7.12.0, ember-cli-babel@^7.13.0, ember-cli-babel@^7.17.2, ember-cli-babel@^7.18.0, ember-cli-babel@^7.19.0, ember-cli-babel@^7.20.0, ember-cli-babel@^7.20.5, ember-cli-babel@^7.21.0, ember-cli-babel@^7.7.3:
version "7.21.0"
resolved "https://registry.yarnpkg.com/ember-cli-babel/-/ember-cli-babel-7.21.0.tgz#c79e888876aee87dfc3260aee7cb580b74264bbc"
integrity sha512-jHVi9melAibo0DrAG3GAxid+29xEyjBoU53652B4qcu3Xp58feZGTH/JGXovH7TjvbeNn65zgNyoV3bk1onULw==
@@ -5505,7 +5525,27 @@ ember-cli-htmlbars@^3.0.0, ember-cli-htmlbars@^3.0.1:
json-stable-stringify "^1.0.1"
strip-bom "^3.0.0"
-ember-cli-htmlbars@^4.0.5, ember-cli-htmlbars@^4.2.0, ember-cli-htmlbars@^4.2.2, ember-cli-htmlbars@^4.3.1:
+ember-cli-htmlbars@^4.0.5:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/ember-cli-htmlbars/-/ember-cli-htmlbars-4.4.0.tgz#7ca17d5ca8f7550984346d9e6e93da0c3323f8d9"
+ integrity sha512-ohgctqk7dXIZR4TgN0xRoUYltWhghFJgqmtuswQTpZ7p74RxI9PKx+E8WV/95mGcPzraesvMNBg5utQNvcqgNg==
+ dependencies:
+ "@ember/edition-utils" "^1.2.0"
+ babel-plugin-htmlbars-inline-precompile "^3.2.0"
+ broccoli-debug "^0.6.5"
+ broccoli-persistent-filter "^2.3.1"
+ broccoli-plugin "^3.1.0"
+ common-tags "^1.8.0"
+ ember-cli-babel-plugin-helpers "^1.1.0"
+ fs-tree-diff "^2.0.1"
+ hash-for-dep "^1.5.1"
+ heimdalljs-logger "^0.1.10"
+ json-stable-stringify "^1.0.1"
+ semver "^6.3.0"
+ strip-bom "^4.0.0"
+ walk-sync "^2.0.2"
+
+ember-cli-htmlbars@^4.2.0, ember-cli-htmlbars@^4.2.2, ember-cli-htmlbars@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/ember-cli-htmlbars/-/ember-cli-htmlbars-4.3.1.tgz#4af8adc21ab3c4953f768956b7f7d207782cb175"
integrity sha512-CW6AY/yzjeVqoRtItOKj3hcYzc5dWPRETmeCzr2Iqjt5vxiVtpl0z5VTqHqIlT5fsFx6sGWBQXNHIe+ivYsxXQ==
@@ -12957,10 +12997,10 @@ validate-npm-package-name@^3.0.0:
dependencies:
builtins "^1.0.3"
-validated-changeset@~0.7.1:
- version "0.7.1"
- resolved "https://registry.yarnpkg.com/validated-changeset/-/validated-changeset-0.7.1.tgz#cb2c11c93d5acfb2286e5bfca07f4a516f83c844"
- integrity sha512-BbFK98Cp7WunEwLOW/oAi6qDZZFBOLkae0q5RZ3ne8ZkwB1sskOYfF5IqQhqubwxRb4emMhA2UgN5rdfOaZxXQ==
+validated-changeset@~0.9.1:
+ version "0.9.1"
+ resolved "https://registry.yarnpkg.com/validated-changeset/-/validated-changeset-0.9.1.tgz#bb4773c7c5392dcc7ecfbc8ccc10c7fef2840d42"
+ integrity sha512-gEMvF+GN8ECLndHw5Ehc9ckXMgM+RRDK5+lCx2hGX9Qie1q68ixLEtbNXbPPhV4JHq6d7krVfTG+sEQ3X6zoHA==
vary@~1.1.2:
version "1.1.2"