@fleetbase/ember-core 0.2.12 → 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.
- package/addon/decorators/engine-service.js +25 -0
- package/addon/decorators/fetch-from.js +37 -40
- package/addon/decorators/from-store.js +40 -43
- package/addon/services/universe.js +240 -16
- package/addon/utils/fleetbase-api-fetch.js +63 -0
- package/addon/utils/inject-engine-service.js +42 -1
- package/addon/utils/is-string.js +3 -0
- package/addon/utils/load-installed-extensions.js +23 -0
- package/app/decorators/engine-service.js +1 -0
- package/app/utils/fleetbase-api-fetch.js +1 -0
- package/app/utils/is-string.js +1 -0
- package/app/utils/load-installed-extensions.js +1 -0
- package/package.json +1 -1
|
@@ -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 {
|
|
3
|
+
import { assert } from '@ember/debug';
|
|
5
4
|
|
|
6
|
-
export default function
|
|
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
|
-
|
|
12
|
-
|
|
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(
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
get()
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
18
|
+
// Setter to get symbol value
|
|
19
|
+
descriptor.set = function (value) {
|
|
20
|
+
this[symbol] = value;
|
|
21
|
+
};
|
|
10
22
|
|
|
11
|
-
|
|
12
|
-
|
|
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(
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
55
|
-
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return descriptor;
|
|
52
|
+
});
|
|
@@ -8,8 +8,12 @@ import { A, isArray } from '@ember/array';
|
|
|
8
8
|
import { later } from '@ember/runloop';
|
|
9
9
|
import { dasherize, camelize } from '@ember/string';
|
|
10
10
|
import { getOwner } from '@ember/application';
|
|
11
|
-
import { assert } from '@ember/debug';
|
|
11
|
+
import { assert, debug } from '@ember/debug';
|
|
12
12
|
import RSVP from 'rsvp';
|
|
13
|
+
import loadInstalledExtensions from '../utils/load-installed-extensions';
|
|
14
|
+
import loadExtensions from '../utils/load-extensions';
|
|
15
|
+
import getWithDefault from '../utils/get-with-default';
|
|
16
|
+
import config from 'ember-get-config';
|
|
13
17
|
|
|
14
18
|
export default class UniverseService extends Service.extend(Evented) {
|
|
15
19
|
@service router;
|
|
@@ -1055,29 +1059,88 @@ export default class UniverseService extends Service.extend(Evented) {
|
|
|
1055
1059
|
}
|
|
1056
1060
|
|
|
1057
1061
|
/**
|
|
1058
|
-
*
|
|
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.
|
|
1059
1066
|
*
|
|
1060
|
-
* @
|
|
1061
|
-
* @
|
|
1062
|
-
* @
|
|
1063
|
-
* @param {
|
|
1064
|
-
*
|
|
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.
|
|
1065
1086
|
*/
|
|
1066
|
-
registerComponentInEngine(engineName, componentClass) {
|
|
1087
|
+
registerComponentInEngine(engineName, componentClass, options = {}) {
|
|
1067
1088
|
const engineInstance = this.getEngineInstance(engineName);
|
|
1068
|
-
|
|
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') {
|
|
1069
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
|
+
}
|
|
1070
1121
|
}
|
|
1071
1122
|
}
|
|
1072
1123
|
|
|
1073
1124
|
/**
|
|
1074
|
-
*
|
|
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.
|
|
1075
1128
|
*
|
|
1076
|
-
* @
|
|
1077
|
-
* @
|
|
1078
|
-
* @
|
|
1079
|
-
*
|
|
1080
|
-
* @
|
|
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.
|
|
1081
1144
|
*/
|
|
1082
1145
|
registerServiceInEngine(targetEngineName, serviceName, currentEngineInstance) {
|
|
1083
1146
|
// Get the target engine instance
|
|
@@ -1108,11 +1171,16 @@ export default class UniverseService extends Service.extend(Evented) {
|
|
|
1108
1171
|
* userService.doSomething();
|
|
1109
1172
|
* }
|
|
1110
1173
|
*/
|
|
1111
|
-
getServiceFromEngine(engineName, serviceName) {
|
|
1174
|
+
getServiceFromEngine(engineName, serviceName, options = {}) {
|
|
1112
1175
|
const engineInstance = this.getEngineInstance(engineName);
|
|
1113
1176
|
|
|
1114
1177
|
if (engineInstance && typeof serviceName === 'string') {
|
|
1115
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
|
+
}
|
|
1116
1184
|
return serviceInstance;
|
|
1117
1185
|
}
|
|
1118
1186
|
|
|
@@ -1269,6 +1337,162 @@ export default class UniverseService extends Service.extend(Evented) {
|
|
|
1269
1337
|
return null;
|
|
1270
1338
|
}
|
|
1271
1339
|
|
|
1340
|
+
/**
|
|
1341
|
+
* Boot all installed engines, ensuring dependencies are resolved.
|
|
1342
|
+
*
|
|
1343
|
+
* This method attempts to boot all installed engines by first checking if all
|
|
1344
|
+
* their dependencies are already booted. If an engine has dependencies that
|
|
1345
|
+
* are not yet booted, it is deferred and retried after its dependencies are
|
|
1346
|
+
* booted. If some dependencies are never booted, an error is logged.
|
|
1347
|
+
*
|
|
1348
|
+
* @method bootEngines
|
|
1349
|
+
* @param {ApplicationInstance|null} owner - The Ember ApplicationInstance that owns the engines.
|
|
1350
|
+
* @return {void}
|
|
1351
|
+
*/
|
|
1352
|
+
bootEngines(owner = null) {
|
|
1353
|
+
const booted = [];
|
|
1354
|
+
const pending = [];
|
|
1355
|
+
const additionalCoreExtensions = config.APP.extensions ?? [];
|
|
1356
|
+
|
|
1357
|
+
// If no owner provided use the owner of this service
|
|
1358
|
+
if (owner === null) {
|
|
1359
|
+
owner = getOwner(this);
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
const tryBootEngine = (extension) => {
|
|
1363
|
+
this.loadEngine(extension.name).then((engineInstance) => {
|
|
1364
|
+
if (engineInstance.base && engineInstance.base.setupExtension) {
|
|
1365
|
+
if (booted.includes(extension.name)) {
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
const engineDependencies = getWithDefault(engineInstance.base, 'engineDependencies', []);
|
|
1370
|
+
const allDependenciesBooted = engineDependencies.every((dep) => booted.includes(dep));
|
|
1371
|
+
|
|
1372
|
+
if (!allDependenciesBooted) {
|
|
1373
|
+
pending.push({ extension, engineInstance });
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
engineInstance.base.setupExtension(owner, engineInstance, this);
|
|
1378
|
+
booted.push(extension.name);
|
|
1379
|
+
debug(`Booted : ${extension.name}`);
|
|
1380
|
+
|
|
1381
|
+
// Try booting pending engines again
|
|
1382
|
+
tryBootPendingEngines();
|
|
1383
|
+
}
|
|
1384
|
+
});
|
|
1385
|
+
};
|
|
1386
|
+
|
|
1387
|
+
const tryBootPendingEngines = () => {
|
|
1388
|
+
const stillPending = [];
|
|
1389
|
+
|
|
1390
|
+
pending.forEach(({ extension, engineInstance }) => {
|
|
1391
|
+
if (booted.includes(extension.name)) {
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
const engineDependencies = getWithDefault(engineInstance.base, 'engineDependencies', []);
|
|
1396
|
+
const allDependenciesBooted = engineDependencies.every((dep) => booted.includes(dep));
|
|
1397
|
+
|
|
1398
|
+
if (allDependenciesBooted) {
|
|
1399
|
+
engineInstance.base.setupExtension(owner, engineInstance, this);
|
|
1400
|
+
booted.push(extension.name);
|
|
1401
|
+
debug(`Booted : ${extension.name}`);
|
|
1402
|
+
} else {
|
|
1403
|
+
stillPending.push({ extension, engineInstance });
|
|
1404
|
+
}
|
|
1405
|
+
});
|
|
1406
|
+
|
|
1407
|
+
// If no progress was made, log an error in debug/development mode
|
|
1408
|
+
assert(`Some engines have unmet dependencies and cannot be booted:`, stillPending.length === 0 && pending.length === 0);
|
|
1409
|
+
|
|
1410
|
+
pending.length = 0;
|
|
1411
|
+
pending.push(...stillPending);
|
|
1412
|
+
};
|
|
1413
|
+
|
|
1414
|
+
loadInstalledExtensions(additionalCoreExtensions).then((extensions) => {
|
|
1415
|
+
extensions.forEach((extension) => {
|
|
1416
|
+
tryBootEngine(extension);
|
|
1417
|
+
});
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
/**
|
|
1422
|
+
* Boots all installed engines, ensuring dependencies are resolved.
|
|
1423
|
+
*
|
|
1424
|
+
* This method loads all installed extensions and then attempts to boot each engine.
|
|
1425
|
+
* For each extension, it loads the engine and, if the engine has a `setupExtension`
|
|
1426
|
+
* method in its base, it calls this method to complete the setup. This function ensures
|
|
1427
|
+
* that dependencies are resolved before booting the engines. If some dependencies are
|
|
1428
|
+
* never booted, an error is logged.
|
|
1429
|
+
*
|
|
1430
|
+
* @method legacyBootEngines
|
|
1431
|
+
* @param {ApplicationInstance|null} owner - The Ember ApplicationInstance that owns the engines.
|
|
1432
|
+
* @return {void}
|
|
1433
|
+
*/
|
|
1434
|
+
legacyBootEngines(owner = null) {
|
|
1435
|
+
const booted = [];
|
|
1436
|
+
const pending = [];
|
|
1437
|
+
|
|
1438
|
+
// If no owner provided use the owner of this service
|
|
1439
|
+
if (owner === null) {
|
|
1440
|
+
owner = getOwner(this);
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
const tryBootEngine = (extension) => {
|
|
1444
|
+
this.loadEngine(extension.name).then((engineInstance) => {
|
|
1445
|
+
if (engineInstance.base && engineInstance.base.setupExtension) {
|
|
1446
|
+
const engineDependencies = getWithDefault(engineInstance.base, 'engineDependencies', []);
|
|
1447
|
+
|
|
1448
|
+
// Check if all dependency engines are booted
|
|
1449
|
+
const allDependenciesBooted = engineDependencies.every((dep) => booted.includes(dep));
|
|
1450
|
+
|
|
1451
|
+
if (!allDependenciesBooted) {
|
|
1452
|
+
pending.push({ extension, engineInstance });
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
engineInstance.base.setupExtension(owner, engineInstance, this);
|
|
1457
|
+
booted.push(extension.name);
|
|
1458
|
+
debug(`Booted : ${extension.name}`);
|
|
1459
|
+
|
|
1460
|
+
// Try booting pending engines again
|
|
1461
|
+
tryBootPendingEngines();
|
|
1462
|
+
}
|
|
1463
|
+
});
|
|
1464
|
+
};
|
|
1465
|
+
|
|
1466
|
+
const tryBootPendingEngines = () => {
|
|
1467
|
+
const stillPending = [];
|
|
1468
|
+
|
|
1469
|
+
pending.forEach(({ extension, engineInstance }) => {
|
|
1470
|
+
const engineDependencies = getWithDefault(engineInstance.base, 'engineDependencies', []);
|
|
1471
|
+
const allDependenciesBooted = engineDependencies.every((dep) => booted.includes(dep));
|
|
1472
|
+
|
|
1473
|
+
if (allDependenciesBooted) {
|
|
1474
|
+
engineInstance.base.setupExtension(owner, engineInstance, this);
|
|
1475
|
+
booted.push(extension.name);
|
|
1476
|
+
debug(`Booted : ${extension.name}`);
|
|
1477
|
+
} else {
|
|
1478
|
+
stillPending.push({ extension, engineInstance });
|
|
1479
|
+
}
|
|
1480
|
+
});
|
|
1481
|
+
|
|
1482
|
+
// If no progress was made, log an error in debug/development mode
|
|
1483
|
+
assert('Some engines have unmet dependencies and cannot be booted:', pending.length === stillPending.length);
|
|
1484
|
+
|
|
1485
|
+
pending.length = 0;
|
|
1486
|
+
pending.push(...stillPending);
|
|
1487
|
+
};
|
|
1488
|
+
|
|
1489
|
+
loadExtensions().then((extensions) => {
|
|
1490
|
+
extensions.forEach((extension) => {
|
|
1491
|
+
tryBootEngine(extension);
|
|
1492
|
+
});
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1272
1496
|
/**
|
|
1273
1497
|
* Alias for intl service `t`
|
|
1274
1498
|
*
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import config from 'ember-get-config';
|
|
2
|
+
|
|
3
|
+
export default async function fleetbaseApiFetch(method, uri, params = {}, fetchOptions = {}) {
|
|
4
|
+
// Prepare base URL
|
|
5
|
+
const baseUrl = `${config.API.host}/${fetchOptions.namespace ?? config.API.namespace}`;
|
|
6
|
+
|
|
7
|
+
// Initialize headers
|
|
8
|
+
const headers = {
|
|
9
|
+
'Content-Type': 'application/json',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// Check localStorage for the session data
|
|
13
|
+
const localStorageSession = JSON.parse(window.localStorage.getItem('ember_simple_auth-session'));
|
|
14
|
+
let token;
|
|
15
|
+
if (localStorageSession) {
|
|
16
|
+
const { authenticated } = localStorageSession;
|
|
17
|
+
if (authenticated) {
|
|
18
|
+
token = authenticated.token;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Set Authorization header if token is available
|
|
23
|
+
if (token) {
|
|
24
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Configure request options
|
|
28
|
+
const options = {
|
|
29
|
+
method,
|
|
30
|
+
headers,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Handle params based on method
|
|
34
|
+
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method) && params) {
|
|
35
|
+
options.body = JSON.stringify(params);
|
|
36
|
+
} else if (method === 'GET' && params) {
|
|
37
|
+
// Add params to URL for GET requests
|
|
38
|
+
const urlParams = new URLSearchParams(params).toString();
|
|
39
|
+
uri += `?${urlParams}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
// Make the fetch request
|
|
44
|
+
const response = await fetch(`${baseUrl}/${uri}`, options);
|
|
45
|
+
|
|
46
|
+
// Check if the response is OK (status in the range 200-299)
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Parse and return the JSON response
|
|
52
|
+
return await response.json();
|
|
53
|
+
} catch (error) {
|
|
54
|
+
// If a fallback response is provided use it instead
|
|
55
|
+
if (fetchOptions && fetchOptions.fallbackResponse !== undefined) {
|
|
56
|
+
return fetchOptions.fallbackResponse;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Handle errors (network errors, JSON parsing errors, etc.)
|
|
60
|
+
console.error('Error making request:', error);
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -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
|
-
|
|
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,23 @@
|
|
|
1
|
+
import loadExtensions from '../utils/load-extensions';
|
|
2
|
+
import fleetbaseApiFetch from '../utils/fleetbase-api-fetch';
|
|
3
|
+
|
|
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
|
+
];
|
|
13
|
+
const INDEXED_ENGINES = await loadExtensions();
|
|
14
|
+
const INSTALLED_ENGINES = await fleetbaseApiFetch('get', 'engines', {}, { namespace: '~registry/v1', fallbackResponse: [] });
|
|
15
|
+
|
|
16
|
+
const isInstalledEngine = (engineName) => {
|
|
17
|
+
return CORE_ENGINES.includes(engineName) || INSTALLED_ENGINES.find((pkg) => pkg.name === engineName);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
return INDEXED_ENGINES.filter((pkg) => {
|
|
21
|
+
return isInstalledEngine(pkg.name);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/ember-core/decorators/engine-service';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/ember-core/utils/fleetbase-api-fetch';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/ember-core/utils/is-string';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/ember-core/utils/load-installed-extensions';
|
package/package.json
CHANGED