diff --git a/ui-v2/app/serializers/application.js b/ui-v2/app/serializers/application.js index 650054ea2155..fb2bd13dbe7f 100644 --- a/ui-v2/app/serializers/application.js +++ b/ui-v2/app/serializers/application.js @@ -5,6 +5,8 @@ import config from 'consul-ui/config/environment'; import { HEADERS_SYMBOL as HTTP_HEADERS_SYMBOL, HEADERS_INDEX as HTTP_HEADERS_INDEX, + HEADERS_DATACENTER as HTTP_HEADERS_DATACENTER, + HEADERS_NAMESPACE as HTTP_HEADERS_NAMESPACE, } from 'consul-ui/utils/http/consul'; import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc'; import { NSPACE_KEY } from 'consul-ui/models/nspace'; @@ -17,12 +19,21 @@ const map = function(obj, cb) { return obj.map(cb); }; -const attachHeaders = function(headers, body) { +const attachHeaders = function(headers, body, query = {}) { // lowercase everything incase we get browser inconsistencies const lower = {}; Object.keys(headers).forEach(function(key) { lower[key.toLowerCase()] = headers[key]; }); + // Add a 'pretend' Datacenter/Nspace header, they are not headers + // the come from the request but we add them here so we can use them later + // for store reconciliation + if (typeof query.dc !== 'undefined') { + lower[HTTP_HEADERS_DATACENTER.toLowerCase()] = query.dc; + } + lower[HTTP_HEADERS_NAMESPACE.toLowerCase()] = + typeof query.ns !== 'undefined' ? query.ns : config.CONSUL_NSPACES_UNDEFINED_NAME; + // body[HTTP_HEADERS_SYMBOL] = lower; return body; }; @@ -36,12 +47,16 @@ export default Serializer.extend({ ), respondForQuery: function(respond, query) { return respond((headers, body) => - attachHeaders(headers, map(body, this.fingerprint(this.primaryKey, this.slugKey, query.dc))) + attachHeaders( + headers, + map(body, this.fingerprint(this.primaryKey, this.slugKey, query.dc)), + query + ) ); }, respondForQueryRecord: function(respond, query) { return respond((headers, body) => - attachHeaders(headers, this.fingerprint(this.primaryKey, this.slugKey, query.dc)(body)) + attachHeaders(headers, this.fingerprint(this.primaryKey, this.slugKey, query.dc)(body), query) ); }, respondForCreateRecord: function(respond, serialized, data) { @@ -127,6 +142,8 @@ export default Serializer.extend({ normalizeMeta: function(store, primaryModelClass, headers, payload, id, requestType) { const meta = { cursor: headers[HTTP_HEADERS_INDEX], + dc: headers[HTTP_HEADERS_DATACENTER.toLowerCase()], + nspace: headers[HTTP_HEADERS_NAMESPACE.toLowerCase()], }; if (requestType === 'query') { meta.date = this.timestamp(); diff --git a/ui-v2/app/services/repository/type/event-source.js b/ui-v2/app/services/repository/type/event-source.js index f8dbe96da668..123a63bc7e4b 100644 --- a/ui-v2/app/services/repository/type/event-source.js +++ b/ui-v2/app/services/repository/type/event-source.js @@ -18,9 +18,13 @@ const createProxy = function(repo, find, settings, cache, serialize = JSON.strin if (typeof meta.date !== 'undefined') { // unload anything older than our current sync date/time store.peekAll(repo.getModelName()).forEach(function(item) { - const date = get(item, 'SyncTime'); - if (typeof date !== 'undefined' && date != meta.date) { - store.unloadRecord(item); + const dc = get(item, 'Datacenter'); + const nspace = get(item, 'Namespace'); + if (dc === meta.dc && nspace === meta.nspace) { + const date = get(item, 'SyncTime'); + if (typeof date !== 'undefined' && date != meta.date) { + store.unloadRecord(item); + } } }); } diff --git a/ui-v2/app/utils/http/consul.js b/ui-v2/app/utils/http/consul.js index 10031c08c51d..4ce38d33b5dd 100644 --- a/ui-v2/app/utils/http/consul.js +++ b/ui-v2/app/utils/http/consul.js @@ -1,5 +1,6 @@ // TODO: Need to make all these headers capital case export const HEADERS_NAMESPACE = 'X-Consul-Namespace'; +export const HEADERS_DATACENTER = 'x-consul-datacenter'; export const HEADERS_INDEX = 'x-consul-index'; export const HEADERS_DIGEST = 'x-consul-contenthash'; // diff --git a/ui-v2/tests/acceptance/dc/services/dc-switch.feature b/ui-v2/tests/acceptance/dc/services/dc-switch.feature new file mode 100644 index 000000000000..e51472ebd66c --- /dev/null +++ b/ui-v2/tests/acceptance/dc/services/dc-switch.feature @@ -0,0 +1,27 @@ +@setupApplicationTest +Feature: dc / services / dc-switch : Switching Datacenters + Scenario: Seeing all services when switching datacenters + Given 2 datacenter models from yaml + --- + - dc-1 + - dc-2 + --- + And 6 service models + When I visit the services page for yaml + --- + dc: dc-1 + --- + Then the url should be /dc-1/services + Then I see 6 service models + When I click dc on the navigation + And I click dcs.1.name + Then the url should be /dc-2/services + Then I see 6 service models + When I click dc on the navigation + And I click dcs.0.name + Then the url should be /dc-1/services + Then I see 6 service models + When I click dc on the navigation + And I click dcs.1.name + Then the url should be /dc-2/services + Then I see 6 service models diff --git a/ui-v2/tests/acceptance/steps/dc/services/dc-switch-steps.js b/ui-v2/tests/acceptance/steps/dc/services/dc-switch-steps.js new file mode 100644 index 000000000000..ba1093295f36 --- /dev/null +++ b/ui-v2/tests/acceptance/steps/dc/services/dc-switch-steps.js @@ -0,0 +1,10 @@ +import steps from '../../steps'; + +// step definitions that are shared between features should be moved to the +// tests/acceptance/steps/steps.js file + +export default function(assert) { + return steps(assert).then('I should find a file', function() { + assert.ok(true, this.step); + }); +} diff --git a/ui-v2/tests/integration/serializers/acl-test.js b/ui-v2/tests/integration/serializers/acl-test.js index 284577c5c1e2..32eb056a4201 100644 --- a/ui-v2/tests/integration/serializers/acl-test.js +++ b/ui-v2/tests/integration/serializers/acl-test.js @@ -1,7 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import { get } from 'consul-ui/tests/helpers/api'; -import { HEADERS_SYMBOL as META } from 'consul-ui/utils/http/consul'; +import { + HEADERS_SYMBOL as META, + HEADERS_DATACENTER as DC, + HEADERS_NAMESPACE as NSPACE, +} from 'consul-ui/utils/http/consul'; module('Integration | Serializer | acl', function(hooks) { setupTest(hooks); const dc = 'dc-1'; @@ -43,7 +47,10 @@ module('Integration | Serializer | acl', function(hooks) { return get(request.url).then(function(payload) { const expected = Object.assign({}, payload[0], { Datacenter: dc, - [META]: {}, + [META]: { + [DC.toLowerCase()]: dc, + [NSPACE.toLowerCase()]: nspace, + }, // TODO: default isn't required here, once we've // refactored out our Serializer this can go Namespace: nspace, diff --git a/ui-v2/tests/integration/serializers/intention-test.js b/ui-v2/tests/integration/serializers/intention-test.js index 048e42416b79..2f503ac4777d 100644 --- a/ui-v2/tests/integration/serializers/intention-test.js +++ b/ui-v2/tests/integration/serializers/intention-test.js @@ -1,7 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import { get } from 'consul-ui/tests/helpers/api'; -import { HEADERS_SYMBOL as META } from 'consul-ui/utils/http/consul'; +import { + HEADERS_SYMBOL as META, + HEADERS_DATACENTER as DC, + HEADERS_NAMESPACE as NSPACE, +} from 'consul-ui/utils/http/consul'; module('Integration | Serializer | intention', function(hooks) { setupTest(hooks); const dc = 'dc-1'; @@ -43,7 +47,10 @@ module('Integration | Serializer | intention', function(hooks) { return get(request.url).then(function(payload) { const expected = Object.assign({}, payload, { Datacenter: dc, - [META]: {}, + [META]: { + [DC.toLowerCase()]: dc, + [NSPACE.toLowerCase()]: nspace, + }, // TODO: default isn't required here, once we've // refactored out our Serializer this can go Namespace: nspace, diff --git a/ui-v2/tests/integration/serializers/kv-test.js b/ui-v2/tests/integration/serializers/kv-test.js index cc878c1df3a0..7907a994be81 100644 --- a/ui-v2/tests/integration/serializers/kv-test.js +++ b/ui-v2/tests/integration/serializers/kv-test.js @@ -1,7 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import { get } from 'consul-ui/tests/helpers/api'; -import { HEADERS_SYMBOL as META } from 'consul-ui/utils/http/consul'; +import { + HEADERS_SYMBOL as META, + HEADERS_DATACENTER as DC, + HEADERS_NAMESPACE as NSPACE, +} from 'consul-ui/utils/http/consul'; module('Integration | Serializer | kv', function(hooks) { setupTest(hooks); const dc = 'dc-1'; @@ -51,7 +55,10 @@ module('Integration | Serializer | kv', function(hooks) { return get(request.url).then(function(payload) { const expected = Object.assign({}, payload[0], { Datacenter: dc, - [META]: {}, + [META]: { + [DC.toLowerCase()]: dc, + [NSPACE.toLowerCase()]: payload[0].Namespace || undefinedNspace, + }, Namespace: payload[0].Namespace || undefinedNspace, uid: `["${payload[0].Namespace || undefinedNspace}","${dc}","${id}"]`, }); diff --git a/ui-v2/tests/integration/serializers/node-test.js b/ui-v2/tests/integration/serializers/node-test.js index 6962665ee020..400c4b9b43ca 100644 --- a/ui-v2/tests/integration/serializers/node-test.js +++ b/ui-v2/tests/integration/serializers/node-test.js @@ -1,7 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import { get } from 'consul-ui/tests/helpers/api'; -import { HEADERS_SYMBOL as META } from 'consul-ui/utils/http/consul'; +import { + HEADERS_SYMBOL as META, + HEADERS_DATACENTER as DC, + HEADERS_NAMESPACE as NSPACE, +} from 'consul-ui/utils/http/consul'; module('Integration | Serializer | node', function(hooks) { setupTest(hooks); const nspace = 'default'; @@ -44,7 +48,10 @@ module('Integration | Serializer | node', function(hooks) { return get(request.url).then(function(payload) { const expected = Object.assign({}, payload, { Datacenter: dc, - [META]: {}, + [META]: { + [DC.toLowerCase()]: dc, + [NSPACE.toLowerCase()]: nspace, + }, // TODO: default isn't required here, once we've // refactored out our Serializer this can go Namespace: nspace, diff --git a/ui-v2/tests/integration/serializers/policy-test.js b/ui-v2/tests/integration/serializers/policy-test.js index 4cb7063ec9a6..6a098574f0ee 100644 --- a/ui-v2/tests/integration/serializers/policy-test.js +++ b/ui-v2/tests/integration/serializers/policy-test.js @@ -1,7 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import { get } from 'consul-ui/tests/helpers/api'; -import { HEADERS_SYMBOL as META } from 'consul-ui/utils/http/consul'; +import { + HEADERS_SYMBOL as META, + HEADERS_DATACENTER as DC, + HEADERS_NAMESPACE as NSPACE, +} from 'consul-ui/utils/http/consul'; module('Integration | Serializer | policy', function(hooks) { setupTest(hooks); const dc = 'dc-1'; @@ -43,7 +47,10 @@ module('Integration | Serializer | policy', function(hooks) { return get(request.url).then(function(payload) { const expected = Object.assign({}, payload, { Datacenter: dc, - [META]: {}, + [META]: { + [DC.toLowerCase()]: dc, + [NSPACE.toLowerCase()]: payload.Namespace || undefinedNspace, + }, Namespace: payload.Namespace || undefinedNspace, uid: `["${payload.Namespace || undefinedNspace}","${dc}","${id}"]`, }); diff --git a/ui-v2/tests/integration/serializers/role-test.js b/ui-v2/tests/integration/serializers/role-test.js index 03f4ebe579dc..10da23561a94 100644 --- a/ui-v2/tests/integration/serializers/role-test.js +++ b/ui-v2/tests/integration/serializers/role-test.js @@ -1,7 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import { get } from 'consul-ui/tests/helpers/api'; -import { HEADERS_SYMBOL as META } from 'consul-ui/utils/http/consul'; +import { + HEADERS_SYMBOL as META, + HEADERS_DATACENTER as DC, + HEADERS_NAMESPACE as NSPACE, +} from 'consul-ui/utils/http/consul'; import { createPolicies } from 'consul-ui/tests/helpers/normalizers'; module('Integration | Serializer | role', function(hooks) { @@ -47,7 +51,10 @@ module('Integration | Serializer | role', function(hooks) { const expected = Object.assign({}, payload, { Datacenter: dc, Policies: createPolicies(payload), - [META]: {}, + [META]: { + [DC.toLowerCase()]: dc, + [NSPACE.toLowerCase()]: payload.Namespace || undefinedNspace, + }, Namespace: payload.Namespace || undefinedNspace, uid: `["${payload.Namespace || undefinedNspace}","${dc}","${id}"]`, }); diff --git a/ui-v2/tests/integration/serializers/service-test.js b/ui-v2/tests/integration/serializers/service-test.js index c624eb889ea1..f970562dc17a 100644 --- a/ui-v2/tests/integration/serializers/service-test.js +++ b/ui-v2/tests/integration/serializers/service-test.js @@ -1,7 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import { get } from 'consul-ui/tests/helpers/api'; -import { HEADERS_SYMBOL as META } from 'consul-ui/utils/http/consul'; +import { + HEADERS_SYMBOL as META, + HEADERS_DATACENTER as DC, + HEADERS_NAMESPACE as NSPACE, +} from 'consul-ui/utils/http/consul'; module('Integration | Serializer | service', function(hooks) { setupTest(hooks); const dc = 'dc-1'; @@ -47,7 +51,10 @@ module('Integration | Serializer | service', function(hooks) { return get(request.url).then(function(payload) { const expected = { Datacenter: dc, - [META]: {}, + [META]: { + [DC.toLowerCase()]: dc, + [NSPACE.toLowerCase()]: payload[0].Service.Namespace || undefinedNspace, + }, Namespace: payload[0].Service.Namespace || undefinedNspace, uid: `["${payload[0].Service.Namespace || undefinedNspace}","${dc}","${id}"]`, Name: id, diff --git a/ui-v2/tests/integration/serializers/session-test.js b/ui-v2/tests/integration/serializers/session-test.js index c7bdfdcc7596..390819b8afb5 100644 --- a/ui-v2/tests/integration/serializers/session-test.js +++ b/ui-v2/tests/integration/serializers/session-test.js @@ -1,7 +1,7 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import { get } from 'consul-ui/tests/helpers/api'; -import { HEADERS_SYMBOL as META } from 'consul-ui/utils/http/consul'; +import { HEADERS_SYMBOL as META, HEADERS_DATACENTER as DC } from 'consul-ui/utils/http/consul'; module('Integration | Adapter | session | response', function(hooks) { setupTest(hooks); const dc = 'dc-1'; @@ -48,7 +48,9 @@ module('Integration | Adapter | session | response', function(hooks) { return get(request.url).then(function(payload) { const expected = Object.assign({}, payload[0], { Datacenter: dc, - [META]: {}, + [META]: { + [DC.toLowerCase()]: dc, + }, Namespace: payload[0].Namespace || undefinedNspace, uid: `["${payload[0].Namespace || undefinedNspace}","${dc}","${id}"]`, }); diff --git a/ui-v2/tests/integration/serializers/token-test.js b/ui-v2/tests/integration/serializers/token-test.js index 23bd4dbd6a7e..11db9ff2353b 100644 --- a/ui-v2/tests/integration/serializers/token-test.js +++ b/ui-v2/tests/integration/serializers/token-test.js @@ -1,7 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import { get } from 'consul-ui/tests/helpers/api'; -import { HEADERS_SYMBOL as META } from 'consul-ui/utils/http/consul'; +import { + HEADERS_SYMBOL as META, + HEADERS_DATACENTER as DC, + HEADERS_NAMESPACE as NSPACE, +} from 'consul-ui/utils/http/consul'; import { createPolicies } from 'consul-ui/tests/helpers/normalizers'; @@ -47,7 +51,10 @@ module('Integration | Serializer | token', function(hooks) { return get(request.url).then(function(payload) { const expected = Object.assign({}, payload, { Datacenter: dc, - [META]: {}, + [META]: { + [DC.toLowerCase()]: dc, + [NSPACE.toLowerCase()]: payload.Namespace || undefinedNspace, + }, Namespace: payload.Namespace || undefinedNspace, uid: `["${payload.Namespace || undefinedNspace}","${dc}","${id}"]`, Policies: createPolicies(payload), diff --git a/ui-v2/tests/integration/services/repository/node-test.js b/ui-v2/tests/integration/services/repository/node-test.js index 9f32a8bedc20..ba0b8e6734b7 100644 --- a/ui-v2/tests/integration/services/repository/node-test.js +++ b/ui-v2/tests/integration/services/repository/node-test.js @@ -64,6 +64,7 @@ test('findBySlug returns the correct data for item endpoint', function(assert) { uid: `["${nspace}","${dc}","${item.ID}"]`, meta: { cursor: undefined, + dc: dc, }, }); }) diff --git a/ui-v2/tests/integration/services/repository/service-test.js b/ui-v2/tests/integration/services/repository/service-test.js index 4487b42e49a6..3f63225c9cbb 100644 --- a/ui-v2/tests/integration/services/repository/service-test.js +++ b/ui-v2/tests/integration/services/repository/service-test.js @@ -91,6 +91,7 @@ const undefinedNspace = 'default'; service.Namespace = payload.Namespace; service.meta = { cursor: undefined, + dc: dc, }; return service; diff --git a/ui-v2/tests/pages/components/page.js b/ui-v2/tests/pages/components/page.js index aa634c7b1bf9..86d864955d71 100644 --- a/ui-v2/tests/pages/components/page.js +++ b/ui-v2/tests/pages/components/page.js @@ -1,5 +1,5 @@ import { clickable } from 'ember-cli-page-object'; -export default { +const page = { navigation: ['services', 'nodes', 'kvs', 'acls', 'intentions', 'docs', 'settings'].reduce( function(prev, item, i, arr) { const key = item; @@ -23,3 +23,5 @@ export default { } ), }; +page.navigation.dc = clickable('[data-test-toggle-button="datacenters"]'); +export default page; diff --git a/ui-v2/tests/pages/dc/services/index.js b/ui-v2/tests/pages/dc/services/index.js index 551a95fef277..3ecab2a87639 100644 --- a/ui-v2/tests/pages/dc/services/index.js +++ b/ui-v2/tests/pages/dc/services/index.js @@ -6,7 +6,9 @@ export default function(visitable, clickable, attribute, collection, page, filte service: clickable('a'), externalSource: attribute('data-test-external-source', 'a span'), }), - dcs: collection('[data-test-datacenter-picker]'), + dcs: collection('[data-test-datacenter-picker]', { + name: clickable('a'), + }), navigation: page.navigation, filter: filter, }; diff --git a/ui-v2/tests/steps.js b/ui-v2/tests/steps.js index 648a86422f46..b5385be010eb 100644 --- a/ui-v2/tests/steps.js +++ b/ui-v2/tests/steps.js @@ -46,7 +46,7 @@ export default function(assert, library, pages, utils) { if (isNaN(parseInt(prop))) { return (obj = obj[prop]); } else { - return (obj = obj.objectAt(prop)); + return (obj = obj.objectAt(parseInt(prop))); } }) && obj ); diff --git a/ui-v2/tests/unit/serializers/application-test.js b/ui-v2/tests/unit/serializers/application-test.js index 88fc6d0f4b0c..39eb5f0841ea 100644 --- a/ui-v2/tests/unit/serializers/application-test.js +++ b/ui-v2/tests/unit/serializers/application-test.js @@ -1,6 +1,11 @@ import { module } from 'qunit'; import test from 'ember-sinon-qunit/test-support/test'; import { setupTest } from 'ember-qunit'; +import { + HEADERS_SYMBOL as META, + HEADERS_DATACENTER as DC, + HEADERS_NAMESPACE as NSPACE, +} from 'consul-ui/utils/http/consul'; module('Unit | Serializer | application', function(hooks) { setupTest(hooks); @@ -60,7 +65,10 @@ module('Unit | Serializer | application', function(hooks) { const expected = { Datacenter: 'dc-1', Name: 'name', - __consul_ui_http_headers__: {}, + [META]: { + [DC.toLowerCase()]: 'dc-1', + [NSPACE.toLowerCase()]: 'default', + }, 'primary-key-name': 'name', }; const respond = function(cb) {