finish v2 model layer and add some unit tests for adapters
This commit is contained in:
parent
b2e24ce2b1
commit
b74ed60497
|
@ -1,10 +1,11 @@
|
|||
/* eslint-disable */
|
||||
import { isEmpty } from '@ember/utils';
|
||||
import ApplicationAdapter from './application';
|
||||
|
||||
export default ApplicationAdapter.extend({
|
||||
namespace: 'v1',
|
||||
_url(backend, id) {
|
||||
let url = `${this.buildURL()}/${backend}/data/`;
|
||||
_url(backend, id, infix = 'data') {
|
||||
let url = `${this.buildURL()}/${backend}/${infix}/`;
|
||||
if (!isEmpty(id)) {
|
||||
url = url + id;
|
||||
}
|
||||
|
@ -16,8 +17,37 @@ export default ApplicationAdapter.extend({
|
|||
return this._url(backend, path) + `?version=${version}`;
|
||||
},
|
||||
|
||||
urlForCreateRecord(modelName, snapshot) {
|
||||
let backend = snapshot.belongsTo('secret').belongsTo('engine').id;
|
||||
let path = snapshot.attr('path');
|
||||
return this._url(backend, path);
|
||||
},
|
||||
|
||||
createRecord(store, modelName, snapshot) {
|
||||
let backend = snapshot.belongsTo('secret').belongsTo('engine').id;
|
||||
let path = snapshot.attr('path');
|
||||
return this._super(...arguments).then(resp => {
|
||||
resp.id = JSON.stringify([backend, path, resp.version]);
|
||||
return resp;
|
||||
});
|
||||
},
|
||||
|
||||
urlForUpdateRecord(id) {
|
||||
let [backend, path] = JSON.parse(id);
|
||||
return this._url(backend, path);
|
||||
},
|
||||
|
||||
deleteRecord(store, type, snapshot) {
|
||||
// use adapterOptions to determine if it's delete or destroy for the version
|
||||
// deleteType should be 'delete', 'destroy', 'undelete'
|
||||
let infix = snapshot.adapterOptions.deleteType;
|
||||
let [backend, path, version] = JSON.parse(snapshot.id);
|
||||
|
||||
return this.ajax(this._url(backend, path, infix), 'POST', { data: { versions: [version] } });
|
||||
},
|
||||
|
||||
handleResponse(/*status, headers, payload, requestData*/) {
|
||||
// the body of the 404 will have some relevant information
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -16,26 +16,35 @@ export default ApplicationAdapter.extend({
|
|||
// concerns and we only want to send "list" to the server
|
||||
query(store, type, query) {
|
||||
let { backend, id } = query;
|
||||
return this.ajax(this._url(backend, id), 'GET', { data: { list: true } });
|
||||
return this.ajax(this._url(backend, id), 'GET', { data: { list: true } }).then(resp => {
|
||||
resp.id = id;
|
||||
return resp;
|
||||
});
|
||||
},
|
||||
|
||||
urlForQueryRecord(query) {
|
||||
let { id, backend } = query;
|
||||
return this._url(backend) + id;
|
||||
return this._url(backend, id);
|
||||
},
|
||||
|
||||
queryRecord(store, type, query) {
|
||||
let { backend, id } = query;
|
||||
return this._super(...arguments).then(resp => {
|
||||
return this.ajax(this._url(backend, id), 'GET').then(resp => {
|
||||
resp.id = id;
|
||||
resp.backend = backend;
|
||||
return resp;
|
||||
});
|
||||
},
|
||||
|
||||
urlForDeleteRecord(store, type, snapshot) {
|
||||
let backend = snapshot.belongsTo('secret-engine', { id: true });
|
||||
urlForUpdateRecord(store, type, snapshot) {
|
||||
let backend = snapshot.belongsTo('engine', { id: true });
|
||||
let { id } = snapshot;
|
||||
return this.urlForQueryRecord({ id, backend });
|
||||
return this._url(backend, id);
|
||||
},
|
||||
|
||||
urlForDeleteRecord(store, type, snapshot) {
|
||||
let backend = snapshot.belongsTo('engine', { id: true });
|
||||
let { id } = snapshot;
|
||||
return this._url(backend, id);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -3,6 +3,9 @@ import Mixin from '@ember/object/mixin';
|
|||
import utils from '../lib/key-utils';
|
||||
|
||||
export default Mixin.create({
|
||||
// what attribute has the path for the key
|
||||
// will.be 'path' for v2 or 'id' v1
|
||||
pathAttr: 'id',
|
||||
flags: null,
|
||||
|
||||
initialParentKey: null,
|
||||
|
@ -11,33 +14,39 @@ export default Mixin.create({
|
|||
return this.get('initialParentKey') != null;
|
||||
}),
|
||||
|
||||
isFolder: computed('id', function() {
|
||||
return utils.keyIsFolder(this.get('id'));
|
||||
pathVal() {
|
||||
return this.get(this.pathAttr);
|
||||
},
|
||||
|
||||
// rather than using defineProperty for all of these,
|
||||
// we're just going to hardcode the known keys for the path ('id' and 'path')
|
||||
isFolder: computed('id', 'path', function() {
|
||||
return utils.keyIsFolder(this.pathVal());
|
||||
}),
|
||||
|
||||
keyParts: computed('id', function() {
|
||||
return utils.keyPartsForKey(this.get('id'));
|
||||
keyParts: computed('id', 'path', function() {
|
||||
return utils.keyPartsForKey(this.pathVal());
|
||||
}),
|
||||
|
||||
parentKey: computed('id', 'isCreating', {
|
||||
parentKey: computed('id', 'path', 'isCreating', {
|
||||
get: function() {
|
||||
return this.get('isCreating') ? this.get('initialParentKey') : utils.parentKeyForKey(this.get('id'));
|
||||
return this.isCreating ? this.initialParentKey : utils.parentKeyForKey(this.pathVal());
|
||||
},
|
||||
set: function(_, value) {
|
||||
return value;
|
||||
},
|
||||
}),
|
||||
|
||||
keyWithoutParent: computed('id', 'parentKey', {
|
||||
keyWithoutParent: computed('id', 'path', 'parentKey', {
|
||||
get: function() {
|
||||
var key = this.get('id');
|
||||
return key ? key.replace(this.get('parentKey'), '') : null;
|
||||
var key = this.pathVal();
|
||||
return key ? key.replace(this.parentKey, '') : null;
|
||||
},
|
||||
set: function(_, value) {
|
||||
if (value && value.trim()) {
|
||||
this.set('id', this.get('parentKey') + value);
|
||||
this.set(this.pathAttr, this.parentKey + value);
|
||||
} else {
|
||||
this.set('id', null);
|
||||
this.set(this.pathAttr, null);
|
||||
}
|
||||
return value;
|
||||
},
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import Secret from './secret';
|
||||
import DS from 'ember-data';
|
||||
|
||||
const { attr } = DS;
|
||||
const { attr, belongsTo } = DS;
|
||||
|
||||
export default Secret.extend({
|
||||
pathAttr: 'path',
|
||||
version: attr('number'),
|
||||
secret: belongsTo('secret-v2'),
|
||||
path: attr('string'),
|
||||
currentVersion: attr('number'),
|
||||
});
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
import Secret from './secret';
|
||||
import DS from 'ember-data';
|
||||
import { match } from '@ember/object/computed';
|
||||
|
||||
const { attr, hasMany, belongsTo, Model } = DS;
|
||||
|
||||
export default Model.extend({
|
||||
engine: belongsTo('secret-engine'),
|
||||
versions: hasMany('secret-v2-version', { async: false }),
|
||||
versions: hasMany('secret-v2-version', { async: false, inverse: null }),
|
||||
selectedVersion: belongsTo('secret-v2-version', { inverse: 'secret' }),
|
||||
createdTime: attr(),
|
||||
updatedTime: attr(),
|
||||
currentVersion: attr('number'),
|
||||
oldestVersion: attr('number'),
|
||||
maxVersions: attr('number'),
|
||||
casRequired: attr('boolean'),
|
||||
isFolder: match('id', /\/$/),
|
||||
});
|
||||
|
|
|
@ -3,23 +3,28 @@ import ApplicationSerializer from './application';
|
|||
|
||||
export default ApplicationSerializer.extend({
|
||||
secretDataPath: 'data.data',
|
||||
normalizeItems(payload, requestType) {
|
||||
normalizeItems(payload) {
|
||||
let path = this.secretDataPath;
|
||||
// move response that is the contents of the secret from the dataPath
|
||||
// to `secret_data` so it will be `secretData` in the model
|
||||
payload.secret_data = get(payload, path);
|
||||
payload = Object.assign({}, payload, payload.data.metadata);
|
||||
delete payload.data;
|
||||
payload.path = payload.id;
|
||||
// return the payload if it's expecting a single object or wrap
|
||||
// it as an array if not
|
||||
return payload;
|
||||
},
|
||||
serialize(snapshot) {
|
||||
return {
|
||||
let data = {
|
||||
data: snapshot.attr('secretData'),
|
||||
options: {
|
||||
cas: snapshot.attr('currentVerion'),
|
||||
},
|
||||
};
|
||||
if (snapshot.attr('currentVersion')) {
|
||||
data.options = {
|
||||
cas: snapshot.attr('currentVerion'),
|
||||
};
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -25,14 +25,15 @@ export default ApplicationSerializer.extend(DS.EmbeddedRecordsMixin, {
|
|||
return { id: fullSecretPath };
|
||||
});
|
||||
}
|
||||
// transform versions to an array with composite IDs
|
||||
if (payload.data.versions) {
|
||||
payload.data.versions = Object.keys(payload.data.versions).map(version => {
|
||||
let body = payload.data.versions[version];
|
||||
body.version = version;
|
||||
body.path = payload.id;
|
||||
body.id = JSON.stringify([payload.backend, payload.id, version]);
|
||||
return body;
|
||||
});
|
||||
console.log(payload);
|
||||
}
|
||||
payload.data.id = payload.id;
|
||||
return requestType === 'queryRecord' ? payload.data : [payload.data];
|
||||
|
|
|
@ -1,26 +1,70 @@
|
|||
import { resolve } from 'rsvp';
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import apiStub from 'vault/tests/helpers/noop-all-api-requests';
|
||||
|
||||
module('Unit | Adapter | secret-v2', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('secret api urls', function(assert) {
|
||||
let url, method, options;
|
||||
let adapter = this.owner.factoryFor('adapter:secret-v2').create({
|
||||
ajax: (...args) => {
|
||||
[url, method, options] = args;
|
||||
return resolve({});
|
||||
hooks.beforeEach(function() {
|
||||
this.server = apiStub();
|
||||
});
|
||||
|
||||
hooks.afterEach(function() {
|
||||
this.server.shutdown();
|
||||
});
|
||||
|
||||
[
|
||||
['query', null, {}, { id: '', backend: 'secret' }, 'GET', '/v1/secret/metadata/?list=true'],
|
||||
['queryRecord', null, {}, { id: 'foo', backend: 'secret' }, 'GET', '/v1/secret/metadata/foo'],
|
||||
[
|
||||
'updateRecord',
|
||||
{
|
||||
serializerFor() {
|
||||
return {
|
||||
serializeIntoHash() {},
|
||||
};
|
||||
},
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'foo',
|
||||
belongsTo() {
|
||||
return 'secret';
|
||||
},
|
||||
},
|
||||
'PUT',
|
||||
'/v1/secret/metadata/foo',
|
||||
],
|
||||
[
|
||||
'deleteRecord',
|
||||
{
|
||||
serializerFor() {
|
||||
return {
|
||||
serializeIntoHash() {},
|
||||
};
|
||||
},
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'foo',
|
||||
belongsTo() {
|
||||
return 'secret';
|
||||
},
|
||||
},
|
||||
'DELETE',
|
||||
'/v1/secret/metadata/foo',
|
||||
],
|
||||
].forEach(([adapterMethod, store, type, queryOrSnapshot, expectedHttpVerb, expectedURL]) => {
|
||||
test(`secret-v2: ${adapterMethod}`, function(assert) {
|
||||
let adapter = this.owner.lookup('adapter:secret-v2');
|
||||
adapter[adapterMethod](store, type, queryOrSnapshot);
|
||||
let { url, method } = this.server.handledRequests[0];
|
||||
assert.equal(url, expectedURL, `${adapterMethod} calls the correct url: ${expectedURL}`);
|
||||
assert.equal(
|
||||
method,
|
||||
expectedHttpVerb,
|
||||
`${adapterMethod} uses the correct http verb: ${expectedHttpVerb}`
|
||||
);
|
||||
});
|
||||
|
||||
adapter.query({}, 'secret', { id: '', backend: 'secret' });
|
||||
assert.equal(url, '/v1/secret/metadata/', 'query generic url OK');
|
||||
assert.equal('GET', method, 'query generic method OK');
|
||||
assert.deepEqual(options, { data: { list: true } }, 'query generic url OK');
|
||||
|
||||
adapter.queryRecord({}, 'secret', { id: 'foo', backend: 'secret' });
|
||||
assert.equal(url, '/v1/secret/data/foo', 'queryRecord generic url OK');
|
||||
assert.equal('GET', method, 'queryRecord generic method OK');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import apiStub from 'vault/tests/helpers/noop-all-api-requests';
|
||||
|
||||
module('Unit | Adapter | secret-v2-version', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
hooks.beforeEach(function() {
|
||||
this.server = apiStub();
|
||||
});
|
||||
|
||||
hooks.afterEach(function() {
|
||||
this.server.shutdown();
|
||||
});
|
||||
|
||||
[
|
||||
[
|
||||
'findRecord with version',
|
||||
'findRecord',
|
||||
[null, {}, JSON.stringify(['secret', 'foo', '2']), {}],
|
||||
'GET',
|
||||
'/v1/secret/data/foo?version=2',
|
||||
],
|
||||
[
|
||||
'deleteRecord with delete',
|
||||
'deleteRecord',
|
||||
[null, {}, { id: JSON.stringify(['secret', 'foo', '2']), adapterOptions: { deleteType: 'delete' } }],
|
||||
'POST',
|
||||
'/v1/secret/delete/foo',
|
||||
{ versions: ['2'] },
|
||||
],
|
||||
[
|
||||
'deleteRecord with destroy',
|
||||
'deleteRecord',
|
||||
[null, {}, { id: JSON.stringify(['secret', 'foo', '2']), adapterOptions: { deleteType: 'destroy' } }],
|
||||
'POST',
|
||||
'/v1/secret/destroy/foo',
|
||||
{ versions: ['2'] },
|
||||
],
|
||||
[
|
||||
'deleteRecord with destroy',
|
||||
'deleteRecord',
|
||||
[null, {}, { id: JSON.stringify(['secret', 'foo', '2']), adapterOptions: { deleteType: 'undelete' } }],
|
||||
'POST',
|
||||
'/v1/secret/undelete/foo',
|
||||
{ versions: ['2'] },
|
||||
],
|
||||
[
|
||||
'updateRecord makes calls to correct url',
|
||||
'updateRecord',
|
||||
[
|
||||
{
|
||||
serializerFor() {
|
||||
return { serializeIntoHash() {} };
|
||||
},
|
||||
},
|
||||
{},
|
||||
{ id: JSON.stringify(['secret', 'foo', '2']) },
|
||||
],
|
||||
'PUT',
|
||||
'/v1/secret/data/foo',
|
||||
],
|
||||
].forEach(([testName, adapterMethod, args, expectedHttpVerb, expectedURL, exptectedRequestBody]) => {
|
||||
test(`secret-v2: ${testName}`, function(assert) {
|
||||
let adapter = this.owner.lookup('adapter:secret-v2-version');
|
||||
adapter[adapterMethod](...args);
|
||||
let { url, method, requestBody } = this.server.handledRequests[0];
|
||||
assert.equal(url, expectedURL, `${adapterMethod} calls the correct url: ${expectedURL}`);
|
||||
assert.equal(
|
||||
method,
|
||||
expectedHttpVerb,
|
||||
`${adapterMethod} uses the correct http verb: ${expectedHttpVerb}`
|
||||
);
|
||||
if (exptectedRequestBody) {
|
||||
assert.deepEqual(JSON.parse(requestBody), exptectedRequestBody);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue