Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
b3f8d33
chore: 🤖 migrate away from component helper for target form
hashicc Nov 20, 2025
e4fcde1
chore: 🤖 migrate away from component helper for managed group
hashicc Nov 20, 2025
40ace73
chore: 🤖 migrate away from component helper for host set form
hashicc Nov 20, 2025
2e6a049
chore: 🤖 migrate component helper usage for storage bucket form
hashicc Nov 20, 2025
ce51d78
chore: 🤖 convert rose form to embroider safe component helper
hashicc Nov 20, 2025
e6367f9
chore: 🤖 bump ember-can
hashicc Nov 25, 2025
447611e
chore: 🤖 import explicit mirage references
hashicc Apr 28, 2025
48c4f3d
chore: 🤖 add mirage setupMirage helper
hashicc Apr 28, 2025
f067660
chore: 🤖 working example with application adapter test
hashicc Apr 28, 2025
f63822c
chore: 🤖 use internal setupMirage test helper from api addon
hashicc Nov 14, 2025
3d77203
chore: 🤖 conditionally load mirage
hashicc Nov 14, 2025
50cfcff
chore: 🤖 filter mirage folder inclusion in production builds
hashicc Nov 14, 2025
b397c8d
chore: 🤖 drop ember-mirage and use our own test helpers
hashicc Nov 20, 2025
261c54f
chore: 🤖 collapse reference to import declaration
hashicc Nov 20, 2025
65ba285
docs: ✏️ add comment better explaining the mirage test env
hashicc Nov 20, 2025
242e4c1
chore: 🤖 update configuration and manage mirage-related deps
hashicc Nov 21, 2025
7dae7f6
fix: 🐛 restore ipc mock and add missing checkOS handler
hashicc Nov 21, 2025
93088ef
chore: 🤖 add comment explaining omitting mirage deps in build
hashicc Nov 21, 2025
3a761c2
chore: 🤖 allow mirage.enabled to include deps and handlers
hashicc Nov 24, 2025
4d00c7c
chore: 🤖 re-enable mirage for core dummy app
hashicc Nov 24, 2025
5b89f08
Add missing copyright headers
hashicc Nov 24, 2025
f702feb
chore: 🤖 use embroider macros to conditionally start mirage
hashicc Nov 24, 2025
69582d4
docs: ✏️ update api readme for mirage changes
hashicc Nov 24, 2025
1dde116
chore: 🤖 replace remaining ember-cli-mirage references
hashicc Nov 24, 2025
0ac968f
chore: 🤖 add mirage to test builds
hashicc Nov 24, 2025
138fc2b
fix: 🐛 ensure that auto mirage start doesn't happen in test env
hashicc Nov 24, 2025
a0ff110
chore: 🤖 add ember-inflector
hashicc Nov 25, 2025
0f2a401
chore: 🤖 add missing dependency on ember-inflector
hashicc Nov 25, 2025
3ccfd55
chore: 🤖 rename "can" service (deprecated) to abilities
hashicc Nov 25, 2025
dbf3adf
chore: 🤖 add missing dependency on ember-inflector
hashicc Nov 25, 2025
9d1be27
chore: 🤖 run codemode to vite
hashicc Nov 25, 2025
bafe813
Merge branch 'migrate-component-helper-target-form' into test-api-v2
hashicc Nov 25, 2025
7a8f7a7
Merge branch 'migrate-component-helper-storage-bucket-form' into test…
hashicc Nov 25, 2025
5dd564b
Merge branch 'migrate-component-helper-managed-group-form' into test-…
hashicc Nov 25, 2025
2c28e61
Merge branch 'migrate-component-helper-host-set-form' into test-api-v2
hashicc Nov 25, 2025
b92cce1
Merge branch 'make-rose-form-embroider-safe' into test-api-v2
hashicc Nov 25, 2025
79c96d1
chore: 🤖 hacky work to get vite working for admin
hashicc Nov 25, 2025
9febf69
chore: 🤖 ember-engines
hashicc Nov 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
38 changes: 34 additions & 4 deletions addons/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,45 @@ This addon contains the API data access layer for Boundary.
Add this addon to an Ember application's `devDependencies` as:
`"api": "workspace:*"`, for applications included in this monorepo.

Since this addon also includes Mirage mocks, be sure to install
`ember-cli-mirage` and add the following config to your UI project:
This addon also includes Mirage mocks. To include the `miragejs` dependency and this addon's mirage handlers configure the consuming app's `config/environment.js`

```js
'ember-cli-mirage': {
directory: '../../addons/api/mirage'
mirage: {
enabled: true
}
```

To have mirage start and intercept requests when the application starts:
1. Add the `@embroider/macros` dependency to your application
2. Configure `@embroider/macros` with `startMirageWithApp` based on app's config within `ember-cli-build.js`:

```js
// ember-cli-build.js
module.exports = async function (defaults) {
// load the app's config
const { EMBER_ENV } = process.env;
var config = require('./config/environment')(EMBER_ENV);

const app = new EmberApp(defaults, {
'@embroider/macros': {
setOwnConfig: {
startMirageWithApp: config.mirage?.enabled ?? false
},
},
});
}
```

3. Finally, use the `@embroider/macros` config value for `startMirageWithApp` in `app/app.js` to conditionally start mirage:

```js
import { macroCondition, importSync, getOwnConfig, isTesting } from '@embroider/macros';

if (macroCondition(getOwnConfig().startMirageWithApp && !isTesting())) {
const startServer = importSync('api/mirage/config').default;
startServer({});
}
```
## Installation

See monorepo README for installation instructions.
Expand Down
33 changes: 33 additions & 0 deletions addons/api/addon-test-support/helpers/mirage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/

import { settled } from '@ember/test-helpers';
import startMirage from 'api/mirage/config';

export function setupMirage(hooks) {
hooks.beforeEach(function () {
if (!this.owner) {
throw new Error(
'Must call one of the ember-qunit setupTest() / setupRenderingTest() / setupApplicationTest() first',
);
}

// the environment property here is configuration to the mirage server:
// https://github.com/miragejs/miragejs/blob/7ff4f3f6fe56bf0cb1648f5af3f5210fcb07e20b/types/index.d.ts#L383
// It is not related to ember's build environment. In this case for mirage the "test" environment does
// not load the default scenario:
// https://github.com/miragejs/miragejs/blob/7ff4f3f6fe56bf0cb1648f5af3f5210fcb07e20b/lib/server.js#L302
this.server = startMirage({ environment: 'test' });
});

hooks.afterEach(function () {
return settled().then(() => {
if (this.server) {
this.server.shutdown();
delete this.server;
}
});
});
}
4 changes: 2 additions & 2 deletions addons/api/addon/abilities/role.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class InvalidRolePrincipalTypeError extends Error {
export default class RoleAbility extends ModelAbility {
// =services

@service can;
@service abilities;

// =permissions

Expand Down Expand Up @@ -64,6 +64,6 @@ export default class RoleAbility extends ModelAbility {
`Expected a role principal of type 'user', 'group', or 'managed-group'. Got '${type}'.`,
);
}
return this.can.can(`read ${type}`, this.model);
return this.abilities.can(`read ${type}`, this.model);
}
}
176 changes: 165 additions & 11 deletions addons/api/mirage/config.js → addons/api/addon/mirage/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@
* SPDX-License-Identifier: BUSL-1.1
*/

import {
discoverEmberDataModels,
applyEmberDataSerializers,
} from 'ember-cli-mirage';
import { createServer, Response } from 'miragejs';
import environmentConfig from '../config/environment';
import { authHandler, deauthHandler } from './route-handlers/auth';
import { targetHandler } from './route-handlers/target';
import { pickRandomStatusString } from './factories/session';
Expand All @@ -18,18 +13,176 @@ import { faker } from '@faker-js/faker';
import { asciicasts } from './data/asciicasts';
import { TYPE_WORKER_PKI } from 'api/models/worker';

const isTesting = environmentConfig.environment === 'test';
// mirage models (alphabetical)
import accountModel from './models/account';
import aliasModel from './models/alias';
import authMethodModel from './models/auth-method';
import baseModel from './models/base';
import channelRecordingModel from './models/channel-recording';
import connectionRecordingModel from './models/connection-recording';
import credentialLibraryModel from './models/credential-library';
import credentialStoreModel from './models/credential-store';
import credentialModel from './models/credential';
import groupModel from './models/group';
import hostCatalogModel from './models/host-catalog';
import hostSetModel from './models/host-set';
import hostModel from './models/host';
import managedGroupModel from './models/managed-group';
import policyModel from './models/policy';
import roleModel from './models/role';
import scopeModel from './models/scope';
import sessionRecordingModel from './models/session-recording';
import sessionModel from './models/session';
import storageBucketModel from './models/storage-bucket';
import targetModel from './models/target';
import userModel from './models/user';
import workerModel from './models/worker';

// mirage serializers (alphabetical)
import accountSerializer from './serializers/account';
import aliasSerializer from './serializers/alias';
import applicationSerializer from './serializers/application';
import authMethodSerializer from './serializers/auth-method';
import channelRecordingSerializer from './serializers/channel-recording';
import connectionRecordingSerializer from './serializers/connection-recording';
import credentialLibrarySerializer from './serializers/credential-library';
import credentialStoreSerializer from './serializers/credential-store';
import credentialSerializer from './serializers/credential';
import groupSerializer from './serializers/group';
import hostCatalogSerializer from './serializers/host-catalog';
import hostSetSerializer from './serializers/host-set';
import hostSerializer from './serializers/host';
import managedGroupSerializer from './serializers/managed-group';
import policySerializer from './serializers/policy';
import roleSerializer from './serializers/role';
import scopeSerializer from './serializers/scope';
import sessionRecordingSerializer from './serializers/session-recording';
import sessionSerializer from './serializers/session';
import storageBucketSerializer from './serializers/storage-bucket';
import targetSerializer from './serializers/target';
import userSerializer from './serializers/user';
import workerSerializer from './serializers/worker';

// mirage scenarios (alphabetical)
import defaultScenario from './scenarios/default';
import ipcScenario from './scenarios/ipc';

// mirage factories (alphabetical)
import accountFactory from './factories/account';
import aliasFactory from './factories/alias';
import authMethodFactory from './factories/auth-method';
import channelRecordingFactory from './factories/channel-recording';
import connectionRecordingFactory from './factories/connection-recording';
import credentialLibraryFactory from './factories/credential-library';
import credentialStoreFactory from './factories/credential-store';
import credentialFactory from './factories/credential';
import groupFactory from './factories/group';
import hostCatalogFactory from './factories/host-catalog';
import hostSetFactory from './factories/host-set';
import hostFactory from './factories/host';
import managedGroupFactory from './factories/managed-group';
import policyFactory from './factories/policy';
import roleFactory from './factories/role';
import scopeFactory from './factories/scope';
import sessionRecordingFactory from './factories/session-recording';
import storageBucketFactory from './factories/storage-bucket';
import sessionFactory from './factories/session';
import targetFactory from './factories/target';
import userFactory from './factories/user';
import workerFactory from './factories/worker';

const environmentConfig = {};
const isTesting = false; // environmentConfig.environment === 'test';

// Main function
// More info about server configuration https://www.ember-cli-mirage.com/docs/advanced/server-configuration
// More info about server configuration:
// https://github.com/miragejs/miragejs/blob/7ff4f3f6fe56bf0cb1648f5af3f5210fcb07e20b/types/index.d.ts#L375-L404
export default function (mirageConfig) {
let finalConfig = {
...mirageConfig,

scenarios: {
default: defaultScenario,
ipcScenario: ipcScenario,
},

factories: {
account: accountFactory,
alias: aliasFactory,
authMethod: authMethodFactory,
channelRecording: channelRecordingFactory,
connectionRecording: connectionRecordingFactory,
credentialLibrary: credentialLibraryFactory,
credentialStore: credentialStoreFactory,
credential: credentialFactory,
group: groupFactory,
hostCatalog: hostCatalogFactory,
hostSet: hostSetFactory,
host: hostFactory,
managedGroup: managedGroupFactory,
policy: policyFactory,
role: roleFactory,
scope: scopeFactory,
sessionRecording: sessionRecordingFactory,
session: sessionFactory,
storageBucket: storageBucketFactory,
target: targetFactory,
user: userFactory,
worker: workerFactory,
},

models: {
...discoverEmberDataModels(mirageConfig.store),
...mirageConfig.models,
account: accountModel,
alias: aliasModel,
authMethod: authMethodModel,
base: baseModel,
channelRecording: channelRecordingModel,
connectionRecording: connectionRecordingModel,
credentialLibrary: credentialLibraryModel,
credentialStore: credentialStoreModel,
credential: credentialModel,
group: groupModel,
hostCatalog: hostCatalogModel,
hostSet: hostSetModel,
host: hostModel,
managedGroup: managedGroupModel,
policy: policyModel,
role: roleModel,
scope: scopeModel,
sessionRecording: sessionRecordingModel,
session: sessionModel,
storageBucket: storageBucketModel,
target: targetModel,
user: userModel,
worker: workerModel,
},

serializers: {
account: accountSerializer,
alias: aliasSerializer,
application: applicationSerializer,
authMethod: authMethodSerializer,
channelRecording: channelRecordingSerializer,
connectionRecording: connectionRecordingSerializer,
credentialLibrary: credentialLibrarySerializer,
credentialStore: credentialStoreSerializer,
credential: credentialSerializer,
group: groupSerializer,
hostCatalog: hostCatalogSerializer,
hostSet: hostSetSerializer,
host: hostSerializer,
managedGroup: managedGroupSerializer,
policy: policySerializer,
role: roleSerializer,
scope: scopeSerializer,
sessionRecording: sessionRecordingSerializer,
session: sessionSerializer,
storageBucket: storageBucketSerializer,
target: targetSerializer,
user: userSerializer,
worker: workerSerializer,
},
serializers: applyEmberDataSerializers(mirageConfig.serializers),

routes,
};
return createServer(finalConfig);
Expand Down Expand Up @@ -57,8 +210,9 @@ function routes() {
return metadata;
});

debugger;
// make this `/api`, for example, if your API is namespaced
this.namespace = environmentConfig.api.namespace;
this.namespace = '/v1';
// delay for each request, automatically set to 0 during testing
this.timing = 1;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ export default function initializeMockIPC(server, config) {

clusterUrl = null;

checkOS() {
return {
isLinux: false,
isMac: true,
isWindows: false,
};
}

getClusterUrl() {
return this.clusterUrl;
}
Expand Down Expand Up @@ -273,7 +281,7 @@ export default function initializeMockIPC(server, config) {
* We mock certain functions even in electron (e.g. hasMacOSChrome) when running
* locally which will force a certain appearance regardless of platform
*/
if (config['ember-cli-mirage'].enabled && !isTesting) {
if (config.mirage?.enabled && !isTesting) {
const mockIPC = new MockIPC();

window.addEventListener('message', async function (event) {
Expand Down
32 changes: 31 additions & 1 deletion addons/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,36 @@ module.exports = {
},
},

included() {
this._super.included.apply(this, arguments);
const includeMirage = this._includeMirageInBuild();

// these are dependencies used our mirage code within the
// api addon and should not be included in production builds,
// after this addon has been migrated to a v2 addon this can
// be removed as the addon's dependencies will be statically
// analyzable
if (!includeMirage) {
// exclude mirage and dependencies from this addon that are used by mirage
this.options.autoImport.exclude.push(
'miragejs',
'sinon',
'@faker-js/faker',
'js-bexpr',
);
}
},

treeForAddon() {
const includeMirage = this._includeMirageInBuild();

// Exclude anything in the workers folder from being bundled in the final
// build as we're manually bundling the files ourselves below.
const tree = this._super.treeForAddon.apply(this, arguments);
return funnel(tree, {
exclude: ['api/workers/**/*'],
exclude: ['api/workers/**/*', !includeMirage && 'api/mirage/**/*'].filter(
Boolean,
),
});
},

Expand Down Expand Up @@ -70,4 +94,10 @@ module.exports = {

return merge(trees);
},

_includeMirageInBuild() {
const env = this.parent.app?.env ?? 'production';
const config = this.project.config(env);
return Boolean(config.mirage?.enabled);
},
};
Loading