UI - Refactor Adapter.handleResponse (#4398)

* Add some tests to check the correct GET API endpoints are called

* Refactor adapters

1. Add integration tests for `urlFor...` and majority `handleResponse` methods
2. Refactor out `handleResponse` a little more into single/batch/boolean
methods
3. Move setting of the `Datacenter` property into the `handleResponse`
method, basically the same place that the uid is being set using the dc
parsed form the URL
4. Add some Errors for if you don't pass ids to certain `urlFor` methods
This commit is contained in:
John Cowen 2018-07-30 17:55:44 +01:00 committed by GitHub
parent 45d85542a9
commit 12811c0844
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1098 additions and 242 deletions

View File

@ -2,7 +2,7 @@ import Adapter, {
REQUEST_CREATE,
REQUEST_UPDATE,
REQUEST_DELETE,
DATACENTER_KEY as API_DATACENTER_KEY,
DATACENTER_QUERY_PARAM as API_DATACENTER_KEY,
} from './application';
import EmberError from '@ember/error';
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/acl';
@ -19,6 +19,9 @@ export default Adapter.extend({
},
urlForQueryRecord: function(query, modelName) {
// https://www.consul.io/api/acl.html#read-acl-token
if (typeof query.id === 'undefined') {
throw new Error('You must specify an id');
}
return this.appendURL('acl/info', [query.id], this.cleanQuery(query));
},
urlForCreateRecord: function(modelName, snapshot) {
@ -89,13 +92,13 @@ export default Adapter.extend({
}
return this._super(...arguments);
},
isCreateRecord: function(url) {
isCreateRecord: function(url, method) {
return (
url.pathname ===
this.parseURL(this.urlForCreateRecord('acl', makeAttrable({ [DATACENTER_KEY]: '' }))).pathname
);
},
isCloneRecord: function(url) {
isCloneRecord: function(url, method) {
return (
url.pathname ===
this.parseURL(
@ -106,7 +109,7 @@ export default Adapter.extend({
).pathname
);
},
isUpdateRecord: function(url) {
isUpdateRecord: function(url, method) {
return (
url.pathname ===
this.parseURL(this.urlForUpdateRecord(null, 'acl', makeAttrable({ [DATACENTER_KEY]: '' })))
@ -115,41 +118,23 @@ export default Adapter.extend({
},
handleResponse: function(status, headers, payload, requestData) {
let response = payload;
const method = requestData.method;
if (status === HTTP_OK) {
const url = this.parseURL(requestData.url);
switch (true) {
case response === true:
response = {
[PRIMARY_KEY]: this.uidForURL(url),
};
response = this.handleBooleanResponse(url, response, PRIMARY_KEY, SLUG_KEY);
break;
case this.isQueryRecord(url):
response = {
...response[0],
...{
[PRIMARY_KEY]: this.uidForURL(url),
},
};
response = this.handleSingleResponse(url, response[0], PRIMARY_KEY, SLUG_KEY);
break;
case this.isUpdateRecord(url):
case this.isCreateRecord(url):
case this.isCloneRecord(url):
response = {
...response,
...{
[PRIMARY_KEY]: this.uidForURL(url, response[SLUG_KEY]),
},
};
case this.isUpdateRecord(url, method):
case this.isCreateRecord(url, method):
case this.isCloneRecord(url, method):
response = this.handleSingleResponse(url, response, PRIMARY_KEY, SLUG_KEY);
break;
default:
response = response.map((item, i, arr) => {
return {
...item,
...{
[PRIMARY_KEY]: this.uidForURL(url, item[SLUG_KEY]),
},
};
});
response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY);
}
} else if (status === HTTP_UNAUTHORIZED) {
const e = new EmberError();

View File

@ -3,6 +3,7 @@ import { inject as service } from '@ember/service';
import URL from 'url';
import createURL from 'consul-ui/utils/createURL';
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
export const REQUEST_CREATE = 'createRecord';
export const REQUEST_READ = 'queryRecord';
@ -10,7 +11,7 @@ export const REQUEST_UPDATE = 'updateRecord';
export const REQUEST_DELETE = 'deleteRecord';
// export const REQUEST_READ_MULTIPLE = 'query';
export const DATACENTER_KEY = 'dc';
export const DATACENTER_QUERY_PARAM = 'dc';
export default Adapter.extend({
namespace: 'v1',
@ -21,13 +22,44 @@ export default Adapter.extend({
...this._super(...arguments),
};
},
handleBooleanResponse: function(url, response, primary, slug) {
return {
// consider a check for a boolean, also for future me,
// response[slug] // this will forever be null, response should be boolean
[primary]: this.uidForURL(url /* response[slug]*/),
};
},
// could always consider an extra 'dc' arg on the end here?
handleSingleResponse: function(url, response, primary, slug, _dc) {
const dc =
typeof _dc !== 'undefined' ? _dc : url.searchParams.get(DATACENTER_QUERY_PARAM) || '';
return {
...response,
...{
[DATACENTER_KEY]: dc,
[primary]: this.uidForURL(url, response[slug]),
},
};
},
handleBatchResponse: function(url, response, primary, slug) {
const dc = url.searchParams.get(DATACENTER_QUERY_PARAM) || '';
return response.map((item, i, arr) => {
return this.handleSingleResponse(url, item, primary, slug, dc);
});
},
cleanQuery: function(_query) {
delete _query.id;
const query = { ..._query };
delete _query[DATACENTER_KEY];
delete _query[DATACENTER_QUERY_PARAM];
return query;
},
isQueryRecord: function(url) {
isUpdateRecord: function(url, method) {
return false;
},
isCreateRecord: function(url, method) {
return false;
},
isQueryRecord: function(url, method) {
// this is ONLY if ALL api's using it
// follow the 'last part of the url is the id' rule
const pathname = url.pathname
@ -44,15 +76,15 @@ export default Adapter.extend({
getHost: function() {
return this.host || `${location.protocol}//${location.host}`;
},
slugFromURL: function(url) {
slugFromURL: function(url, decode = decodeURIComponent) {
// follow the 'last part of the url is the id' rule
return decodeURIComponent(url.pathname.split('/').pop());
return decode(url.pathname.split('/').pop());
},
parseURL: function(str) {
return new URL(str, this.getHost());
},
uidForURL: function(url, _slug = '') {
const dc = url.searchParams.get(DATACENTER_KEY) || '';
uidForURL: function(url, _slug = '', hash = JSON.stringify) {
const dc = url.searchParams.get(DATACENTER_QUERY_PARAM) || '';
const slug = _slug === '' ? this.slugFromURL(url) : _slug;
if (dc.length < 1) {
throw new Error('Unable to create unique id, missing datacenter');
@ -62,7 +94,7 @@ export default Adapter.extend({
}
// TODO: we could use a URL here? They are unique AND useful
// but probably slower to create?
return JSON.stringify([dc, slug]);
return hash([dc, slug]);
},
// appendURL in turn calls createURL

View File

@ -9,18 +9,14 @@ export default ApplicationAdapter.extend({
// https://www.consul.io/api/coordinate.html#read-lan-coordinates-for-all-nodes
return this.appendURL('coordinate/nodes', [], this.cleanQuery(query));
},
isQueryRecord: function(url) {
return true;
},
handleResponse: function(status, headers, payload, requestData) {
let response = payload;
if (status === HTTP_OK) {
const url = this.parseURL(requestData.url);
response = response.map((item, i, arr) => {
return {
...item,
...{
[PRIMARY_KEY]: this.uidForURL(url, item[SLUG_KEY]),
},
};
});
response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY);
}
return this._super(status, headers, response, requestData);
},

View File

@ -1,7 +1,7 @@
import Adapter, {
REQUEST_CREATE,
REQUEST_UPDATE,
DATACENTER_KEY as API_DATACENTER_KEY,
DATACENTER_QUERY_PARAM as API_DATACENTER_KEY,
} from './application';
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/intention';
@ -13,6 +13,9 @@ export default Adapter.extend({
return this.appendURL('connect/intentions', [], this.cleanQuery(query));
},
urlForQueryRecord: function(query, modelName) {
if (typeof query.id === 'undefined') {
throw new Error('You must specify an id');
}
return this.appendURL('connect/intentions', [query.id], this.cleanQuery(query));
},
urlForCreateRecord: function(modelName, snapshot) {
@ -30,7 +33,7 @@ export default Adapter.extend({
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
},
isUpdateRecord: function(url) {
isUpdateRecord: function(url, method) {
return (
url.pathname ===
this.parseURL(
@ -48,30 +51,19 @@ export default Adapter.extend({
},
handleResponse: function(status, headers, payload, requestData) {
let response = payload;
const method = requestData.method;
if (status === HTTP_OK) {
const url = this.parseURL(requestData.url);
switch (true) {
case this.isQueryRecord(url):
case this.isUpdateRecord(url):
case this.isCreateRecord(url, requestData.method):
case this.isQueryRecord(url, method):
case this.isUpdateRecord(url, method):
case this.isCreateRecord(url, method):
// TODO: We just need to upgrade this (^^ sorry linter) entire API to
// use a full request-like object
response = {
...response,
...{
[PRIMARY_KEY]: this.uidForURL(url),
},
};
response = this.handleSingleResponse(url, response, PRIMARY_KEY, SLUG_KEY);
break;
default:
response = response.map((item, i, arr) => {
return {
...item,
...{
[PRIMARY_KEY]: this.uidForURL(url, item[SLUG_KEY]),
},
};
});
response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY);
}
}
return this._super(status, headers, response, requestData);

View File

@ -2,7 +2,7 @@ import Adapter, {
REQUEST_CREATE,
REQUEST_UPDATE,
REQUEST_DELETE,
DATACENTER_KEY as API_DATACENTER_KEY,
DATACENTER_QUERY_PARAM as API_DATACENTER_KEY,
} from './application';
import isFolder from 'consul-ui/utils/isFolder';
import injectableRequestToJQueryAjaxHash from 'consul-ui/utils/injectableRequestToJQueryAjaxHash';
@ -45,6 +45,9 @@ export default Adapter.extend({
}),
decoder: service('atob'),
urlForQuery: function(query, modelName) {
if (typeof query.id === 'undefined') {
throw new Error('You must specify an id');
}
// append keys here otherwise query.keys will add an '='
return this.appendURL('kv', keyToArray(query.id), {
...{
@ -54,6 +57,9 @@ export default Adapter.extend({
});
},
urlForQueryRecord: function(query, modelName) {
if (typeof query.id === 'undefined') {
throw new Error('You must specify an id');
}
return this.appendURL('kv', keyToArray(query.id), this.cleanQuery(query));
},
urlForCreateRecord: function(modelName, snapshot) {
@ -84,35 +90,36 @@ export default Adapter.extend({
.join('/')
);
},
isQueryRecord: function(url) {
isQueryRecord: function(url, method) {
return !url.searchParams.has(API_KEYS_KEY);
},
handleBatchResponse: function(url, response, primary, slug) {
const dc = url.searchParams.get(API_DATACENTER_KEY) || '';
return response.map((item, i, arr) => {
return {
[DATACENTER_KEY]: dc,
[PRIMARY_KEY]: this.uidForURL(url, item),
[SLUG_KEY]: item,
};
});
},
// handleSingleResponse: function(url, response, primary, slug) {
// return this._super(url, removeNull(response[0]), PRIMARY_KEY, SLUG_KEY);
// },
handleResponse: function(status, headers, payload, requestData) {
let response = payload;
const method = requestData.method;
if (status === HTTP_OK) {
const url = this.parseURL(requestData.url);
switch (true) {
case response === true:
response = {
[PRIMARY_KEY]: this.uidForURL(url),
};
response = this.handleBooleanResponse(url, response, PRIMARY_KEY, SLUG_KEY);
break;
case this.isQueryRecord(url):
response = {
...removeNull(response[0]),
...{
[PRIMARY_KEY]: this.uidForURL(url),
},
};
case this.isQueryRecord(url, method):
response = this.handleSingleResponse(url, removeNull(response[0]), PRIMARY_KEY, SLUG_KEY);
break;
default:
// isQuery
response = response.map((item, i, arr) => {
return {
[PRIMARY_KEY]: this.uidForURL(url, item),
[SLUG_KEY]: item,
};
});
response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY);
}
}
return this._super(status, headers, response, requestData);

View File

@ -1,4 +1,5 @@
import Adapter from './application';
import Adapter, { DATACENTER_QUERY_PARAM as API_DATACENTER_KEY } from './application';
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/node';
import { OK as HTTP_OK } from 'consul-ui/utils/http/status';
// TODO: Looks like ID just isn't used at all
@ -14,32 +15,38 @@ export default Adapter.extend({
return this.appendURL('internal/ui/nodes', [], this.cleanQuery(query));
},
urlForQueryRecord: function(query, modelName) {
if (typeof query.id === 'undefined') {
throw new Error('You must specify an id');
}
return this.appendURL('internal/ui/node', [query.id], this.cleanQuery(query));
},
handleBatchResponse: function(url, response, primary, slug) {
const dc = url.searchParams.get(API_DATACENTER_KEY) || '';
return response.map((item, i, arr) => {
// this can go in the serializer
item = fillSlug(item);
// this could be replaced by handleSingleResponse
// maybe perf test first although even polyfilled searchParams should be super fast
return {
...item,
...{
[DATACENTER_KEY]: dc,
[PRIMARY_KEY]: this.uidForURL(url, item[SLUG_KEY]),
},
};
});
},
handleResponse: function(status, headers, payload, requestData) {
let response = payload;
const method = requestData.method;
if (status === HTTP_OK) {
const url = this.parseURL(requestData.url);
switch (true) {
case this.isQueryRecord(url):
response = fillSlug(response);
response = {
...response,
...{
[PRIMARY_KEY]: this.uidForURL(url),
},
};
case this.isQueryRecord(url, method):
response = this.handleSingleResponse(url, fillSlug(response), PRIMARY_KEY, SLUG_KEY);
break;
default:
response = response.map((item, i, arr) => {
item = fillSlug(item);
return {
...item,
...{
[PRIMARY_KEY]: this.uidForURL(url, item[SLUG_KEY]),
},
};
});
response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY);
}
}
return this._super(status, headers, response, requestData);

View File

@ -1,33 +1,27 @@
import Adapter from './application';
import { PRIMARY_KEY } from 'consul-ui/models/service';
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/service';
import { OK as HTTP_OK } from 'consul-ui/utils/http/status';
export default Adapter.extend({
urlForQuery: function(query, modelName) {
return this.appendURL('internal/ui/services', [], this.cleanQuery(query));
},
urlForQueryRecord: function(query, modelName) {
if (typeof query.id === 'undefined') {
throw new Error('You must specify an id');
}
return this.appendURL('health/service', [query.id], this.cleanQuery(query));
},
handleResponse: function(status, headers, payload, requestData) {
let response = payload;
const method = requestData.method;
if (status === HTTP_OK) {
const url = this.parseURL(requestData.url);
switch (true) {
case this.isQueryRecord(url):
response = {
[PRIMARY_KEY]: this.uidForURL(url),
Nodes: response,
};
case this.isQueryRecord(url, method):
response = this.handleSingleResponse(url, { Nodes: response }, PRIMARY_KEY, SLUG_KEY);
break;
default:
response = response.map((item, i, arr) => {
return {
...item,
...{
[PRIMARY_KEY]: this.uidForURL(url, item.Name),
},
};
});
response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY);
}
}
return this._super(status, headers, response, requestData);

View File

@ -1,4 +1,7 @@
import Adapter, { REQUEST_DELETE, DATACENTER_KEY as API_DATACENTER_KEY } from './application';
import Adapter, {
REQUEST_DELETE,
DATACENTER_QUERY_PARAM as API_DATACENTER_KEY,
} from './application';
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/session';
@ -7,9 +10,15 @@ import { OK as HTTP_OK } from 'consul-ui/utils/http/status';
export default Adapter.extend({
urlForQuery: function(query, modelName) {
if (typeof query.id === 'undefined') {
throw new Error('You must specify an id');
}
return this.appendURL('session/node', [query.id], this.cleanQuery(query));
},
urlForQueryRecord: function(query, modelName) {
if (typeof query.id === 'undefined') {
throw new Error('You must specify an id');
}
return this.appendURL('session/info', [query.id], this.cleanQuery(query));
},
urlForDeleteRecord: function(id, modelName, snapshot) {
@ -27,31 +36,18 @@ export default Adapter.extend({
},
handleResponse: function(status, headers, payload, requestData) {
let response = payload;
const method = requestData.method;
if (status === HTTP_OK) {
const url = this.parseURL(requestData.url);
switch (true) {
case response === true:
response = {
[PRIMARY_KEY]: this.uidForURL(url),
};
response = this.handleBooleanResponse(url, response, PRIMARY_KEY, SLUG_KEY);
break;
case this.isQueryRecord(url):
response = {
...response[0],
...{
[PRIMARY_KEY]: this.uidForURL(url),
},
};
case this.isQueryRecord(url, method):
response = this.handleSingleResponse(url, response[0], PRIMARY_KEY, SLUG_KEY);
break;
default:
response = response.map((item, i, arr) => {
return {
...item,
...{
[PRIMARY_KEY]: this.uidForURL(url, item[SLUG_KEY]),
},
};
});
response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY);
}
}
return this._super(status, headers, response, requestData);

View File

@ -1,5 +1,5 @@
import Service, { inject as service } from '@ember/service';
import { get, set } from '@ember/object';
import { get } from '@ember/object';
import { typeOf } from '@ember/utils';
import { PRIMARY_KEY } from 'consul-ui/models/acl';
export default Service.extend({
@ -8,27 +8,15 @@ export default Service.extend({
return get(this, 'store').clone('acl', get(item, PRIMARY_KEY));
},
findAllByDatacenter: function(dc) {
return get(this, 'store')
.query('acl', {
dc: dc,
})
.then(function(items) {
// TODO: Sort with anonymous first
return items.forEach(function(item, i, arr) {
set(item, 'Datacenter', dc);
});
});
return get(this, 'store').query('acl', {
dc: dc,
});
},
findBySlug: function(slug, dc) {
return get(this, 'store')
.queryRecord('acl', {
id: slug,
dc: dc,
})
.then(function(item) {
set(item, 'Datacenter', dc);
return item;
});
return get(this, 'store').queryRecord('acl', {
id: slug,
dc: dc,
});
},
create: function() {
return get(this, 'store').createRecord('acl');

View File

@ -1,28 +1,17 @@
import Service, { inject as service } from '@ember/service';
import { get, set } from '@ember/object';
import { get } from '@ember/object';
import { typeOf } from '@ember/utils';
import { PRIMARY_KEY } from 'consul-ui/models/intention';
export default Service.extend({
store: service('store'),
findAllByDatacenter: function(dc) {
return get(this, 'store')
.query('intention', { dc: dc })
.then(function(items) {
return items.forEach(function(item, i, arr) {
set(item, 'Datacenter', dc);
});
});
return get(this, 'store').query('intention', { dc: dc });
},
findBySlug: function(slug, dc) {
return get(this, 'store')
.queryRecord('intention', {
id: slug,
dc: dc,
})
.then(function(item) {
set(item, 'Datacenter', dc);
return item;
});
return get(this, 'store').queryRecord('intention', {
id: slug,
dc: dc,
});
},
create: function() {
return get(this, 'store').createRecord('intention');

View File

@ -19,15 +19,10 @@ export default Service.extend({
}
return Promise.resolve(item);
}
return get(this, 'store')
.queryRecord('kv', {
id: key,
dc: dc,
})
.then(function(item) {
set(item, 'Datacenter', dc);
return item;
});
return get(this, 'store').queryRecord('kv', {
id: key,
dc: dc,
});
},
// this one only gives you keys
// https://www.consul.io/api/kv.html
@ -42,14 +37,9 @@ export default Service.extend({
separator: '/',
})
.then(function(items) {
return items
.filter(function(item) {
return key !== get(item, 'Key');
})
.map(function(item, i, arr) {
set(item, 'Datacenter', dc);
return item;
});
return items.filter(function(item) {
return key !== get(item, 'Key');
});
})
.catch(e => {
if (e.errors && e.errors[0] && e.errors[0].status == '404') {

View File

@ -4,13 +4,7 @@ import { get, set } from '@ember/object';
export default Service.extend({
store: service('store'),
findAllByDatacenter: function(dc) {
return get(this, 'store')
.query('service', { dc: dc })
.then(function(items) {
return items.forEach(function(item) {
set(item, 'Datacenter', dc);
});
});
return get(this, 'store').query('service', { dc: dc });
},
findBySlug: function(slug, dc) {
return get(this, 'store')

View File

@ -1,31 +1,19 @@
import Service, { inject as service } from '@ember/service';
import { get, set } from '@ember/object';
import { get } from '@ember/object';
export default Service.extend({
store: service('store'),
findByNode: function(node, dc) {
return get(this, 'store')
.query('session', {
id: node,
dc: dc,
})
.then(function(items) {
return items.map(function(item, i, arr) {
set(item, 'Datacenter', dc);
return item;
});
});
return get(this, 'store').query('session', {
id: node,
dc: dc,
});
},
findByKey: function(slug, dc) {
return get(this, 'store')
.queryRecord('session', {
id: slug,
dc: dc,
})
.then(function(item) {
set(item, 'Datacenter', dc);
return item;
});
return get(this, 'store').queryRecord('session', {
id: slug,
dc: dc,
});
},
remove: function(item) {
return item.destroyRecord().then(item => {

View File

@ -8,6 +8,7 @@ Feature: Page Navigation
dc: dc-1
---
Then the url should be /dc-1/services
Then the last GET request was made to "/v1/internal/ui/services?dc=dc-1"
Scenario: Clicking [Link] in the navigation takes me to [URL]
When I visit the services page for yaml
---
@ -15,15 +16,16 @@ Feature: Page Navigation
---
When I click [Link] on the navigation
Then the url should be [URL]
Then the last GET request was made to "[Endpoint]"
Where:
----------------------------------------
| Link | URL |
| nodes | /dc-1/nodes |
| kvs | /dc-1/kv |
| acls | /dc-1/acls |
| intentions | /dc-1/intentions |
| settings | /settings |
----------------------------------------
----------------------------------------------------------------------
| Link | URL | Endpoint |
| nodes | /dc-1/nodes | /v1/internal/ui/nodes?dc=dc-1 |
| kvs | /dc-1/kv | /v1/kv/?keys&dc=dc-1&separator=%2F |
| acls | /dc-1/acls | /v1/acl/list?dc=dc-1 |
| intentions | /dc-1/intentions | /v1/connect/intentions?dc=dc-1 |
| settings | /settings | /v1/catalog/datacenters |
----------------------------------------------------------------------
Scenario: Clicking a [Item] in the [Model] listing and back again
When I visit the [Model] page for yaml
---
@ -31,17 +33,56 @@ Feature: Page Navigation
---
When I click [Item] on the [Model]
Then the url should be [URL]
Then the last GET request was made to "[Endpoint]"
And I click "[data-test-back]"
Then the url should be [Back]
Where:
--------------------------------------------------------------------------------------------------------
| Item | Model | URL | Back |
| service | services | /dc-1/services/service-0 | /dc-1/services |
| node | nodes | /dc-1/nodes/node-0 | /dc-1/nodes |
| kv | kvs | /dc-1/kv/necessitatibus-0/edit | /dc-1/kv |
| acl | acls | /dc-1/acls/anonymous | /dc-1/acls |
| intention | intentions | /dc-1/intentions/ee52203d-989f-4f7a-ab5a-2bef004164ca | /dc-1/intentions |
--------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Item | Model | URL | Endpoint | Back |
| service | services | /dc-1/services/service-0 | /v1/health/service/service-0?dc=dc-1 | /dc-1/services |
| node | nodes | /dc-1/nodes/node-0 | /v1/session/node/node-0?dc=dc-1 | /dc-1/nodes |
| kv | kvs | /dc-1/kv/necessitatibus-0/edit | /v1/session/info/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=dc-1 | /dc-1/kv |
| acl | acls | /dc-1/acls/anonymous | /v1/acl/info/anonymous?dc=dc-1 | /dc-1/acls |
| intention | intentions | /dc-1/intentions/ee52203d-989f-4f7a-ab5a-2bef004164ca | /v1/internal/ui/services?dc=dc-1 | /dc-1/intentions |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Scenario: The node detail page calls the correct API endpoints
When I visit the node page for yaml
---
dc: dc-1
node: node-0
---
Then the last GET requests were like yaml
---
- /v1/catalog/datacenters
- /v1/internal/ui/node/node-0?dc=dc-1
- /v1/coordinate/nodes?dc=dc-1
- /v1/session/node/node-0?dc=dc-1
---
Scenario: The kv detail page calls the correct API endpoints
When I visit the kv page for yaml
---
dc: dc-1
kv: keyname
---
Then the last GET requests were like yaml
---
- /v1/catalog/datacenters
- /v1/kv/keyname?dc=dc-1
- /v1/session/info/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=dc-1
---
Scenario: The intention detail page calls the correct API endpoints
When I visit the intention page for yaml
---
dc: dc-1
intention: intention
---
Then the last GET requests were like yaml
---
- /v1/catalog/datacenters
- /v1/connect/intentions/intention?dc=dc-1
- /v1/internal/ui/services?dc=dc-1
---
Scenario: Clicking a [Item] in the [Model] listing and canceling
When I visit the [Model] page for yaml
---
@ -77,5 +118,6 @@ Feature: Page Navigation
| acl | acls | /dc-1/acls/create | /dc-1/acls |
| intention | intentions | /dc-1/intentions/create | /dc-1/intentions |
------------------------------------------------------------------------
@ignore
Scenario: Using I click on should change the currentPage ^
Then ok

View File

@ -11,4 +11,28 @@ if (apiConfig) {
temp.pop();
path = temp.join('/');
}
export default getAPI(path, setCookies, typeToURL, reader);
const api = getAPI(path, setCookies, typeToURL, reader);
export const get = function(_url, options = { headers: { cookie: {} } }) {
const url = new URL(_url, 'http://localhost');
return new Promise(function(resolve) {
return api.api.serve(
{
method: 'GET',
path: url.pathname,
url: url.href,
cookies: options.headers.cookie || {},
query: [...url.searchParams.keys()].reduce(function(prev, key) {
prev[key] = url.searchParams.get(key);
return prev;
}, {}),
},
{
send: function(content) {
resolve(JSON.parse(content));
},
},
function() {}
);
});
};
export default api;

View File

@ -0,0 +1,38 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { get } from 'consul-ui/tests/helpers/api';
module('Integration | Adapter | acl | response', function(hooks) {
setupTest(hooks);
const dc = 'dc-1';
const id = 'token-name';
test('handleResponse returns the correct data for list endpoint', function(assert) {
const adapter = this.owner.lookup('adapter:acl');
const request = {
url: `/v1/acl/list?dc=${dc}`,
};
return get(request.url).then(function(payload) {
const expected = payload.map(item =>
Object.assign({}, item, {
Datacenter: dc,
uid: `["${dc}","${item.ID}"]`,
})
);
const actual = adapter.handleResponse(200, {}, payload, request);
assert.deepEqual(actual, expected);
});
});
test('handleResponse returns the correct data for item endpoint', function(assert) {
const adapter = this.owner.lookup('adapter:acl');
const request = {
url: `/v1/acl/info/${id}?dc=${dc}`,
};
return get(request.url).then(function(payload) {
const expected = Object.assign({}, payload[0], {
Datacenter: dc,
uid: `["${dc}","${id}"]`,
});
const actual = adapter.handleResponse(200, {}, payload, request);
assert.deepEqual(actual, expected);
});
});
});

View File

@ -0,0 +1,83 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import makeAttrable from 'consul-ui/utils/makeAttrable';
module('Integration | Adapter | acl | url', function(hooks) {
setupTest(hooks);
const dc = 'dc-1';
const id = 'token-name';
test('urlForQuery returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:acl');
const expected = `/v1/acl/list?dc=${dc}`;
const actual = adapter.urlForQuery({
dc: dc,
});
assert.equal(actual, expected);
});
test('urlForQueryRecord returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:acl');
const expected = `/v1/acl/info/${id}?dc=${dc}`;
const actual = adapter.urlForQueryRecord({
dc: dc,
id: id,
});
assert.equal(actual, expected);
});
test("urlForQueryRecord throws if you don't specify an id", function(assert) {
const adapter = this.owner.lookup('adapter:acl');
assert.throws(function() {
adapter.urlForQueryRecord({
dc: dc,
});
});
});
test('urlForCreateRecord returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:acl');
const expected = `/v1/acl/create?dc=${dc}`;
const actual = adapter.urlForCreateRecord(
'acl',
makeAttrable({
Datacenter: dc,
ID: id,
})
);
assert.equal(actual, expected);
});
test('urlForUpdateRecord returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:acl');
const expected = `/v1/acl/update?dc=${dc}`;
const actual = adapter.urlForUpdateRecord(
id,
'acl',
makeAttrable({
Datacenter: dc,
ID: id,
})
);
assert.equal(actual, expected);
});
test('urlForDeleteRecord returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:acl');
const expected = `/v1/acl/destroy/${id}?dc=${dc}`;
const actual = adapter.urlForDeleteRecord(
id,
'acl',
makeAttrable({
Datacenter: dc,
ID: id,
})
);
assert.equal(actual, expected);
});
test('urlForCloneRecord returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:acl');
const expected = `/v1/acl/clone/${id}?dc=${dc}`;
const actual = adapter.urlForCloneRecord(
'acl',
makeAttrable({
Datacenter: dc,
ID: id,
})
);
assert.equal(actual, expected);
});
});

View File

@ -0,0 +1,23 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { get } from 'consul-ui/tests/helpers/api';
module('Integration | Adapter | coordinate | response', function(hooks) {
setupTest(hooks);
const dc = 'dc-1';
test('handleResponse returns the correct data for list endpoint', function(assert) {
const adapter = this.owner.lookup('adapter:coordinate');
const request = {
url: `/v1/coordinate/nodes?dc=${dc}`,
};
return get(request.url).then(function(payload) {
const expected = payload.map(item =>
Object.assign({}, item, {
Datacenter: dc,
uid: `["${dc}","${item.Node}"]`,
})
);
const actual = adapter.handleResponse(200, {}, payload, request);
assert.deepEqual(actual, expected);
});
});
});

View File

@ -0,0 +1,14 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Integration | Adapter | coordinate | url', function(hooks) {
setupTest(hooks);
const dc = 'dc-1';
test('urlForQuery returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:coordinate');
const expected = `/v1/coordinate/nodes?dc=${dc}`;
const actual = adapter.urlForQuery({
dc: dc,
});
assert.equal(actual, expected);
});
});

View File

@ -0,0 +1,17 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { get } from 'consul-ui/tests/helpers/api';
module('Integration | Adapter | dc | response', function(hooks) {
setupTest(hooks);
test('handleResponse returns the correct data for list endpoint', function(assert) {
const adapter = this.owner.lookup('adapter:dc');
const request = {
url: `/v1/catalog/datacenters`,
};
return get(request.url).then(function(payload) {
const expected = payload;
const actual = adapter.handleResponse(200, {}, payload, request);
assert.deepEqual(actual, expected);
});
});
});

View File

@ -0,0 +1,11 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Integration | Adapter | dc | url', function(hooks) {
setupTest(hooks);
test('urlForFindAll returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:dc');
const expected = `/v1/catalog/datacenters`;
const actual = adapter.urlForFindAll();
assert.equal(actual, expected);
});
});

View File

@ -0,0 +1,40 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { get } from 'consul-ui/tests/helpers/api';
module('Integration | Adapter | intention | response', function(hooks) {
setupTest(hooks);
const dc = 'dc-1';
const id = 'intention-name';
test('handleResponse returns the correct data for list endpoint', function(assert) {
const adapter = this.owner.lookup('adapter:intention');
const request = {
url: `/v1/connect/intentions?dc=${dc}`,
method: 'GET',
};
return get(request.url).then(function(payload) {
const expected = payload.map(item =>
Object.assign({}, item, {
Datacenter: dc,
uid: `["${dc}","${item.ID}"]`,
})
);
const actual = adapter.handleResponse(200, {}, payload, request);
assert.deepEqual(actual, expected);
});
});
test('handleResponse returns the correct data for item endpoint', function(assert) {
const adapter = this.owner.lookup('adapter:intention');
const request = {
url: `/v1/connect/intentions/${id}?dc=${dc}`,
method: 'GET',
};
return get(request.url).then(function(payload) {
const expected = Object.assign({}, payload, {
Datacenter: dc,
uid: `["${dc}","${id}"]`,
});
const actual = adapter.handleResponse(200, {}, payload, request);
assert.deepEqual(actual, expected);
});
});
});

View File

@ -0,0 +1,71 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import makeAttrable from 'consul-ui/utils/makeAttrable';
module('Integration | Adapter | intention | url', function(hooks) {
setupTest(hooks);
const dc = 'dc-1';
const id = 'intention-name';
test('urlForQuery returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:intention');
const expected = `/v1/connect/intentions?dc=${dc}`;
const actual = adapter.urlForQuery({
dc: dc,
});
assert.equal(actual, expected);
});
test('urlForQueryRecord returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:intention');
const expected = `/v1/connect/intentions/${id}?dc=${dc}`;
const actual = adapter.urlForQueryRecord({
dc: dc,
id: id,
});
assert.equal(actual, expected);
});
test("urlForQueryRecord throws if you don't specify an id", function(assert) {
const adapter = this.owner.lookup('adapter:intention');
assert.throws(function() {
adapter.urlForQueryRecord({
dc: dc,
});
});
});
test('urlForCreateRecord returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:intention');
const expected = `/v1/connect/intentions?dc=${dc}`;
const actual = adapter.urlForCreateRecord(
'acl',
makeAttrable({
Datacenter: dc,
ID: id,
})
);
assert.equal(actual, expected);
});
test('urlForUpdateRecord returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:intention');
const expected = `/v1/connect/intentions/${id}?dc=${dc}`;
const actual = adapter.urlForUpdateRecord(
id,
'intention',
makeAttrable({
Datacenter: dc,
ID: id,
})
);
assert.equal(actual, expected);
});
test('urlForDeleteRecord returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:intention');
const expected = `/v1/connect/intentions/${id}?dc=${dc}`;
const actual = adapter.urlForDeleteRecord(
id,
'intention',
makeAttrable({
Datacenter: dc,
ID: id,
})
);
assert.equal(actual, expected);
});
});

View File

@ -0,0 +1,44 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { get } from 'consul-ui/tests/helpers/api';
module('Integration | Adapter | kv | response', function(hooks) {
setupTest(hooks);
const dc = 'dc-1';
const id = 'key-name/here';
test('handleResponse returns the correct data for list endpoint', function(assert) {
const adapter = this.owner.lookup('adapter:kv');
const request = {
url: `/v1/kv/${id}?keys&dc=${dc}`,
};
return get(request.url).then(function(payload) {
const expected = payload.map(item =>
Object.assign(
{},
{
Key: item,
},
{
Datacenter: dc,
uid: `["${dc}","${item}"]`,
}
)
);
const actual = adapter.handleResponse(200, {}, payload, request);
assert.deepEqual(actual, expected);
});
});
test('handleResponse returns the correct data for item endpoint', function(assert) {
const adapter = this.owner.lookup('adapter:kv');
const request = {
url: `/v1/kv/${id}?dc=${dc}`,
};
return get(request.url).then(function(payload) {
const expected = Object.assign({}, payload[0], {
Datacenter: dc,
uid: `["${dc}","${id}"]`,
});
const actual = adapter.handleResponse(200, {}, payload, request);
assert.deepEqual(actual, expected);
});
});
});

View File

@ -0,0 +1,101 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import makeAttrable from 'consul-ui/utils/makeAttrable';
module('Integration | Adapter | kv | url', function(hooks) {
setupTest(hooks);
const dc = 'dc-1';
const id = 'key-name/here';
test('slugFromURL returns the correct slug', function(assert) {
const adapter = this.owner.lookup('adapter:kv');
const url = `/v1/kv/${id}?dc=${dc}`;
const expected = id;
const actual = adapter.slugFromURL(new URL(url, 'http://localhost'));
assert.equal(actual, expected);
});
test('urlForQuery returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:kv');
const expected = `/v1/kv/${id}?keys&dc=${dc}`;
const actual = adapter.urlForQuery({
dc: dc,
id: id,
});
assert.equal(actual, expected);
});
test('urlForQueryRecord returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:kv');
const expected = `/v1/kv/${id}?dc=${dc}`;
const actual = adapter.urlForQueryRecord({
dc: dc,
id: id,
});
assert.equal(actual, expected);
});
test("urlForQueryRecord throws if you don't specify an id", function(assert) {
const adapter = this.owner.lookup('adapter:kv');
assert.throws(function() {
adapter.urlForQueryRecord({
dc: dc,
});
});
});
test("urlForQuery throws if you don't specify an id", function(assert) {
const adapter = this.owner.lookup('adapter:kv');
assert.throws(function() {
adapter.urlForQuery({
dc: dc,
});
});
});
test('urlForCreateRecord returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:kv');
const expected = `/v1/kv/${id}?dc=${dc}`;
const actual = adapter.urlForCreateRecord(
'kv',
makeAttrable({
Datacenter: dc,
Key: id,
})
);
assert.equal(actual, expected);
});
test('urlForUpdateRecord returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:kv');
const expected = `/v1/kv/${id}?dc=${dc}`;
const actual = adapter.urlForUpdateRecord(
id,
'kv',
makeAttrable({
Datacenter: dc,
Key: id,
})
);
assert.equal(actual, expected);
});
test('urlForDeleteRecord returns the correct url for non-folders', function(assert) {
const adapter = this.owner.lookup('adapter:kv');
const expected = `/v1/kv/${id}?dc=${dc}`;
const actual = adapter.urlForDeleteRecord(
id,
'kv',
makeAttrable({
Datacenter: dc,
Key: id,
})
);
assert.equal(actual, expected);
});
test('urlForDeleteRecord returns the correct url for folders', function(assert) {
const adapter = this.owner.lookup('adapter:kv');
const folder = `${id}/`;
const expected = `/v1/kv/${folder}?dc=${dc}&recurse`;
const actual = adapter.urlForDeleteRecord(
folder,
'kv',
makeAttrable({
Datacenter: dc,
Key: folder,
})
);
assert.equal(actual, expected);
});
});

View File

@ -0,0 +1,39 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { get } from 'consul-ui/tests/helpers/api';
module('Integration | Adapter | node | response', function(hooks) {
setupTest(hooks);
test('handleResponse returns the correct data for list endpoint', function(assert) {
const adapter = this.owner.lookup('adapter:node');
const dc = 'dc-1';
const request = {
url: `/v1/internal/ui/nodes?dc=${dc}`,
};
return get(request.url).then(function(payload) {
const expected = payload.map(item =>
Object.assign({}, item, {
Datacenter: dc,
uid: `["${dc}","${item.ID}"]`,
})
);
const actual = adapter.handleResponse(200, {}, payload, request);
assert.deepEqual(actual, expected);
});
});
test('handleResponse returns the correct data for item endpoint', function(assert) {
const adapter = this.owner.lookup('adapter:node');
const dc = 'dc-1';
const id = 'node-name';
const request = {
url: `/v1/internal/ui/node/${id}?dc=${dc}`,
};
return get(request.url).then(function(payload) {
const expected = Object.assign({}, payload, {
Datacenter: dc,
uid: `["${dc}","${id}"]`,
});
const actual = adapter.handleResponse(200, {}, payload, request);
assert.deepEqual(actual, expected);
});
});
});

View File

@ -0,0 +1,32 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Integration | Adapter | node | url', function(hooks) {
setupTest(hooks);
const dc = 'dc-1';
const id = 'node-name';
test('urlForQuery returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:node');
const expected = `/v1/internal/ui/nodes?dc=${dc}`;
const actual = adapter.urlForQuery({
dc: dc,
});
assert.equal(actual, expected);
});
test('urlForQueryRecord returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:node');
const expected = `/v1/internal/ui/node/${id}?dc=${dc}`;
const actual = adapter.urlForQueryRecord({
dc: dc,
id: id,
});
assert.equal(actual, expected);
});
test("urlForQueryRecord throws if you don't specify an id", function(assert) {
const adapter = this.owner.lookup('adapter:node');
assert.throws(function() {
adapter.urlForQueryRecord({
dc: dc,
});
});
});
});

View File

@ -0,0 +1,40 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { get } from 'consul-ui/tests/helpers/api';
module('Integration | Adapter | service | response', function(hooks) {
setupTest(hooks);
test('handleResponse returns the correct data for list endpoint', function(assert) {
const adapter = this.owner.lookup('adapter:service');
const dc = 'dc-1';
const request = {
url: `/v1/internal/ui/services?dc=${dc}`,
};
return get(request.url).then(function(payload) {
const expected = payload.map(item =>
Object.assign({}, item, {
Datacenter: dc,
uid: `["${dc}","${item.Name}"]`,
})
);
const actual = adapter.handleResponse(200, {}, payload, request);
assert.deepEqual(actual, expected);
});
});
test('handleResponse returns the correct data for item endpoint', function(assert) {
const adapter = this.owner.lookup('adapter:service');
const dc = 'dc-1';
const id = 'service-name';
const request = {
url: `/v1/health/service/${id}?dc=${dc}`,
};
return get(request.url).then(function(payload) {
const expected = {
Datacenter: dc,
uid: `["${dc}","${id}"]`,
Nodes: payload,
};
const actual = adapter.handleResponse(200, {}, payload, request);
assert.deepEqual(actual, expected);
});
});
});

View File

@ -0,0 +1,32 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Integration | Adapter | service | url', function(hooks) {
setupTest(hooks);
const dc = 'dc-1';
const id = 'service-name';
test('urlForQuery returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:service');
const expected = `/v1/internal/ui/services?dc=${dc}`;
const actual = adapter.urlForQuery({
dc: dc,
});
assert.equal(actual, expected);
});
test('urlForQueryRecord returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:service');
const expected = `/v1/health/service/${id}?dc=${dc}`;
const actual = adapter.urlForQueryRecord({
dc: dc,
id: id,
});
assert.equal(actual, expected);
});
test("urlForQueryRecord throws if you don't specify an id", function(assert) {
const adapter = this.owner.lookup('adapter:service');
assert.throws(function() {
adapter.urlForQueryRecord({
dc: dc,
});
});
});
});

View File

@ -0,0 +1,39 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { get } from 'consul-ui/tests/helpers/api';
module('Integration | Adapter | session | response', function(hooks) {
setupTest(hooks);
const dc = 'dc-1';
const id = 'session-id';
test('handleResponse returns the correct data for list endpoint', function(assert) {
const adapter = this.owner.lookup('adapter:session');
const node = 'node-id';
const request = {
url: `/v1/session/node/${node}?dc=${dc}`,
};
return get(request.url).then(function(payload) {
const expected = payload.map(item =>
Object.assign({}, item, {
Datacenter: dc,
uid: `["${dc}","${item.ID}"]`,
})
);
const actual = adapter.handleResponse(200, {}, payload, request);
assert.deepEqual(actual, expected);
});
});
test('handleResponse returns the correct data for item endpoint', function(assert) {
const adapter = this.owner.lookup('adapter:session');
const request = {
url: `/v1/session/info/${id}?dc=${dc}`,
};
return get(request.url).then(function(payload) {
const expected = Object.assign({}, payload[0], {
Datacenter: dc,
uid: `["${dc}","${id}"]`,
});
const actual = adapter.handleResponse(200, {}, payload, request);
assert.deepEqual(actual, expected);
});
});
});

View File

@ -0,0 +1,56 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import makeAttrable from 'consul-ui/utils/makeAttrable';
module('Integration | Adapter | session | url', function(hooks) {
setupTest(hooks);
const dc = 'dc-1';
const id = 'session-id';
test('urlForQuery returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:session');
const node = 'node-id';
const expected = `/v1/session/node/${node}?dc=${dc}`;
const actual = adapter.urlForQuery({
dc: dc,
id: node,
});
assert.equal(actual, expected);
});
test('urlForQueryRecord returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:session');
const expected = `/v1/session/info/${id}?dc=${dc}`;
const actual = adapter.urlForQueryRecord({
dc: dc,
id: id,
});
assert.equal(actual, expected);
});
test("urlForQuery throws if you don't specify an id", function(assert) {
const adapter = this.owner.lookup('adapter:session');
assert.throws(function() {
adapter.urlForQuery({
dc: dc,
});
});
});
test("urlForQueryRecord throws if you don't specify an id", function(assert) {
const adapter = this.owner.lookup('adapter:session');
assert.throws(function() {
adapter.urlForQueryRecord({
dc: dc,
});
});
});
test('urlForDeleteRecord returns the correct url', function(assert) {
const adapter = this.owner.lookup('adapter:session');
const expected = `/v1/session/destroy/${id}?dc=${dc}`;
const actual = adapter.urlForDeleteRecord(
id,
'acl',
makeAttrable({
Datacenter: dc,
ID: id,
})
);
assert.equal(actual, expected);
});
});

View File

@ -250,6 +250,18 @@ export default function(assert) {
});
assert.equal(request.url, url, `Expected the request url to be ${url}, was ${request.url}`);
})
.then('the last $method requests were like yaml\n$yaml', function(method, data) {
const requests = api.server.history.reverse().filter(function(item) {
return item.method === method;
});
data.reverse().forEach(function(item, i, arr) {
assert.equal(
requests[i].url,
item,
`Expected the request url to be ${item}, was ${requests[i].url}`
);
});
})
.then('the url should be $url', function(url) {
// TODO: nice! $url should be wrapped in ""
if (url === "''") {

View File

@ -1,4 +1,5 @@
import { module, test } from 'qunit';
import { module } from 'qunit';
import test from 'ember-sinon-qunit/test-support/test';
import { setupTest } from 'ember-qunit';
module('Unit | Adapter | application', function(hooks) {
@ -6,7 +7,145 @@ module('Unit | Adapter | application', function(hooks) {
// Replace this with your real tests.
test('it exists', function(assert) {
let adapter = this.owner.lookup('adapter:application');
const adapter = this.owner.lookup('adapter:application');
assert.ok(adapter);
});
test('slugFromURL returns the slug (on the assumptions its the last chunk of the url)', function(assert) {
const adapter = this.owner.lookup('adapter:application');
const decode = this.stub().returnsArg(0);
const expected = 'slug';
const actual = adapter.slugFromURL({ pathname: `/this/is/a/url/with/a/${expected}` }, decode);
assert.equal(actual, expected);
assert.ok(decode.calledOnce);
});
test("uidForURL returns the a 'unique' hash for the uid using the entire url", function(assert) {
const adapter = this.owner.lookup('adapter:application');
const hash = this.stub().returnsArg(0);
const expected = ['dc-1', 'slug'];
const url = {
pathname: `/this/is/a/url/with/a/${expected[1]}`,
searchParams: {
get: this.stub().returns('dc-1'),
},
};
const actual = adapter.uidForURL(url, '', hash);
assert.deepEqual(actual, expected);
assert.ok(hash.calledOnce);
assert.ok(url.searchParams.get.calledOnce);
});
test("uidForURL returns the a 'unique' hash for the uid when specifying the slug", function(assert) {
const adapter = this.owner.lookup('adapter:application');
const hash = this.stub().returnsArg(0);
const expected = ['dc-1', 'slug'];
const url = {
searchParams: {
get: this.stub().returns('dc-1'),
},
};
const actual = adapter.uidForURL(url, expected[1], hash);
assert.deepEqual(actual, expected);
assert.ok(hash.calledOnce);
assert.ok(url.searchParams.get.calledOnce);
});
test("uidForURL throws an error if it can't find a datacenter on the search params", function(assert) {
const adapter = this.owner.lookup('adapter:application');
const hash = this.stub().returnsArg(0);
const expected = ['dc-1', 'slug'];
const url = {
pathname: `/this/is/a/url/with/a/${expected[1]}`,
searchParams: {
get: this.stub().returns(''),
},
};
assert.throws(function() {
adapter.uidForURL(url, expected[1], hash);
}, /datacenter/);
assert.ok(url.searchParams.get.calledOnce);
});
test("uidForURL throws an error if it can't find a slug", function(assert) {
const adapter = this.owner.lookup('adapter:application');
const hash = this.stub().returnsArg(0);
const url = {
pathname: `/`,
searchParams: {
get: this.stub().returns('dc-1'),
},
};
assert.throws(function() {
adapter.uidForURL(url, '', hash);
}, /slug/);
assert.ok(url.searchParams.get.calledOnce);
});
test("uidForURL throws an error if it can't find a slug", function(assert) {
const adapter = this.owner.lookup('adapter:application');
const hash = this.stub().returnsArg(0);
const url = {
pathname: `/`,
searchParams: {
get: this.stub().returns('dc-1'),
},
};
assert.throws(function() {
adapter.uidForURL(url, '', hash);
}, /slug/);
assert.ok(url.searchParams.get.calledOnce);
});
test('handleBooleanResponse returns the expected pojo structure', function(assert) {
const adapter = this.owner.lookup('adapter:application');
adapter.uidForURL = this.stub().returnsArg(0);
const expected = {
'primary-key-name': 'url',
};
const actual = adapter.handleBooleanResponse('url', {}, Object.keys(expected)[0], 'slug');
assert.deepEqual(actual, expected);
assert.ok(adapter.uidForURL.calledOnce);
});
test('handleSingleResponse returns the expected pojo structure', function(assert) {
const adapter = this.owner.lookup('adapter:application');
const url = {
pathname: `/`,
searchParams: {
get: this.stub().returns('dc-1'),
},
};
adapter.uidForURL = this.stub().returns('name');
const expected = {
Datacenter: 'dc-1',
Name: 'name',
'primary-key-name': 'name',
};
const actual = adapter.handleSingleResponse(url, { Name: 'name' }, 'primary-key-name', 'Name');
assert.deepEqual(actual, expected);
assert.ok(adapter.uidForURL.calledOnce);
});
test('handleBatchResponse returns the expected pojo structure', function(assert) {
const adapter = this.owner.lookup('adapter:application');
const url = {
pathname: `/`,
searchParams: {
get: this.stub().returns('dc-1'),
},
};
adapter.uidForURL = this.stub().returnsArg(1);
const expected = [
{
Datacenter: 'dc-1',
Name: 'name1',
'primary-key-name': 'name1',
},
{
Datacenter: 'dc-1',
Name: 'name2',
'primary-key-name': 'name2',
},
];
const actual = adapter.handleBatchResponse(
url,
[{ Name: 'name1' }, { Name: 'name2' }],
'primary-key-name',
'Name'
);
assert.deepEqual(actual, expected);
assert.ok(adapter.uidForURL.calledTwice);
});
});

View File

@ -44,6 +44,7 @@ module('Unit | Adapter | kv', function(hooks) {
});
it("returns the original payload plus the uid if it's not a Boolean", function() {
const uid = {
Datacenter: dc,
uid: JSON.stringify([dc, expected]),
};
const actual = adapter.handleResponse(200, {}, [uid], { url: url });

View File

@ -82,12 +82,12 @@
js-yaml "^3.10.0"
"@hashicorp/consul-api-double@^1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-1.4.0.tgz#6b5222263f2acf1539d0e375c1a935d50217b9ab"
version "1.4.1"
resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-1.4.1.tgz#547643b98c3a26a1fe1584189bd05e4d9f383966"
"@hashicorp/ember-cli-api-double@^1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@hashicorp/ember-cli-api-double/-/ember-cli-api-double-1.3.0.tgz#d07b5b11701cd55d6b01cb8a47ce17c4bac21fed"
version "1.4.0"
resolved "https://registry.yarnpkg.com/@hashicorp/ember-cli-api-double/-/ember-cli-api-double-1.4.0.tgz#4190b30f8b6a51ec33a707c45effede6e93e6b38"
dependencies:
"@hashicorp/api-double" "^1.3.0"
array-range "^1.0.1"
@ -4064,8 +4064,8 @@ esprima@^2.6.0:
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
esprima@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804"
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
esprima@~3.0.0:
version "3.0.0"