@fleetbase/ember-core 0.2.13 → 0.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,25 @@
1
+ import { decoratorWithRequiredParams } from '@ember-decorators/utils/decorator';
2
+ import { computed } from '@ember/object';
3
+ import { assert } from '@ember/debug';
4
+ import injectEngineService from '../utils/inject-engine-service';
5
+ import isObject from '../utils/is-object';
6
+
7
+ export default decoratorWithRequiredParams(function (target, key, descriptor, [engineName, options = {}]) {
8
+ assert('The first argument of the @engineService decorator must be a string', typeof engineName === 'string');
9
+ assert('The second argument of the @engineService decorator must be an object', isObject(options));
10
+
11
+ const { initializer } = descriptor;
12
+ delete descriptor.initializer;
13
+
14
+ const cp = computed(`_engineService_${key}`, function () {
15
+ const service = injectEngineService(this, engineName, key, options);
16
+
17
+ if (initializer) {
18
+ return initializer.call(this);
19
+ }
20
+
21
+ return service;
22
+ });
23
+
24
+ return cp(target, key, descriptor);
25
+ });
@@ -1,55 +1,52 @@
1
1
  import { decoratorWithRequiredParams } from '@ember-decorators/utils/decorator';
2
- import { assert } from '@ember/debug';
3
2
  import { getOwner } from '@ember/application';
4
- import { scheduleOnce } from '@ember/runloop';
3
+ import { assert } from '@ember/debug';
5
4
 
6
- export default function fetchFrom(endpoint, query = {}, options = {}) {
5
+ export default decoratorWithRequiredParams(function (target, key, descriptor, [endpoint, query = {}, options = {}]) {
7
6
  assert('The first argument of the @fetchFrom decorator must be a string', typeof endpoint === 'string');
8
7
  assert('The second argument of the @fetchFrom decorator must be an object', typeof query === 'object');
9
8
  assert('The third argument of the @fetchFrom decorator must be an object', typeof options === 'object');
10
9
 
11
- return decoratorWithRequiredParams(function (target, key) {
12
- const symbol = Symbol(`__${key}_fetchFrom`);
10
+ // Remove value and writable if previously set, use getter instead
11
+ delete descriptor.value;
12
+ delete descriptor.writable;
13
+ delete descriptor.initializer;
14
+
15
+ // Create symbol to track value
16
+ const symbol = Symbol(`__${key}_fetchFrom`);
17
+
18
+ // Setter to get symbol value
19
+ descriptor.set = function (value) {
20
+ this[symbol] = value;
21
+ };
22
+
23
+ // Get or set symbol value
24
+ descriptor.get = async function () {
25
+ if (this[symbol] !== undefined) {
26
+ return this[symbol];
27
+ }
13
28
 
14
- Object.defineProperty(target, symbol, {
29
+ Object.defineProperty(this, symbol, {
15
30
  configurable: true,
16
31
  enumerable: false,
17
32
  writable: true,
18
33
  value: null,
19
34
  });
20
35
 
21
- Object.defineProperty(target, key, {
22
- configurable: true,
23
- enumerable: true,
24
- get() {
25
- return this[symbol];
26
- },
27
- set(value) {
28
- this[symbol] = value;
29
- },
30
- });
31
-
32
- const originalInit = target.init;
33
-
34
- target.init = function () {
35
- if (originalInit) {
36
- originalInit.call(this);
37
- }
38
-
39
- scheduleOnce('afterRender', this, function () {
40
- const owner = getOwner(this);
41
- const fetch = owner.lookup('service:fetch'); // Get the Fleetbase Fetch service
42
-
43
- // Perform the query and set the result to the property
44
- fetch
45
- .get(endpoint, query, options)
46
- .then((result) => {
47
- this.set(key, result);
48
- })
49
- .catch(() => {
50
- this.set(key, []);
51
- });
36
+ const owner = getOwner(this);
37
+ const fetch = owner.lookup('service:fetch');
38
+ return fetch
39
+ .get(endpoint, query, options)
40
+ .then((response) => {
41
+ this.set(key, response);
42
+ if (options && typeof options.onComplete === 'function') {
43
+ options.onComplete(response, this);
44
+ }
45
+ })
46
+ .catch(() => {
47
+ this.set(key, null);
52
48
  });
53
- };
54
- }, 'fetchFrom')(endpoint, query, options);
55
- }
49
+ };
50
+
51
+ return descriptor;
52
+ });
@@ -1,55 +1,52 @@
1
1
  import { decoratorWithRequiredParams } from '@ember-decorators/utils/decorator';
2
- import { assert } from '@ember/debug';
3
2
  import { getOwner } from '@ember/application';
4
- import { scheduleOnce } from '@ember/runloop';
3
+ import { assert } from '@ember/debug';
4
+
5
+ export default decoratorWithRequiredParams(function (target, key, descriptor, [modelName, query = {}, options = {}]) {
6
+ assert('The first argument of the @fetchFrom decorator must be a string', typeof modelName === 'string');
7
+ assert('The second argument of the @fetchFrom decorator must be an object', typeof query === 'object');
8
+ assert('The third argument of the @fetchFrom decorator must be an object', typeof options === 'object');
9
+
10
+ // Remove value and writable if previously set, use getter instead
11
+ delete descriptor.value;
12
+ delete descriptor.writable;
13
+ delete descriptor.initializer;
14
+
15
+ // Create symbol to track value
16
+ const symbol = Symbol(`__${key}_fromStore`);
5
17
 
6
- export default function fromStore(modelName, query = {}, options = {}) {
7
- assert('The first argument of the @fromStore decorator must be a string', typeof modelName === 'string');
8
- assert('The second argument of the @fromStore decorator must be an object', typeof query === 'object');
9
- assert('The third argument of the @fromStore decorator must be an object', typeof options === 'object');
18
+ // Setter to get symbol value
19
+ descriptor.set = function (value) {
20
+ this[symbol] = value;
21
+ };
10
22
 
11
- return decoratorWithRequiredParams(function (target, key) {
12
- const symbol = Symbol(`__${key}_fromStore`);
23
+ // Get or set symbol value
24
+ descriptor.get = function () {
25
+ if (this[symbol] !== undefined) {
26
+ return this[symbol];
27
+ }
13
28
 
14
- Object.defineProperty(target, symbol, {
29
+ Object.defineProperty(this, symbol, {
15
30
  configurable: true,
16
31
  enumerable: false,
17
32
  writable: true,
18
33
  value: null,
19
34
  });
20
35
 
21
- Object.defineProperty(target, key, {
22
- configurable: true,
23
- enumerable: true,
24
- get() {
25
- return this[symbol];
26
- },
27
- set(value) {
28
- this[symbol] = value;
29
- },
30
- });
31
-
32
- const originalInit = target.init;
33
-
34
- target.init = function () {
35
- if (originalInit) {
36
- originalInit.call(this);
37
- }
38
-
39
- scheduleOnce('afterRender', this, function () {
40
- const owner = getOwner(this);
41
- const store = owner.lookup('service:store'); // Get the Ember Data store
42
-
43
- // Perform the query and set the result to the property
44
- store
45
- .query(modelName, query, options)
46
- .then((result) => {
47
- this.set(key, result);
48
- })
49
- .catch(() => {
50
- this.set(key, []);
51
- });
36
+ const owner = getOwner(this);
37
+ const store = owner.lookup('service:store');
38
+ return store
39
+ .query(modelName, query, options)
40
+ .then((response) => {
41
+ this.set(key, response);
42
+ if (options && typeof options.onComplete === 'function') {
43
+ options.onComplete(response, this);
44
+ }
45
+ })
46
+ .catch(() => {
47
+ this.set(key, null);
52
48
  });
53
- };
54
- }, 'fromStore')(modelName, query, options);
55
- }
49
+ };
50
+
51
+ return descriptor;
52
+ });
@@ -13,6 +13,7 @@ import RSVP from 'rsvp';
13
13
  import loadInstalledExtensions from '../utils/load-installed-extensions';
14
14
  import loadExtensions from '../utils/load-extensions';
15
15
  import getWithDefault from '../utils/get-with-default';
16
+ import config from 'ember-get-config';
16
17
 
17
18
  export default class UniverseService extends Service.extend(Evented) {
18
19
  @service router;
@@ -1058,29 +1059,88 @@ export default class UniverseService extends Service.extend(Evented) {
1058
1059
  }
1059
1060
 
1060
1061
  /**
1061
- * Manually registers a component in a specified engine.
1062
+ * Registers a component class under one or more names within a specified engine instance.
1063
+ * This function provides flexibility in component registration by supporting registration under the component's
1064
+ * full class name, a simplified alias derived from the class name, and an optional custom name provided through the options.
1065
+ * This flexibility facilitates varied referencing styles within different parts of the application, enhancing modularity and reuse.
1062
1066
  *
1063
- * @method registerComponentInEngine
1064
- * @public
1065
- * @memberof UniverseService
1066
- * @param {String} engineName - The name of the engine where the component should be registered.
1067
- * @param {Object} componentClass - The component class to register, which should have a 'name' property.
1067
+ * @param {string} engineName - The name of the engine where the component will be registered.
1068
+ * @param {class} componentClass - The component class to be registered. Must be a class, not an instance.
1069
+ * @param {Object} [options] - Optional parameters for additional configuration.
1070
+ * @param {string} [options.registerAs] - A custom name under which the component can also be registered.
1071
+ *
1072
+ * @example
1073
+ * // Register a component with its default and alias names
1074
+ * registerComponentInEngine('mainEngine', HeaderComponent);
1075
+ *
1076
+ * // Additionally register the component under a custom name
1077
+ * registerComponentInEngine('mainEngine', HeaderComponent, { registerAs: 'header' });
1078
+ *
1079
+ * @remarks
1080
+ * - The function does not return any value.
1081
+ * - Registration only occurs if:
1082
+ * - The specified engine instance exists.
1083
+ * - The component class is properly defined with a non-empty name.
1084
+ * - The custom name, if provided, must be a valid string.
1085
+ * - Allows flexible component referencing by registering under multiple names.
1068
1086
  */
1069
- registerComponentInEngine(engineName, componentClass) {
1087
+ registerComponentInEngine(engineName, componentClass, options = {}) {
1070
1088
  const engineInstance = this.getEngineInstance(engineName);
1071
- if (engineInstance && !isBlank(componentClass) && typeof componentClass.name === 'string') {
1089
+ this.registerComponentToEngineInstance(engineInstance, componentClass, options);
1090
+ }
1091
+
1092
+ /**
1093
+ * Registers a component class under its full class name, a simplified alias, and an optional custom name within a specific engine instance.
1094
+ * This helper function does the actual registration of the component to the engine instance. It registers the component under its
1095
+ * full class name, a dasherized alias of the class name (with 'Component' suffix removed if present), and any custom name provided via options.
1096
+ *
1097
+ * @param {EngineInstance} engineInstance - The engine instance where the component will be registered.
1098
+ * @param {class} componentClass - The component class to be registered. This should be a class reference, not an instance.
1099
+ * @param {Object} [options] - Optional parameters for further configuration.
1100
+ * @param {string} [options.registerAs] - A custom name under which the component can be registered.
1101
+ *
1102
+ * @example
1103
+ * // Typical usage within the system (not usually called directly by users)
1104
+ * registerComponentToEngineInstance(engineInstance, HeaderComponent, { registerAs: 'header' });
1105
+ *
1106
+ * @remarks
1107
+ * - No return value.
1108
+ * - The registration is performed only if:
1109
+ * - The engine instance is valid and not null.
1110
+ * - The component class has a defined and non-empty name.
1111
+ * - The custom name, if provided, is a valid string.
1112
+ * - This function directly manipulates the engine instance's registration map.
1113
+ */
1114
+ registerComponentToEngineInstance(engineInstance, componentClass, options = {}) {
1115
+ if (engineInstance && componentClass && typeof componentClass.name === 'string') {
1072
1116
  engineInstance.register(`component:${componentClass.name}`, componentClass);
1117
+ engineInstance.register(`component:${dasherize(componentClass.name.replace('Component', ''))}`, componentClass);
1118
+ if (options && typeof options.registerAs === 'string') {
1119
+ engineInstance.register(`component:${options.registerAs}`, componentClass);
1120
+ }
1073
1121
  }
1074
1122
  }
1075
1123
 
1076
1124
  /**
1077
- * Manually registers a service in a specified engine.
1125
+ * Registers a service from one engine instance to another within the application.
1126
+ * This method retrieves an instance of a service from the current engine and then registers it
1127
+ * in a target engine, allowing the service to be shared across different parts of the application.
1078
1128
  *
1079
- * @method registerComponentInEngine
1080
- * @public
1081
- * @memberof UniverseService
1082
- * @param {String} engineName - The name of the engine where the component should be registered.
1083
- * @param {Object} serviceClass - The service class to register, which should have a 'name' property.
1129
+ * @param {string} targetEngineName - The name of the engine where the service should be registered.
1130
+ * @param {string} serviceName - The name of the service to be shared and registered.
1131
+ * @param {Object} currentEngineInstance - The engine instance that currently holds the service to be shared.
1132
+ *
1133
+ * @example
1134
+ * // Assuming 'appEngine' and 'componentEngine' are existing engine instances and 'logger' is a service in 'appEngine'
1135
+ * registerServiceInEngine('componentEngine', 'logger', appEngine);
1136
+ *
1137
+ * Note:
1138
+ * - This function does not return any value.
1139
+ * - It only performs registration if all provided parameters are valid:
1140
+ * - Both engine instances must exist.
1141
+ * - The service name must be a string.
1142
+ * - The service must exist in the current engine instance.
1143
+ * - The service is registered without instantiating a new copy in the target engine.
1084
1144
  */
1085
1145
  registerServiceInEngine(targetEngineName, serviceName, currentEngineInstance) {
1086
1146
  // Get the target engine instance
@@ -1111,11 +1171,16 @@ export default class UniverseService extends Service.extend(Evented) {
1111
1171
  * userService.doSomething();
1112
1172
  * }
1113
1173
  */
1114
- getServiceFromEngine(engineName, serviceName) {
1174
+ getServiceFromEngine(engineName, serviceName, options = {}) {
1115
1175
  const engineInstance = this.getEngineInstance(engineName);
1116
1176
 
1117
1177
  if (engineInstance && typeof serviceName === 'string') {
1118
1178
  const serviceInstance = engineInstance.lookup(`service:${serviceName}`);
1179
+ if (options && options.inject) {
1180
+ for (let injectionName in options.inject) {
1181
+ serviceInstance[injectionName] = options.inject[injectionName];
1182
+ }
1183
+ }
1119
1184
  return serviceInstance;
1120
1185
  }
1121
1186
 
@@ -1287,6 +1352,7 @@ export default class UniverseService extends Service.extend(Evented) {
1287
1352
  bootEngines(owner = null) {
1288
1353
  const booted = [];
1289
1354
  const pending = [];
1355
+ const additionalCoreExtensions = config.APP.extensions ?? [];
1290
1356
 
1291
1357
  // If no owner provided use the owner of this service
1292
1358
  if (owner === null) {
@@ -1296,9 +1362,11 @@ export default class UniverseService extends Service.extend(Evented) {
1296
1362
  const tryBootEngine = (extension) => {
1297
1363
  this.loadEngine(extension.name).then((engineInstance) => {
1298
1364
  if (engineInstance.base && engineInstance.base.setupExtension) {
1299
- const engineDependencies = getWithDefault(engineInstance.base, 'engineDependencies', []);
1365
+ if (booted.includes(extension.name)) {
1366
+ return;
1367
+ }
1300
1368
 
1301
- // Check if all dependency engines are booted
1369
+ const engineDependencies = getWithDefault(engineInstance.base, 'engineDependencies', []);
1302
1370
  const allDependenciesBooted = engineDependencies.every((dep) => booted.includes(dep));
1303
1371
 
1304
1372
  if (!allDependenciesBooted) {
@@ -1320,6 +1388,10 @@ export default class UniverseService extends Service.extend(Evented) {
1320
1388
  const stillPending = [];
1321
1389
 
1322
1390
  pending.forEach(({ extension, engineInstance }) => {
1391
+ if (booted.includes(extension.name)) {
1392
+ return;
1393
+ }
1394
+
1323
1395
  const engineDependencies = getWithDefault(engineInstance.base, 'engineDependencies', []);
1324
1396
  const allDependenciesBooted = engineDependencies.every((dep) => booted.includes(dep));
1325
1397
 
@@ -1333,13 +1405,13 @@ export default class UniverseService extends Service.extend(Evented) {
1333
1405
  });
1334
1406
 
1335
1407
  // If no progress was made, log an error in debug/development mode
1336
- assert('Some engines have unmet dependencies and cannot be booted:', pending.length === stillPending.length);
1408
+ assert(`Some engines have unmet dependencies and cannot be booted:`, stillPending.length === 0 && pending.length === 0);
1337
1409
 
1338
1410
  pending.length = 0;
1339
1411
  pending.push(...stillPending);
1340
1412
  };
1341
1413
 
1342
- loadInstalledExtensions().then((extensions) => {
1414
+ loadInstalledExtensions(additionalCoreExtensions).then((extensions) => {
1343
1415
  extensions.forEach((extension) => {
1344
1416
  tryBootEngine(extension);
1345
1417
  });
@@ -1,10 +1,49 @@
1
1
  import { getOwner } from '@ember/application';
2
+ import { isArray } from '@ember/array';
3
+ import isObject from './is-object';
2
4
 
3
- export default function injectEngineService(target, engineName, serviceName, key = null) {
5
+ function findService(owner, target, serviceName) {
6
+ let service = target[serviceName];
7
+ if (!service) {
8
+ service = owner.lookup(`service:${serviceName}`);
9
+ }
10
+
11
+ return service;
12
+ }
13
+
14
+ function injectServices(service, target, owner, injections) {
15
+ if (isArray(injections)) {
16
+ for (let i = 0; i < injections.length; i++) {
17
+ const serviceName = injections[i];
18
+ service[serviceName] = findService(owner, target, serviceName);
19
+ }
20
+ } else if (isObject(injections)) {
21
+ for (let serviceName in injections) {
22
+ service[serviceName] = injections[serviceName] ?? findService(owner, target, serviceName);
23
+ }
24
+ }
25
+ }
26
+
27
+ // unresolved services value will be the key as a string
28
+ function automaticServiceResolution(service, target, owner) {
29
+ for (let prop in service) {
30
+ if (typeof prop === 'string' && typeof service[prop] === 'string' && prop === service[prop]) {
31
+ service[prop] = findService(owner, target, prop);
32
+ }
33
+ }
34
+ }
35
+
36
+ export default function injectEngineService(target, engineName, serviceName, options = {}) {
4
37
  const owner = getOwner(target);
5
38
  const universe = owner.lookup('service:universe');
6
39
  const service = universe.getServiceFromEngine(engineName, serviceName);
40
+ const key = options.key || null;
7
41
  const effectiveServiceName = key || serviceName;
42
+ if (options && options.inject) {
43
+ injectServices(service, target, owner, options.inject);
44
+ } else {
45
+ automaticServiceResolution(service, target, owner);
46
+ }
8
47
 
9
48
  Object.defineProperty(target, effectiveServiceName, {
10
49
  value: service,
@@ -12,4 +51,6 @@ export default function injectEngineService(target, engineName, serviceName, key
12
51
  configurable: true,
13
52
  enumerable: true,
14
53
  });
54
+
55
+ return service;
15
56
  }
@@ -0,0 +1,3 @@
1
+ export default function isString(_var) {
2
+ return typeof _var === 'string';
3
+ }
@@ -1,8 +1,15 @@
1
1
  import loadExtensions from '../utils/load-extensions';
2
2
  import fleetbaseApiFetch from '../utils/fleetbase-api-fetch';
3
3
 
4
- export default async function loadInstalledExtensions() {
5
- const CORE_ENGINES = ['@fleetbase/fleetops-engine', '@fleetbase/storefront-engine', '@fleetbase/registry-bridge-engine', '@fleetbase/dev-engine', '@fleetbase/iam-engine'];
4
+ export default async function loadInstalledExtensions(additionalCoreEngines = []) {
5
+ const CORE_ENGINES = [
6
+ '@fleetbase/fleetops-engine',
7
+ '@fleetbase/storefront-engine',
8
+ '@fleetbase/registry-bridge-engine',
9
+ '@fleetbase/dev-engine',
10
+ '@fleetbase/iam-engine',
11
+ ...additionalCoreEngines,
12
+ ];
6
13
  const INDEXED_ENGINES = await loadExtensions();
7
14
  const INSTALLED_ENGINES = await fleetbaseApiFetch('get', 'engines', {}, { namespace: '~registry/v1', fallbackResponse: [] });
8
15
 
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/ember-core/decorators/engine-service';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/ember-core/utils/is-string';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fleetbase/ember-core",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
4
4
  "description": "Provides all the core services, decorators and utilities for building a Fleetbase extension for the Console.",
5
5
  "keywords": [
6
6
  "fleetbase-core",