mirror of
https://github.com/hashicorp/vault.git
synced 2026-05-05 04:16:31 +02:00
UI - add delete for the various kmip models (#7015)
* add menu-loader component to show menu loading button when the model relationship isPending * list what keys we've got in api-path error * fix spacing issue on error flash * add an action on list-controller that bubbles to the list-route mixin to refresh the route * empty store when creating scopes * don't delete _requestQuery in the loop, do it after * add scope deletion from the scope list * add deleteRecord to kmip adapters * add model-wrap component * delete role from detail page and list * add revoke credentials functionality * fix comment * treat all operations fields specially on kmip roles * adjust kmip role edit form for new fields * fix api-path test * update document blocks for menu-loader and model-wrap components
This commit is contained in:
parent
88cb465184
commit
5e002bec87
@ -13,4 +13,18 @@ export default BaseAdapter.extend({
|
||||
return model;
|
||||
});
|
||||
},
|
||||
|
||||
deleteRecord(store, type, snapshot) {
|
||||
let url = this._url(type.modelName, {
|
||||
backend: snapshot.record.backend,
|
||||
scope: snapshot.record.scope,
|
||||
role: snapshot.record.role,
|
||||
});
|
||||
url = `${url}/revoke`;
|
||||
return this.ajax(url, 'POST', {
|
||||
data: {
|
||||
serial_number: snapshot.id,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import BaseAdapter from './base';
|
||||
import { decamelize } from '@ember/string';
|
||||
import { getProperties } from '@ember/object';
|
||||
|
||||
export default BaseAdapter.extend({
|
||||
createRecord(store, type, snapshot) {
|
||||
@ -15,19 +17,47 @@ export default BaseAdapter.extend({
|
||||
return {
|
||||
id: name,
|
||||
name,
|
||||
backend: snapshot.record.backend,
|
||||
scope: snapshot.record.scope,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
deleteRecord(store, type, snapshot) {
|
||||
let name = snapshot.id || snapshot.attr('name');
|
||||
let url = this._url(
|
||||
type.modelName,
|
||||
{
|
||||
backend: snapshot.record.backend,
|
||||
scope: snapshot.record.scope,
|
||||
},
|
||||
name
|
||||
);
|
||||
return this.ajax(url, 'DELETE');
|
||||
},
|
||||
|
||||
serialize(snapshot) {
|
||||
// the endpoint here won't allow sending `operation_all` and `operation_none` at the same time or with
|
||||
// other values, so we manually check for them and send an abbreviated object
|
||||
let json = snapshot.serialize();
|
||||
let keys = snapshot.record.nonOperationFields.map(decamelize);
|
||||
let nonOperationFields = getProperties(json, keys);
|
||||
for (let field in nonOperationFields) {
|
||||
if (nonOperationFields[field] == null) {
|
||||
delete nonOperationFields[field];
|
||||
}
|
||||
}
|
||||
if (json.operation_all) {
|
||||
return { operation_all: true };
|
||||
return {
|
||||
operation_all: true,
|
||||
...nonOperationFields,
|
||||
};
|
||||
}
|
||||
if (json.operation_none) {
|
||||
return { operation_none: true };
|
||||
return {
|
||||
operation_none: true,
|
||||
...nonOperationFields,
|
||||
};
|
||||
}
|
||||
delete json.operation_none;
|
||||
delete json.operation_all;
|
||||
|
||||
@ -12,4 +12,8 @@ export default BaseAdapter.extend({
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
deleteRecord(store, type, snapshot) {
|
||||
return this.ajax(this._url(type.modelName, { backend: snapshot.record.backend }, snapshot.id), 'DELETE');
|
||||
},
|
||||
});
|
||||
|
||||
@ -2,8 +2,10 @@ import DS from 'ember-data';
|
||||
import fieldToAttrs from 'vault/utils/field-to-attrs';
|
||||
import { computed } from '@ember/object';
|
||||
const { attr } = DS;
|
||||
import apiPath from 'vault/utils/api-path';
|
||||
import attachCapabilities from 'vault/lib/attach-capabilities';
|
||||
|
||||
export default DS.Model.extend({
|
||||
const Model = DS.Model.extend({
|
||||
backend: attr({ readOnly: true }),
|
||||
scope: attr({ readOnly: true }),
|
||||
role: attr({ readOnly: true }),
|
||||
@ -28,3 +30,7 @@ export default DS.Model.extend({
|
||||
return fieldToAttrs(this, groups);
|
||||
}),
|
||||
});
|
||||
|
||||
export default attachCapabilities(Model, {
|
||||
deletePath: apiPath`${'backend'}/scope/${'scope'}/role/${'role'}/credentials/revoke`,
|
||||
});
|
||||
|
||||
@ -6,24 +6,40 @@ import apiPath from 'vault/utils/api-path';
|
||||
import attachCapabilities from 'vault/lib/attach-capabilities';
|
||||
|
||||
const { attr } = DS;
|
||||
const Model = DS.Model.extend({
|
||||
export const COMPUTEDS = {
|
||||
operationFields: computed('newFields', function() {
|
||||
return this.newFields.filter(key => key.startsWith('operation'));
|
||||
}),
|
||||
|
||||
operationFieldsWithoutSpecial: computed('operationFields', function() {
|
||||
return this.operationFields.slice().removeObjects(['operationAll', 'operationNone']);
|
||||
}),
|
||||
|
||||
nonOperationFields: computed('operationFields', function() {
|
||||
let excludeFields = ['role'].concat(this.operationFields);
|
||||
return this.newFields.slice().removeObjects(excludeFields);
|
||||
}),
|
||||
};
|
||||
|
||||
const Model = DS.Model.extend(COMPUTEDS, {
|
||||
useOpenAPI: true,
|
||||
backend: attr({ readOnly: true }),
|
||||
scope: attr({ readOnly: true }),
|
||||
name: attr({ readOnly: true }),
|
||||
getHelpUrl(path) {
|
||||
return `/v1/${path}/scope/example/role/example?help=1`;
|
||||
},
|
||||
|
||||
name: attr({ readOnly: true }),
|
||||
fieldGroups: computed(function() {
|
||||
let fields = this.newFields.without('role');
|
||||
const groups = [{ 'Allowed Operations': fields }];
|
||||
return fieldToAttrs(this, groups);
|
||||
fieldGroups: computed('fields', 'nonOperationFields', function() {
|
||||
const groups = [{ default: this.nonOperationFields }, { 'Allowed Operations': this.operationFields }];
|
||||
let ret = fieldToAttrs(this, groups);
|
||||
return ret;
|
||||
}),
|
||||
|
||||
fields: computed(function() {
|
||||
let fields = this.newFields.removeObjects(['role', 'operationAll', 'operationNone']);
|
||||
return expandAttributeMeta(this, fields);
|
||||
operationFormFields: computed('operationFieldsWithoutSpecial', function() {
|
||||
return expandAttributeMeta(this, this.operationFieldsWithoutSpecial);
|
||||
}),
|
||||
fields: computed('nonOperationFields', function() {
|
||||
return expandAttributeMeta(this, this.nonOperationFields);
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@ -1,12 +1,19 @@
|
||||
import { computed } from '@ember/object';
|
||||
import DS from 'ember-data';
|
||||
import apiPath from 'vault/utils/api-path';
|
||||
import attachCapabilities from 'vault/lib/attach-capabilities';
|
||||
|
||||
const { attr } = DS;
|
||||
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
|
||||
|
||||
export default DS.Model.extend({
|
||||
let Model = DS.Model.extend({
|
||||
name: attr('string'),
|
||||
backend: attr({ readOnly: true }),
|
||||
attrs: computed(function() {
|
||||
return expandAttributeMeta(this, ['name']);
|
||||
}),
|
||||
});
|
||||
|
||||
export default attachCapabilities(Model, {
|
||||
updatePath: apiPath`${'backend'}/scope/${'id'}`,
|
||||
});
|
||||
|
||||
@ -16,11 +16,10 @@ export default DS.JSONSerializer.extend({
|
||||
}
|
||||
let pk = this.get('primaryKey') || 'id';
|
||||
let model = { [pk]: key };
|
||||
// if we've added a in the adapter, we want
|
||||
// if we've added _requestQuery in the adapter, we want
|
||||
// attach it to the individual models
|
||||
if (payload._requestQuery) {
|
||||
model = { ...model, ...payload._requestQuery };
|
||||
delete payload._requestQuery;
|
||||
}
|
||||
return model;
|
||||
});
|
||||
@ -44,6 +43,7 @@ export default DS.JSONSerializer.extend({
|
||||
|
||||
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
|
||||
const responseJSON = this.normalizeItems(payload, requestType);
|
||||
delete payload._requestQuery;
|
||||
if (id && !responseJSON.id) {
|
||||
responseJSON.id = id;
|
||||
}
|
||||
|
||||
@ -13,8 +13,8 @@ export default function apiPath(strings, ...keys) {
|
||||
let dict = data || {};
|
||||
let result = [strings[0]];
|
||||
assert(
|
||||
`Expected ${keys.length} keys in apiPath context, only recieved ${Object.keys(data).length}`,
|
||||
keys.length === Object.keys(data).length
|
||||
`Expected ${keys.length} keys in apiPath context, only recieved ${Object.keys(data).join(',')}`,
|
||||
Object.keys(data).length >= keys.length
|
||||
);
|
||||
keys.forEach((key, i) => {
|
||||
result.push(dict[key], strings[i + 1]);
|
||||
|
||||
@ -19,7 +19,7 @@ export default Component.extend({
|
||||
successCallback();
|
||||
} catch (e) {
|
||||
let errString = e.errors.join(' ');
|
||||
flash.danger(failureMessage + errString);
|
||||
flash.danger(failureMessage + ' ' + errString);
|
||||
model.rollbackAttributes();
|
||||
}
|
||||
}),
|
||||
|
||||
22
ui/lib/core/addon/components/menu-loader.js
Normal file
22
ui/lib/core/addon/components/menu-loader.js
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @module MenuLoader
|
||||
* MenuLoader components are used to show a loading state when fetching data is triggered by opening a
|
||||
* popup menu.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* <MenuLoader @loadingParam={model.updatePath.isPending} />
|
||||
* ```
|
||||
*
|
||||
* @param loadingParam {Boolean} - If the value of this param is true, the loading state will be rendered,
|
||||
* else the component will yield.
|
||||
*/
|
||||
import Component from '@ember/component';
|
||||
import layout from '../templates/components/menu-loader';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'li',
|
||||
classNames: 'action',
|
||||
layout,
|
||||
loadingParam: null,
|
||||
});
|
||||
37
ui/lib/core/addon/components/model-wrap.js
Normal file
37
ui/lib/core/addon/components/model-wrap.js
Normal file
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* @module ModelWrap
|
||||
* ModelWrap components provide a way to call methods on models directly from templates. This is done by yielding callMethod task to the wrapped component.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* <ModelWrap as |m|>
|
||||
<button onclick={{action (perform m.callMethod "save" model "Saved!" "Errored!" (transition-to "route")}}>
|
||||
* </ModelWrap>
|
||||
* ```
|
||||
*
|
||||
* @yields callMethod {Function}
|
||||
*
|
||||
*/
|
||||
import { inject as service } from '@ember/service';
|
||||
import Component from '@ember/component';
|
||||
import { task } from 'ember-concurrency';
|
||||
import layout from '../templates/components/model-wrap';
|
||||
|
||||
export default Component.extend({
|
||||
layout,
|
||||
flashMessages: service(),
|
||||
tagName: '',
|
||||
|
||||
callMethod: task(function*(method, model, successMessage, failureMessage, successCallback = () => {}) {
|
||||
let flash = this.get('flashMessages');
|
||||
try {
|
||||
yield model[method]();
|
||||
flash.success(successMessage);
|
||||
successCallback();
|
||||
} catch (e) {
|
||||
let errString = e.errors.join(' ');
|
||||
flash.danger(failureMessage + ' ' + errString);
|
||||
model.rollbackAttributes();
|
||||
}
|
||||
}),
|
||||
});
|
||||
@ -48,5 +48,9 @@ export default Mixin.create({
|
||||
setFilterFocus(bool) {
|
||||
this.set('filterFocused', bool);
|
||||
},
|
||||
refresh() {
|
||||
// bubble to the list-route
|
||||
this.send('reload');
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
7
ui/lib/core/addon/templates/components/menu-loader.hbs
Normal file
7
ui/lib/core/addon/templates/components/menu-loader.hbs
Normal file
@ -0,0 +1,7 @@
|
||||
{{#if @loadingParam}}
|
||||
<button disabled type="button" class="link button is-loading is-transparent">
|
||||
loading
|
||||
</button>
|
||||
{{else}}
|
||||
{{yield}}
|
||||
{{/if}}
|
||||
1
ui/lib/core/addon/templates/components/model-wrap.hbs
Normal file
1
ui/lib/core/addon/templates/components/model-wrap.hbs
Normal file
@ -0,0 +1 @@
|
||||
{{yield (hash callMethod=callMethod)}}
|
||||
1
ui/lib/core/app/components/menu-loader.js
Normal file
1
ui/lib/core/app/components/menu-loader.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from 'core/components/menu-loader';
|
||||
1
ui/lib/core/app/components/model-wrap.js
Normal file
1
ui/lib/core/app/components/model-wrap.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from 'core/components/model-wrap';
|
||||
@ -40,7 +40,7 @@ export default EditForm.extend({
|
||||
model.set('operationAll', null);
|
||||
return resolve(model);
|
||||
}
|
||||
model.newFields.without('role').forEach(field => {
|
||||
model.operationFields.concat(['operationAll', 'operationNone']).forEach(field => {
|
||||
// this will set operationAll or operationNone to true
|
||||
if (field === display) {
|
||||
model.set(field, true);
|
||||
|
||||
@ -9,6 +9,7 @@ export default Route.extend({
|
||||
return this.paramsFor('scope').scope_name;
|
||||
},
|
||||
beforeModel() {
|
||||
this.store.unloadAll('kmip/role');
|
||||
return this.pathHelp.getNewModel('kmip/role', this.secretMountPath.currentPath);
|
||||
},
|
||||
model() {
|
||||
|
||||
@ -4,6 +4,9 @@ import { inject as service } from '@ember/service';
|
||||
export default Route.extend({
|
||||
store: service(),
|
||||
secretMountPath: service(),
|
||||
beforeModel() {
|
||||
this.store.unloadAll('kmip/scope');
|
||||
},
|
||||
model() {
|
||||
let model = this.store.createRecord('kmip/scope', {
|
||||
backend: this.secretMountPath.currentPath,
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
{{/each}}
|
||||
{{#if (eq this.display "choose")}}
|
||||
<div class="box is-sideless is-shadowless is-marginless">
|
||||
{{#each this.model.fields as |attr|}}
|
||||
{{#each this.model.operationFormFields as |attr|}}
|
||||
<FormField
|
||||
data-test-field
|
||||
@attr={{attr}}
|
||||
@ -43,6 +43,14 @@
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#each this.model.fields as |attr|}}
|
||||
<FormField
|
||||
data-test-field
|
||||
@attr={{attr}}
|
||||
@model={{model}}
|
||||
/>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="field is-grouped is-grouped-split is-fullwidth box is-bottomless">
|
||||
<div class="field is-grouped">
|
||||
|
||||
@ -59,6 +59,29 @@
|
||||
View credentials
|
||||
{{/link-to}}
|
||||
</li>
|
||||
{{#if list.item.deletePath.canDelete}}
|
||||
<MenuLoader @loadingParam={{list.item.deletePath.isPending}}>
|
||||
<ConfirmAction
|
||||
@buttonClasses="link is-destroy"
|
||||
@onConfirmAction={{action
|
||||
(perform
|
||||
Item.callMethod
|
||||
"destroyRecord"
|
||||
list.item
|
||||
"Successfully revoked credentials"
|
||||
"There was an error revoking the credentials"
|
||||
(action "refresh")
|
||||
)
|
||||
}}
|
||||
@confirmTitle="Revoke this?"
|
||||
@confirmMessage="Any client using these credentials will no longer be able to."
|
||||
@cancelButtonText="Cancel"
|
||||
@confirmButtonText="Revoke"
|
||||
>
|
||||
Revoke credentials
|
||||
</ConfirmAction>
|
||||
</MenuLoader>
|
||||
{{/if}}
|
||||
</Item.menu>
|
||||
</ListItem>
|
||||
{{else}}
|
||||
|
||||
@ -15,6 +15,29 @@
|
||||
>
|
||||
Back to role
|
||||
</ToolbarLink>
|
||||
{{#if model.deletePath.canDelete}}
|
||||
<ModelWrap as |m|>
|
||||
<ConfirmAction
|
||||
@buttonClasses="toolbar-link"
|
||||
@onConfirmAction={{action
|
||||
(perform
|
||||
m.callMethod
|
||||
"destroyRecord"
|
||||
model
|
||||
"Successfully revoked credentials"
|
||||
"There was an error revoking credentials"
|
||||
(transition-to "vault.cluster.secrets.backend.kmip.credentials.index" this.scope this.role)
|
||||
)
|
||||
}}
|
||||
@confirmTitle="Revoke this?"
|
||||
@confirmMessage="Any client using these credentials will no longer be able to."
|
||||
@cancelButtonText="Cancel"
|
||||
@confirmButtonText="Revoke"
|
||||
>
|
||||
Revoke credentials
|
||||
</ConfirmAction>
|
||||
</ModelWrap>
|
||||
{{/if}}
|
||||
<CopyButton
|
||||
class="toolbar-link"
|
||||
@clipboardText={{model.certificate}}
|
||||
|
||||
@ -1,11 +1,34 @@
|
||||
<HeaderCredentials @role={{this.role}} @scope={{this.scope}} />
|
||||
<Toolbar>
|
||||
<ToolbarActions>
|
||||
<ToolbarLink
|
||||
@params={{array "role.edit" this.scope this.role}}
|
||||
>
|
||||
Edit role
|
||||
</ToolbarLink>
|
||||
{{#if model.updatePath.canUpdate}}
|
||||
<ModelWrap as |m|>
|
||||
<ConfirmAction
|
||||
@buttonClasses="toolbar-link"
|
||||
@onConfirmAction={{action
|
||||
(perform
|
||||
m.callMethod
|
||||
"destroyRecord"
|
||||
model
|
||||
(concat "Successfully deleted role " model.id)
|
||||
(concat "There was an error deleting the role " model.id)
|
||||
(transition-to "vault.cluster.secrets.backend.kmip.scope.roles" this.scope)
|
||||
)
|
||||
}}
|
||||
@confirmMessage={{concat "Are you sure you want to delete " model.id "?"}}
|
||||
@cancelButtonText="Cancel"
|
||||
>
|
||||
Delete role
|
||||
</ConfirmAction>
|
||||
</ModelWrap>
|
||||
{{/if}}
|
||||
{{#if model.updatePath.canUpdate}}
|
||||
<ToolbarLink
|
||||
@params={{array "role.edit" this.scope this.role}}
|
||||
>
|
||||
Edit role
|
||||
</ToolbarLink>
|
||||
{{/if}}
|
||||
</ToolbarActions>
|
||||
</Toolbar>
|
||||
<div class="box is-fullwidth is-sideless is-shadowless">
|
||||
|
||||
@ -74,6 +74,35 @@
|
||||
View role
|
||||
{{/link-to}}
|
||||
</li>
|
||||
{{#if list.item.updatePath.canUpdate}}
|
||||
<MenuLoader @loadingParam={{list.item.updatePath.isPending}}>
|
||||
{{#link-to "role.edit" this.scope list.item.id class="is-block"}}
|
||||
Edit role
|
||||
{{/link-to}}
|
||||
</MenuLoader>
|
||||
{{/if}}
|
||||
{{#if list.item.updatePath.canDelete}}
|
||||
<MenuLoader @loadingParam={{list.item.updatePath.isPending}}>
|
||||
<ConfirmAction
|
||||
@buttonClasses="link is-destroy"
|
||||
@onConfirmAction={{action
|
||||
(perform
|
||||
Item.callMethod
|
||||
"destroyRecord"
|
||||
list.item
|
||||
(concat "Successfully deleted role " list.item.id)
|
||||
(concat "There was an error deleting the role " list.item.id)
|
||||
(action "refresh")
|
||||
)
|
||||
}}
|
||||
@confirmMessage={{concat "Are you sure you want to delete " list.item.id "?"}}
|
||||
@cancelButtonText="Cancel"
|
||||
data-test-scope-delete="true"
|
||||
>
|
||||
Delete role
|
||||
</ConfirmAction>
|
||||
</MenuLoader>
|
||||
{{/if}}
|
||||
</Item.menu>
|
||||
</ListItem>
|
||||
{{else}}
|
||||
|
||||
@ -59,6 +59,28 @@
|
||||
View scope
|
||||
{{/link-to}}
|
||||
</li>
|
||||
{{#if list.item.updatePath.canDelete}}
|
||||
<MenuLoader @loadingParam={{list.item.updatePath.isPending}}>
|
||||
<ConfirmAction
|
||||
@buttonClasses="link is-destroy"
|
||||
@onConfirmAction={{action
|
||||
(perform
|
||||
Item.callMethod
|
||||
"destroyRecord"
|
||||
list.item
|
||||
(concat "Successfully deleted scope " list.item.id)
|
||||
(concat "There was an error deleting the scope " list.item.id)
|
||||
(action "refresh")
|
||||
)
|
||||
}}
|
||||
@confirmMessage={{concat "Are you sure you want to delete " list.item.id "?"}}
|
||||
@cancelButtonText="Cancel"
|
||||
data-test-scope-delete="true"
|
||||
>
|
||||
Delete scope
|
||||
</ConfirmAction>
|
||||
</MenuLoader>
|
||||
{{/if}}
|
||||
</Item.menu>
|
||||
</ListItem>
|
||||
{{else}}
|
||||
|
||||
@ -8,6 +8,7 @@ import { render, settled, click } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import sinon from 'sinon';
|
||||
import engineResolverFor from 'ember-engines/test-support/engine-resolver-for';
|
||||
import { COMPUTEDS } from 'vault/models/kmip/role';
|
||||
const resolver = engineResolverFor('kmip');
|
||||
|
||||
const flash = Service.extend({
|
||||
@ -16,12 +17,29 @@ const flash = Service.extend({
|
||||
const namespace = Service.extend({});
|
||||
|
||||
const createModel = options => {
|
||||
let model = EmberObject.extend({
|
||||
fields: computed('newFields', function() {
|
||||
return this.newFields.map(field => ({ name: field, type: 'boolean' }));
|
||||
}),
|
||||
let model = EmberObject.extend(COMPUTEDS, {
|
||||
/* eslint-disable ember/avoid-leaking-state-in-ember-objects */
|
||||
newFields: ['operationAll', 'operationNone', 'operationGet', 'operationCreate', 'operationDestroy'],
|
||||
newFields: [
|
||||
'role',
|
||||
'operationActivate',
|
||||
'operationAddAttribute',
|
||||
'operationAll',
|
||||
'operationCreate',
|
||||
'operationDestroy',
|
||||
'operationDiscoverVersion',
|
||||
'operationGet',
|
||||
'operationGetAttributes',
|
||||
'operationLocate',
|
||||
'operationNone',
|
||||
'operationRekey',
|
||||
'operationRevoke',
|
||||
'tlsClientKeyBits',
|
||||
'tlsClientKeyType',
|
||||
'tlsClientTtl',
|
||||
],
|
||||
fields: computed('operationFields', function() {
|
||||
return this.operationFields.map(field => ({ name: field, type: 'boolean' }));
|
||||
}),
|
||||
destroyRecord() {
|
||||
return resolve();
|
||||
},
|
||||
|
||||
@ -6,25 +6,47 @@ module('Unit | Adapter | kmip/role', function(hooks) {
|
||||
|
||||
let serializeTests = [
|
||||
[
|
||||
'operation_all is the only item present after serialization',
|
||||
'operation_all is the only operation item present after serialization',
|
||||
{
|
||||
serialize() {
|
||||
return { operation_all: true, operation_get: true, operation_create: true, tls_ttl: '10s' };
|
||||
},
|
||||
record: {
|
||||
nonOperationFields: ['tlsTtl'],
|
||||
},
|
||||
},
|
||||
{
|
||||
operation_all: true,
|
||||
tls_ttl: '10s',
|
||||
},
|
||||
],
|
||||
[
|
||||
'serialize does not include nonOperationFields values if they are not set',
|
||||
{
|
||||
serialize() {
|
||||
return { operation_all: true, operation_get: true, operation_create: true };
|
||||
},
|
||||
record: {
|
||||
nonOperationFields: ['tlsTtl'],
|
||||
},
|
||||
},
|
||||
{
|
||||
operation_all: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
'operation_none is the only item present after serialization',
|
||||
'operation_none is the only operation item present after serialization',
|
||||
{
|
||||
serialize() {
|
||||
return { operation_none: true, operation_get: true, operation_add_attribute: true };
|
||||
return { operation_none: true, operation_get: true, operation_add_attribute: true, tls_ttl: '10s' };
|
||||
},
|
||||
record: {
|
||||
nonOperationFields: ['tlsTtl'],
|
||||
},
|
||||
},
|
||||
{
|
||||
operation_none: true,
|
||||
tls_ttl: '10s',
|
||||
},
|
||||
],
|
||||
[
|
||||
@ -39,6 +61,9 @@ module('Unit | Adapter | kmip/role', function(hooks) {
|
||||
operation_destroy: true,
|
||||
};
|
||||
},
|
||||
record: {
|
||||
nonOperationFields: ['tlsTtl'],
|
||||
},
|
||||
},
|
||||
{
|
||||
operation_get: true,
|
||||
@ -49,7 +74,6 @@ module('Unit | Adapter | kmip/role', function(hooks) {
|
||||
];
|
||||
for (let testCase of serializeTests) {
|
||||
let [name, snapshotStub, expected] = testCase;
|
||||
|
||||
test(`adapter serialize: ${name}`, function(assert) {
|
||||
let adapter = this.owner.lookup('adapter:kmip/role');
|
||||
let result = adapter.serialize(snapshotStub);
|
||||
|
||||
@ -18,6 +18,6 @@ module('Unit | Util | api path', function() {
|
||||
let ret = apiPath`foo/${'one'}/${'two'}`;
|
||||
assert.throws(() => {
|
||||
ret({ one: 1 });
|
||||
}, /Error: Assertion Failed: Expected 2 keys in apiPath context, only recieved 1/);
|
||||
}, /Error: Assertion Failed: Expected 2 keys in apiPath context, only recieved one/);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user