diff --git a/README.md b/README.md index 4c200c5e6..e3951ba65 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ subproject, e.g. running or testing. - [Branching](#branching) - [Building ToC](#building-toc) - [Building Core UI for Production](#building-core-ui-for-production) + - [Building with a Container](#building-with-a-container) - [Committing](#committing) - [License Checking](#license-checking) @@ -111,7 +112,18 @@ To quickly produce a production build of Core UI, run from this folder: ```bash yarn install -yarn build:production:core +yarn build:production:ui:core +``` + +Assets are saved to `ui/core/dist/`. + +#### Building with a Container + +Optionally, you may build the UI within a container. Execute the +following command: + +```bash +./build_production.sh ``` Assets are saved to `ui/core/dist/`. diff --git a/addons/api/addon/adapters/application.js b/addons/api/addon/adapters/application.js index cef50d39e..182fa3493 100644 --- a/addons/api/addon/adapters/application.js +++ b/addons/api/addon/adapters/application.js @@ -6,6 +6,36 @@ import { dasherize } from '@ember/string'; import { pluralize } from 'ember-inflector'; import { isArray } from '@ember/array'; +/** + * Returns true if the payload is an empty object, false otherwise. + * Taken from Ember Data fetch-manager and "positivized" the name. + * @param {object} adapterPayload + * @return {boolean} + */ +function payloadIsBlank(adapterPayload) { + if (Array.isArray(adapterPayload)) { + return false; + } else { + return Object.keys(adapterPayload || {}).length === 0; + } +} + +/** + * If the resonse is empty, returns a new payload containing an empty `items` + * array, as expected by the serializer. Otherwise, returns the response. + * + * Normally this would be performed 100% inside the serializer. + * However, the fetch manager, which we cannot easily override, glues the + * adapter and serializers together. Before it forwards a response from the + * adapter on to the serializer, it checks for an "empty" response. If the + * response is empty, fetch manager throws and error and never forwards it on. + * @param {object} response + * @return {object} + */ +function prenormalizeArrayResponse(response) { + return payloadIsBlank(response) ? {items: []} : response; +} + export default class ApplicationAdapter extends RESTAdapter { // =attributes @@ -36,6 +66,26 @@ export default class ApplicationAdapter extends RESTAdapter { return dasherize(pluralize(type)); } + /** + * Intercepts "empty" responses and adds an empty `items` array. + * @override + * @method findAll + * @return {Promise} promise + */ + findAll() { + return super.findAll(...arguments).then(prenormalizeArrayResponse); + } + + /** + * Intercepts "empty" responses and adds an empty `items` array. + * @override + * @method query + * @return {Promise} promise + */ + query() { + return super.query(...arguments).then(prenormalizeArrayResponse); + } + /** * Overrides default ajax method by rewritting PUT requests as PATCH. * PATCH is the request method used by our API for updates. diff --git a/addons/api/package.json b/addons/api/package.json index 151c4959b..f9a6af815 100644 --- a/addons/api/package.json +++ b/addons/api/package.json @@ -35,6 +35,7 @@ "ember-cli-dependency-checker": "^3.2.0", "ember-cli-eslint": "^5.1.0", "ember-cli-inject-live-reload": "^2.0.2", + "ember-cli-mirage": "^1.1.7", "ember-cli-sri": "^2.1.1", "ember-cli-template-lint": "^1.0.0-beta.3", "ember-cli-uglify": "^3.0.0", diff --git a/addons/api/tests/dummy/mirage/config.js b/addons/api/tests/dummy/mirage/config.js new file mode 100644 index 000000000..c2cd26a9d --- /dev/null +++ b/addons/api/tests/dummy/mirage/config.js @@ -0,0 +1,26 @@ +export default function() { + + // These comments are here to help you get started. Feel free to delete them. + + /* + Config (with defaults). + + Note: these only affect routes defined *after* them! + */ + + // this.urlPrefix = ''; // make this `http://localhost:8080`, for example, if your API is on a different server + // this.namespace = ''; // make this `/api`, for example, if your API is namespaced + // this.timing = 400; // delay for each request, automatically set to 0 during testing + + /* + Shorthand cheatsheet: + + this.get('/posts'); + this.post('/posts'); + this.get('/posts/:id'); + this.put('/posts/:id'); // or this.patch + this.del('/posts/:id'); + + https://www.ember-cli-mirage.com/docs/route-handlers/shorthands + */ +} diff --git a/addons/api/tests/dummy/mirage/scenarios/default.js b/addons/api/tests/dummy/mirage/scenarios/default.js new file mode 100644 index 000000000..adb3af4c0 --- /dev/null +++ b/addons/api/tests/dummy/mirage/scenarios/default.js @@ -0,0 +1,9 @@ +export default function(/* server */) { + + /* + Seed your development database using your factories. + This data will not be loaded in your tests. + */ + + // server.createList('post', 10); +} diff --git a/addons/api/tests/dummy/mirage/serializers/application.js b/addons/api/tests/dummy/mirage/serializers/application.js new file mode 100644 index 000000000..6d47a3669 --- /dev/null +++ b/addons/api/tests/dummy/mirage/serializers/application.js @@ -0,0 +1,4 @@ +import { JSONAPISerializer } from 'ember-cli-mirage'; + +export default JSONAPISerializer.extend({ +}); diff --git a/addons/api/tests/unit/adapters/application-test.js b/addons/api/tests/unit/adapters/application-test.js index 8c58bafec..366c44c4e 100644 --- a/addons/api/tests/unit/adapters/application-test.js +++ b/addons/api/tests/unit/adapters/application-test.js @@ -3,9 +3,12 @@ import { setupTest } from 'ember-qunit'; import config from 'ember-get-config'; import RESTAdapter from '@ember-data/adapter/rest'; import { InvalidError } from '@ember-data/adapter/error'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import { Response } from 'miragejs'; module('Unit | Adapter | application', function (hooks) { setupTest(hooks); + setupMirage(hooks); test('its namespace is equal to the configured namespace', function (assert) { assert.expect(2); @@ -24,12 +27,23 @@ module('Unit | Adapter | application', function (hooks) { assert.expect(1); const adapter = this.owner.lookup('adapter:application'); // TODO this is icky, should be changed to a spy or stub + const originalAjax = RESTAdapter.prototype.ajax; RESTAdapter.prototype.ajax = (url, type) => { assert.equal(type, 'PATCH'); + RESTAdapter.prototype.ajax = originalAjax; }; adapter.ajax('/', 'PUT'); }); + test('it prenormalizes "empty" responses into a form that the fetch-manager will not reject', async function (assert) { + assert.expect(1); + const adapter = this.owner.lookup('adapter:project'); + const store = this.owner.lookup('service:store'); + this.server.get('/v1/projects', () => new Response({})); + const prenormalized = await adapter.findAll(store, {modelName: 'project'}, null, []); + assert.deepEqual(prenormalized, {items: []}); + }); + test('it correctly identifies 400 responses as invalid', function (assert) { assert.expect(2); const adapter = this.owner.lookup('adapter:application'); diff --git a/docker-compose-embedding.yml b/docker-compose-embedding.yml new file mode 100644 index 000000000..1cd3d329b --- /dev/null +++ b/docker-compose-embedding.yml @@ -0,0 +1,29 @@ +# Note: this will change the permissions of the files in this directory after +# running to allow for easy deletion, so this should only be run as part of +# watchtower's build task. + +version: '3.8' + +services: + node: &node + image: node:12 + volumes: + - './:/src' + - 'icu_node_modules:/src/node_modules' + - 'icu_addons_api_node_modules:/src/addons/api/node_modules' + - 'icu_addons_auth_node_modules:/src/addons/auth/node_modules' + - 'icu_addons_rose_node_modules:/src/addons/rose/node_modules' + - 'icu_ui_core_node_modules:/src/ui/core/node_modules' + working_dir: /src + + build_production: + <<: *node + entrypoint: bash + command: '-c "yarn install && yarn build:production:ui:core && chmod -R 777 ."' + +volumes: + icu_node_modules: + icu_addons_api_node_modules: + icu_addons_auth_node_modules: + icu_addons_rose_node_modules: + icu_ui_core_node_modules: diff --git a/package.json b/package.json index 17d23d120..8e18bfe44 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "ui/*" ], "scripts": { - "build:production:core": "cd ui/core && yarn build:production", + "build:production:ui:core": "cd ui/core && yarn build:production", "lint": "yarn lint:addons:api && yarn lint:addons:auth && yarn lint:addons:rose && yarn lint:ui:core", "lint:addons:api": "echo \"\n\nlint: addons/api\n\" && cd addons/api && yarn lint", "lint:addons:auth": "echo \"\n\nlint: addons/auth\n\" && cd addons/auth && yarn lint", diff --git a/ui/core/app/templates/orgs/org/roles/index.hbs b/ui/core/app/templates/orgs/org/roles/index.hbs index 498aecac5..07e3acfaa 100644 --- a/ui/core/app/templates/orgs/org/roles/index.hbs +++ b/ui/core/app/templates/orgs/org/roles/index.hbs @@ -33,7 +33,7 @@ @route="orgs.org.roles.role" @model={{role}} > - {{role.name}} + {{role.displayName}} {{role.description}} diff --git a/yarn.lock b/yarn.lock index 90f435da4..214eb95d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7330,6 +7330,21 @@ ember-cli-mirage@^1.1.6: lodash-es "^4.17.11" miragejs "^0.1.31" +ember-cli-mirage@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/ember-cli-mirage/-/ember-cli-mirage-1.1.7.tgz#846ec8e5199b15fa670da30dfa1708a8ee11e9dc" + integrity sha512-4kVLXXvgHYlv30ChM47U4xYnUtphIoEj+BQwXX0QT7pvD3KQxYnmfZSwlevSEVUy2UhbsBUPw6merENb+dIQ0A== + dependencies: + broccoli-file-creator "^2.1.1" + broccoli-funnel "^3.0.2" + broccoli-merge-trees "^4.2.0" + ember-auto-import "^1.2.19" + ember-cli-babel "^7.5.0" + ember-get-config "^0.2.2" + ember-inflector "^2.0.0 || ^3.0.0" + lodash-es "^4.17.11" + miragejs "^0.1.31" + ember-cli-normalize-entity-name@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/ember-cli-normalize-entity-name/-/ember-cli-normalize-entity-name-1.0.0.tgz#0b14f7bcbc599aa117b5fddc81e4fd03c4bad5b7"