diff --git a/ui-v2/app/adapters/gateway.js b/ui-v2/app/adapters/gateway.js new file mode 100644 index 000000000..96d1e4cf1 --- /dev/null +++ b/ui-v2/app/adapters/gateway.js @@ -0,0 +1,17 @@ +import Adapter from './application'; + +export default Adapter.extend({ + requestForQueryRecord: function(request, { dc, ns, index, id }) { + if (typeof id === 'undefined') { + throw new Error('You must specify an id'); + } + return request` + GET /v1/internal/ui/gateway-services-nodes/${id}?${{ dc }} + + ${{ + ...this.formatNspace(ns), + index, + }} + `; + }, +}); diff --git a/ui-v2/app/models/gateway.js b/ui-v2/app/models/gateway.js new file mode 100644 index 000000000..85b081a0f --- /dev/null +++ b/ui-v2/app/models/gateway.js @@ -0,0 +1,12 @@ +import Model from 'ember-data/model'; +import attr from 'ember-data/attr'; + +export const PRIMARY_KEY = 'uid'; +export const SLUG_KEY = 'Name'; +export default Model.extend({ + [PRIMARY_KEY]: attr('string'), + [SLUG_KEY]: attr('string'), + Datacenter: attr('string'), + Namespace: attr('string'), + Services: attr(), +}); diff --git a/ui-v2/app/router.js b/ui-v2/app/router.js index d11aa5a95..794b5f98a 100644 --- a/ui-v2/app/router.js +++ b/ui-v2/app/router.js @@ -19,6 +19,12 @@ export const routes = { intentions: { _options: { path: '/intentions' }, }, + services: { + _options: { path: '/services' }, + }, + upstreams: { + _options: { path: '/upstreams' }, + }, routing: { _options: { path: '/routing' }, }, diff --git a/ui-v2/app/routes/dc/services/show.js b/ui-v2/app/routes/dc/services/show.js index 0ff145fbf..a01281f55 100644 --- a/ui-v2/app/routes/dc/services/show.js +++ b/ui-v2/app/routes/dc/services/show.js @@ -8,6 +8,7 @@ export default Route.extend({ intentionRepo: service('repository/intention'), chainRepo: service('repository/discovery-chain'), proxyRepo: service('repository/proxy'), + gatewayRepo: service('repository/gateway'), settings: service('settings'), model: function(params, transition = {}) { const dc = this.modelFor('dc').dc.Name; @@ -17,32 +18,43 @@ export default Route.extend({ urls: this.settings.findBySlug('urls'), dc: dc, proxies: [], - }).then(model => { - return ['connect-proxy', 'mesh-gateway'].includes(get(model, 'item.Service.Kind')) - ? model - : hash({ - intentions: this.intentionRepo.findByService(params.name, dc, nspace), - chain: this.chainRepo.findBySlug(params.name, dc, nspace).catch(function(e) { - const code = get(e, 'errors.firstObject.status'); - // Currently we are specifically catching a 500, but we return null - // by default, so null for all errors. - // The extra code here is mainly for documentation purposes - // and for if we need to perform different actions based on the error code - // in the future - switch (code) { - case '500': - // connect is likely to be disabled - // we just return a null to hide the tab - // `Connect must be enabled in order to use this endpoint` - return null; - default: - return null; - } - }), - proxies: this.proxyRepo.findAllBySlug(params.name, dc, nspace), - ...model, - }); - }); + }) + .then(model => { + return ['connect-proxy', 'mesh-gateway', 'ingress-gateway', 'terminating-gateway'].includes( + get(model, 'item.Service.Kind') + ) + ? model + : hash({ + intentions: this.intentionRepo.findByService(params.name, dc, nspace), + chain: this.chainRepo.findBySlug(params.name, dc, nspace).catch(function(e) { + const code = get(e, 'errors.firstObject.status'); + // Currently we are specifically catching a 500, but we return null + // by default, so null for all errors. + // The extra code here is mainly for documentation purposes + // and for if we need to perform different actions based on the error code + // in the future + switch (code) { + case '500': + // connect is likely to be disabled + // we just return a null to hide the tab + // `Connect must be enabled in order to use this endpoint` + return null; + default: + return null; + } + }), + proxies: this.proxyRepo.findAllBySlug(params.name, dc, nspace), + ...model, + }); + }) + .then(model => { + return ['ingress-gateway', 'terminating-gateway'].includes(get(model, 'item.Service.Kind')) + ? hash({ + gateway: this.gatewayRepo.findBySlug(params.name, dc, nspace), + ...model, + }) + : model; + }); }, setupController: function(controller, model) { controller.setProperties(model); diff --git a/ui-v2/app/routes/dc/services/show/services.js b/ui-v2/app/routes/dc/services/show/services.js new file mode 100644 index 000000000..9793d62c0 --- /dev/null +++ b/ui-v2/app/routes/dc/services/show/services.js @@ -0,0 +1,14 @@ +import Route from '@ember/routing/route'; + +export default Route.extend({ + model: function() { + const parent = this.routeName + .split('.') + .slice(0, -1) + .join('.'); + return this.modelFor(parent); + }, + setupController: function(controller, model) { + controller.setProperties(model); + }, +}); diff --git a/ui-v2/app/routes/dc/services/show/upstreams.js b/ui-v2/app/routes/dc/services/show/upstreams.js new file mode 100644 index 000000000..9793d62c0 --- /dev/null +++ b/ui-v2/app/routes/dc/services/show/upstreams.js @@ -0,0 +1,14 @@ +import Route from '@ember/routing/route'; + +export default Route.extend({ + model: function() { + const parent = this.routeName + .split('.') + .slice(0, -1) + .join('.'); + return this.modelFor(parent); + }, + setupController: function(controller, model) { + controller.setProperties(model); + }, +}); diff --git a/ui-v2/app/serializers/gateway.js b/ui-v2/app/serializers/gateway.js new file mode 100644 index 000000000..069c7a695 --- /dev/null +++ b/ui-v2/app/serializers/gateway.js @@ -0,0 +1,17 @@ +import Serializer from './application'; +import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/gateway'; + +export default Serializer.extend({ + primaryKey: PRIMARY_KEY, + slugKey: SLUG_KEY, + respondForQueryRecord: function(respond, query) { + return this._super(function(cb) { + return respond(function(headers, body) { + return cb(headers, { + Name: query.id, + Services: body, + }); + }); + }, query); + }, +}); diff --git a/ui-v2/app/services/repository/gateway.js b/ui-v2/app/services/repository/gateway.js new file mode 100644 index 000000000..647cf8662 --- /dev/null +++ b/ui-v2/app/services/repository/gateway.js @@ -0,0 +1,8 @@ +import RepositoryService from 'consul-ui/services/repository'; + +const modelName = 'gateway'; +export default RepositoryService.extend({ + getModelName: function() { + return modelName; + }, +}); diff --git a/ui-v2/app/styles/components/composite-row/layout.scss b/ui-v2/app/styles/components/composite-row/layout.scss index 7175b2166..7826a7b72 100644 --- a/ui-v2/app/styles/components/composite-row/layout.scss +++ b/ui-v2/app/styles/components/composite-row/layout.scss @@ -33,3 +33,12 @@ %composite-row-detail .port button:hover { background-color: transparent !important; } + +// Tooltip +%composite-row-detail .feedback-dialog-out { + left: -12px; + bottom: 12px; +} +%composite-row-detail .feedback-dialog-out::after { + left: 18px; +} diff --git a/ui-v2/app/styles/components/composite-row/skin.scss b/ui-v2/app/styles/components/composite-row/skin.scss index b80065922..3f7b6fc4d 100644 --- a/ui-v2/app/styles/components/composite-row/skin.scss +++ b/ui-v2/app/styles/components/composite-row/skin.scss @@ -1,9 +1,9 @@ %composite-row { list-style-type: none; - border-top-color: $gray-200; - border-bottom-color: transparent; - border-right-color: transparent; - border-left-color: transparent; + border-top-color: $transparent; + border-bottom-color: $gray-200; + border-right-color: $transparent; + border-left-color: $transparent; } %composite-row-intent { border-color: $gray-200; @@ -16,9 +16,6 @@ %composite-row-detail { color: $gray-500; } -%composite-row:last-child { - border-bottom-color: $gray-200; -} // Health Checks %composite-row .passing::before { diff --git a/ui-v2/app/styles/components/consul-service-list.scss b/ui-v2/app/styles/components/consul-service-list.scss index d6d41bbbf..82c9cc3d1 100644 --- a/ui-v2/app/styles/components/consul-service-list.scss +++ b/ui-v2/app/styles/components/consul-service-list.scss @@ -1,7 +1,7 @@ .consul-service-list > ul { @extend %consul-service-list; } -%consul-service-list > li { +%consul-service-list > li:not(:first-child) { @extend %consul-service-row; } %consul-service-row { diff --git a/ui-v2/app/styles/components/list-collection.scss b/ui-v2/app/styles/components/list-collection.scss index a91557d06..78505f903 100644 --- a/ui-v2/app/styles/components/list-collection.scss +++ b/ui-v2/app/styles/components/list-collection.scss @@ -1,7 +1,19 @@ .list-collection { + @extend %list-collection; height: 500px; position: relative; } -.list-collection > ul { +%list-collection > ul { + border-top: 1px solid $gray-200; overflow-x: hidden !important; } +%list-collection > ul > li:nth-child(2) .with-feedback p { + bottom: auto; + top: 24px; +} +%list-collection > ul > li:nth-child(2) p::after { + bottom: auto; + top: -10px !important; + border-bottom-width: 18px; + border-top-width: 0; +} diff --git a/ui-v2/app/styles/routes/dc/services/index.scss b/ui-v2/app/styles/routes/dc/services/index.scss index 5c6071c98..6f4f8c89f 100644 --- a/ui-v2/app/styles/routes/dc/services/index.scss +++ b/ui-v2/app/styles/routes/dc/services/index.scss @@ -1,5 +1,19 @@ +// Services - Linked Services tab +// TODO - move this into composite-row +.consul-gateway-services-list > ul { + @extend %consul-gateway-services-list; +} +%consul-gateway-services-list > li:not(:first-child) { + @extend %gateway-service-row; +} +%gateway-service-row { + @extend %composite-row, %with-composite-row-intent; +} + +// Service Detail - Proxy Info tab .proxy-upstreams > ul { @extend %proxy-upstreams-list; + border-top: 1px solid $gray-200; } %proxy-upstreams-list > li { @extend %composite-row; diff --git a/ui-v2/app/templates/dc/services/show.hbs b/ui-v2/app/templates/dc/services/show.hbs index 0f766c90f..eaa5b44cf 100644 --- a/ui-v2/app/templates/dc/services/show.hbs +++ b/ui-v2/app/templates/dc/services/show.hbs @@ -17,14 +17,26 @@ - {{#if (not item.Service.Kind)}} + {{#if (not-eq item.Service.Kind 'mesh-gateway')}} {{/if}} diff --git a/ui-v2/app/templates/dc/services/show/services.hbs b/ui-v2/app/templates/dc/services/show/services.hbs new file mode 100644 index 000000000..1d2a29f48 --- /dev/null +++ b/ui-v2/app/templates/dc/services/show/services.hbs @@ -0,0 +1,43 @@ +
+
+ {{#if (gt gateway.Services.length 0)}} +
+

+ The following services may receive traffic from external services through this gateway. Learn more about configuring gateways in our + step-by-step guide. + +

+ {{#let item.Service.Namespace as |nspace|}} + + + {{item.Name}} + +
    + {{#if (env 'CONSUL_NSPACES_ENABLED')}} + {{#if (not-eq item.Namespace nspace)}} +
  • + {{item.Namespace}} +
  • + {{/if}} + {{/if}} + {{#if (not-eq item.InstanceCount 0)}} +
  • + {{format-number item.InstanceCount}} {{pluralize item.InstanceCount 'Instance' without-count=true}} +
  • + {{/if}} + +
  • + +
  • +
    +
+
+ {{/let}} +
+ {{else}} +

+ There are no linked services. +

+ {{/if}} +
+
diff --git a/ui-v2/app/templates/dc/services/show/upstreams.hbs b/ui-v2/app/templates/dc/services/show/upstreams.hbs new file mode 100644 index 000000000..61764e1a3 --- /dev/null +++ b/ui-v2/app/templates/dc/services/show/upstreams.hbs @@ -0,0 +1,41 @@ +
+
+ {{#if (gt gateway.Services.length 0)}} +
+

+ Upstreams are services that may receive traffic from this gateway. Learn more about configuring gateways in our + documentation. +

+ {{#let item.Service.Namespace as |nspace|}} + + + {{item.Name}} + +
    + {{#if (env 'CONSUL_NSPACES_ENABLED')}} + {{#if (not-eq item.Namespace nspace)}} +
  • + {{item.Namespace}} +
  • + {{/if}} + {{/if}} + {{#if (not-eq item.GatewayConfig.ListenerPort 0)}} +
  • + + :{{item.GatewayConfig.ListenerPort}} +
  • + {{/if}} +
+
+ {{/let}} +
+ {{else}} +

+ There are no upstreams. +

+ {{/if}} +
+
diff --git a/ui-v2/tests/acceptance/dc/services/show/services.feature b/ui-v2/tests/acceptance/dc/services/show/services.feature new file mode 100644 index 000000000..cc576c6c1 --- /dev/null +++ b/ui-v2/tests/acceptance/dc/services/show/services.feature @@ -0,0 +1,57 @@ +@setupApplicationTest +Feature: dc / services / show / services + Background: + Given 1 datacenter model with the value "dc1" + And 1 node models + And 1 service model from yaml + --- + - Service: + Name: terminating-gateway-1 + Kind: terminating-gateway + --- + Scenario: Seeing the Linked Services tab + When I visit the service page for yaml + --- + dc: dc1 + service: terminating-gateway-1 + --- + And the title should be "terminating-gateway-1 - Consul" + And I see linkedServices on the tabs + When I click linkedServices on the tabs + And I see linkedServicesIsSelected on the tabs + Scenario: Seeing the list of Linked Services + Given 3 service models from yaml + When I visit the service page for yaml + --- + dc: dc1 + service: terminating-gateway-1 + --- + And the title should be "terminating-gateway-1 - Consul" + When I click linkedServices on the tabs + Then I see 3 service models + Scenario: Don't see the Linked Services tab + Given 1 datacenter model with the value "dc1" + And 1 node models + And 1 service model from yaml + --- + - Service: + Name: [Name] + Kind: [Kind] + --- + When I visit the service page for yaml + --- + dc: dc1 + service: [Name] + --- + And the title should be "[Name] - Consul" + And I don't see linkedServices on the tabs + Where: + --------------------------------------------- + | Name | Kind | + | service | ~ | + | ingress-gateway | ingress-gateway | + | mesh-gateway | mesh-gateway | + --------------------------------------------- + + + diff --git a/ui-v2/tests/acceptance/dc/services/show/upstreams.feature b/ui-v2/tests/acceptance/dc/services/show/upstreams.feature new file mode 100644 index 000000000..4e0def920 --- /dev/null +++ b/ui-v2/tests/acceptance/dc/services/show/upstreams.feature @@ -0,0 +1,54 @@ +@setupApplicationTest +Feature: dc / services / show / upstreams + Background: + Given 1 datacenter model with the value "dc1" + And 1 node models + And 1 service model from yaml + --- + - Service: + Name: ingress-gateway-1 + Kind: ingress-gateway + --- + Scenario: Seeing the Upstreams tab + When I visit the service page for yaml + --- + dc: dc1 + service: ingress-gateway-1 + --- + And the title should be "ingress-gateway-1 - Consul" + And I see upstreams on the tabs + When I click upstreams on the tabs + And I see upstreamsIsSelected on the tabs + Scenario: Seeing the list of Upstreams + Given 3 service models from yaml + When I visit the service page for yaml + --- + dc: dc1 + service: ingress-gateway-1 + --- + And the title should be "ingress-gateway-1 - Consul" + When I click upstreams on the tabs + Then I see 3 service models + Scenario: Don't see the Upstreams tab + Given 1 datacenter model with the value "dc1" + And 1 node models + And 1 service model from yaml + --- + - Service: + Name: [Name] + Kind: [Kind] + --- + When I visit the service page for yaml + --- + dc: dc1 + service: [Name] + --- + And the title should be "[Name] - Consul" + And I don't see upstreams on the tabs + Where: + --------------------------------------------- + | Name | Kind | + | service | ~ | + | terminating-gateway | terminating-gateway | + | mesh-gateway | mesh-gateway | + --------------------------------------------- diff --git a/ui-v2/tests/acceptance/steps/dc/services/show/services-steps.js b/ui-v2/tests/acceptance/steps/dc/services/show/services-steps.js new file mode 100644 index 000000000..3231912b9 --- /dev/null +++ b/ui-v2/tests/acceptance/steps/dc/services/show/services-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/acceptance/steps/dc/services/show/upstreams-steps.js b/ui-v2/tests/acceptance/steps/dc/services/show/upstreams-steps.js new file mode 100644 index 000000000..3231912b9 --- /dev/null +++ b/ui-v2/tests/acceptance/steps/dc/services/show/upstreams-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/adapters/gateway-test.js b/ui-v2/tests/integration/adapters/gateway-test.js new file mode 100644 index 000000000..f2138a61b --- /dev/null +++ b/ui-v2/tests/integration/adapters/gateway-test.js @@ -0,0 +1,27 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Integration | Adapter | gateway', function(hooks) { + setupTest(hooks); + const dc = 'dc-1'; + const id = 'slug'; + test('requestForQueryRecord returns the correct url/method', function(assert) { + const adapter = this.owner.lookup('adapter:gateway'); + const client = this.owner.lookup('service:client/http'); + const expected = `GET /v1/internal/ui/gateway-services-nodes/${id}?dc=${dc}`; + const actual = adapter.requestForQueryRecord(client.url, { + dc: dc, + id: id, + }); + assert.equal(actual, expected); + }); + test("requestForQueryRecord throws if you don't specify an id", function(assert) { + const adapter = this.owner.lookup('adapter:gateway'); + const client = this.owner.lookup('service:client/http'); + assert.throws(function() { + adapter.requestForQueryRecord(client.url, { + dc: dc, + }); + }); + }); +}); diff --git a/ui-v2/tests/integration/serializers/gateway-test.js b/ui-v2/tests/integration/serializers/gateway-test.js new file mode 100644 index 000000000..837c09aa7 --- /dev/null +++ b/ui-v2/tests/integration/serializers/gateway-test.js @@ -0,0 +1,47 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +import { get } from 'consul-ui/tests/helpers/api'; +import { + HEADERS_SYMBOL as META, + HEADERS_DATACENTER as DC, + HEADERS_NAMESPACE as NSPACE, +} from 'consul-ui/utils/http/consul'; + +module('Integration | Serializer | gateway', function(hooks) { + setupTest(hooks); + test('respondForQueryRecord returns the correct data for item endpoint', function(assert) { + const serializer = this.owner.lookup('serializer:gateway'); + const dc = 'dc-1'; + const id = 'slug'; + const nspace = 'default'; + const request = { + url: `/v1/internal/ui/gateway-services-nodes/${id}?dc=${dc}`, + }; + return get(request.url).then(function(payload) { + const expected = { + Datacenter: dc, + [META]: { + [DC.toLowerCase()]: dc, + [NSPACE.toLowerCase()]: nspace, + }, + uid: `["${nspace}","${dc}","${id}"]`, + Name: id, + Namespace: nspace, + Services: payload, + }; + const actual = serializer.respondForQueryRecord( + function(cb) { + const headers = {}; + const body = payload; + return cb(headers, body); + }, + { + dc: dc, + id: id, + } + ); + assert.deepEqual(actual, expected); + }); + }); +}); diff --git a/ui-v2/tests/integration/services/repository/gateway-test.js b/ui-v2/tests/integration/services/repository/gateway-test.js new file mode 100644 index 000000000..c43b2f7e4 --- /dev/null +++ b/ui-v2/tests/integration/services/repository/gateway-test.js @@ -0,0 +1,42 @@ +import { moduleFor, test } from 'ember-qunit'; +import repo from 'consul-ui/tests/helpers/repo'; + +moduleFor('service:repository/gateway', 'Integration | Repository | gateway', { + // Specify the other units that are required for this test. + integration: true, +}); +const dc = 'dc-1'; +const id = 'slug'; +const nspace = 'default'; +test('findBySlug returns the correct data for item endpoint', function(assert) { + return repo( + 'Gateway', + 'findBySlug', + this.subject(), + function retrieveStub(stub) { + return stub(`/v1/internal/ui/gateway-services-nodes/${id}`); + }, + function performTest(service) { + return service.findBySlug(id, dc); + }, + function performAssertion(actual, expected) { + assert.deepEqual( + actual, + expected(function(payload) { + return Object.assign( + {}, + { + Datacenter: dc, + Name: id, + Namespace: nspace, + uid: `["${nspace}","${dc}","${id}"]`, + }, + { + Services: payload, + } + ); + }) + ); + } + ); +}); diff --git a/ui-v2/tests/pages/dc/services/show.js b/ui-v2/tests/pages/dc/services/show.js index e5fa7bb6d..c9fd7bc30 100644 --- a/ui-v2/tests/pages/dc/services/show.js +++ b/ui-v2/tests/pages/dc/services/show.js @@ -7,7 +7,14 @@ export default function(visitable, attribute, collection, text, intentions, filt dashboardAnchor: { href: attribute('href', '[data-test-dashboard-anchor]'), }, - tabs: tabs('tab', ['instances', 'intentions', 'routing', 'tags']), + tabs: tabs('tab', [ + 'instances', + 'linked-services', + 'upstreams', + 'intentions', + 'routing', + 'tags', + ]), filter: filter, // TODO: These need to somehow move to subpages @@ -15,5 +22,8 @@ export default function(visitable, attribute, collection, text, intentions, filt address: text('[data-test-address]'), }), intentions: intentions(), + services: collection('.consul-gateway-services-list> ul > li:not(:first-child)', { + name: text('[data-test-service-name]'), + }), }; } diff --git a/ui-v2/tests/unit/adapters/gateway-test.js b/ui-v2/tests/unit/adapters/gateway-test.js new file mode 100644 index 000000000..e131d648b --- /dev/null +++ b/ui-v2/tests/unit/adapters/gateway-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Adapter | gateway', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let adapter = this.owner.lookup('adapter:gateway'); + assert.ok(adapter); + }); +}); diff --git a/ui-v2/tests/unit/models/gateway-test.js b/ui-v2/tests/unit/models/gateway-test.js new file mode 100644 index 000000000..d86be7ec1 --- /dev/null +++ b/ui-v2/tests/unit/models/gateway-test.js @@ -0,0 +1,13 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Model | gateway', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let store = this.owner.lookup('service:store'); + let model = store.createRecord('gateway', {}); + assert.ok(model); + }); +}); diff --git a/ui-v2/tests/unit/serializers/gateway-test.js b/ui-v2/tests/unit/serializers/gateway-test.js new file mode 100644 index 000000000..fc0a1c80e --- /dev/null +++ b/ui-v2/tests/unit/serializers/gateway-test.js @@ -0,0 +1,23 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Serializer | gateway', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let store = this.owner.lookup('service:store'); + let serializer = store.serializerFor('gateway'); + + assert.ok(serializer); + }); + + test('it serializes records', function(assert) { + let store = this.owner.lookup('service:store'); + let record = store.createRecord('gateway', {}); + + let serializedRecord = record.serialize(); + + assert.ok(serializedRecord); + }); +}); diff --git a/ui-v2/tests/unit/services/repository/gateway-test.js b/ui-v2/tests/unit/services/repository/gateway-test.js new file mode 100644 index 000000000..ca2989eb3 --- /dev/null +++ b/ui-v2/tests/unit/services/repository/gateway-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Repository | gateway', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + const repo = this.owner.lookup('service:repository/gateway'); + assert.ok(repo); + }); +}); diff --git a/ui-v2/yarn.lock b/ui-v2/yarn.lock index eb4a17664..fe780fc5f 100644 --- a/ui-v2/yarn.lock +++ b/ui-v2/yarn.lock @@ -1211,9 +1211,9 @@ js-yaml "^3.13.1" "@hashicorp/consul-api-double@^2.6.2": - version "2.14.7" - resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-2.14.7.tgz#80cd19461a3f3716bf76ba28bcabff842bcd9aef" - integrity sha512-QjpwvrraUswn/hFh+9lIKuA9keCGOkh1yr/cT3I6fDiw4JKLyDLaRN8bF/JNGtgoA/SsQh10L1YI3feZ7M3VKw== + version "2.15.1" + resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-2.15.1.tgz#1b41c92ee7930e0bcead8283eea019b5f1238819" + integrity sha512-0q0h2krXFR5uj/A+x5WtsKVF1ltPPDrrxmX9g+SjUmeWHIcffH7qz/PCo4fdqWOPjcTXkPfBxSZwGd2uDishaQ== "@hashicorp/ember-cli-api-double@^3.0.2": version "3.0.2"