@fleetbase/ember-core 0.2.13 → 0.2.15
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/abilities/dynamic.js +39 -0
- 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/exports/host-services.js +1 -0
- package/addon/exports/services.js +1 -0
- package/addon/services/abilities.js +7 -0
- package/addon/services/current-user.js +100 -51
- package/addon/services/universe.js +92 -20
- package/addon/utils/inject-engine-service.js +42 -1
- package/addon/utils/is-string.js +3 -0
- package/addon/utils/load-installed-extensions.js +9 -2
- package/app/abilities/dynamic.js +1 -0
- package/app/decorators/engine-service.js +1 -0
- package/app/services/abilities.js +1 -0
- package/app/utils/is-string.js +1 -0
- package/index.js +1 -0
- package/package.json +124 -126
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Ability } from 'ember-can';
|
|
2
|
+
import { tracked } from '@glimmer/tracking';
|
|
3
|
+
import { inject as service } from '@ember/service';
|
|
4
|
+
import { singularize } from 'ember-inflector';
|
|
5
|
+
|
|
6
|
+
export default class extends Ability {
|
|
7
|
+
@service currentUser;
|
|
8
|
+
@tracked service;
|
|
9
|
+
@tracked resource;
|
|
10
|
+
@tracked ability;
|
|
11
|
+
@tracked permissions = new Set();
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
super(...arguments);
|
|
15
|
+
this.permissions = new Set(this.currentUser.permissions.map((permission) => permission.name));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
parseProperty(str) {
|
|
19
|
+
let [service, ability, resource] = str.split(' ');
|
|
20
|
+
|
|
21
|
+
this.service = service;
|
|
22
|
+
this.ability = ability;
|
|
23
|
+
this.resource = singularize(resource);
|
|
24
|
+
|
|
25
|
+
return 'can';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get can() {
|
|
29
|
+
if (this.currentUser.isAdmin) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const permission = [this.service, this.ability, this.resource].join(' ');
|
|
34
|
+
const wilcardPermission = [this.service, '*', this.resource].join(' ');
|
|
35
|
+
const wildcardServicePermission = [this.service, '*'].join(' ');
|
|
36
|
+
|
|
37
|
+
return this.permissions.has(permission) || this.permissions.has(wilcardPermission) || this.permissions.has(wildcardServicePermission);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -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
|
+
});
|
|
@@ -54,6 +54,13 @@ export default class CurrentUserService extends Service.extend(Evented) {
|
|
|
54
54
|
id: 'anon',
|
|
55
55
|
};
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* The current users permissions.
|
|
59
|
+
*
|
|
60
|
+
* @memberof CurrentUserService
|
|
61
|
+
*/
|
|
62
|
+
@tracked permissions = [];
|
|
63
|
+
|
|
57
64
|
/**
|
|
58
65
|
* User options in localStorage
|
|
59
66
|
*
|
|
@@ -96,36 +103,82 @@ export default class CurrentUserService extends Service.extend(Evented) {
|
|
|
96
103
|
*/
|
|
97
104
|
@alias('user.company_uuid') companyId;
|
|
98
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Alias for if user is admin.
|
|
108
|
+
*
|
|
109
|
+
* @var {Boolean}
|
|
110
|
+
* @memberof CurrentUserService
|
|
111
|
+
*/
|
|
112
|
+
@alias('user.is_admin') isAdmin;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The prefix for this user options
|
|
116
|
+
*
|
|
117
|
+
* @var {String}
|
|
118
|
+
*/
|
|
119
|
+
@computed('id') get optionsPrefix() {
|
|
120
|
+
return `${this.id}:`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
get latitude() {
|
|
124
|
+
return this.whois('latitude');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
get longitude() {
|
|
128
|
+
return this.whois('longitude');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
get currency() {
|
|
132
|
+
return this.whois('currency.code');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
get city() {
|
|
136
|
+
return this.whois('city');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
get country() {
|
|
140
|
+
return this.whois('country_code');
|
|
141
|
+
}
|
|
142
|
+
|
|
99
143
|
/**
|
|
100
144
|
* Loads the current authenticated user
|
|
101
145
|
*
|
|
102
|
-
* @
|
|
146
|
+
* @return Promise<UserModel>|null
|
|
103
147
|
*/
|
|
104
148
|
async load() {
|
|
105
149
|
if (this.session.isAuthenticated) {
|
|
106
150
|
let user = await this.store.findRecord('user', 'me');
|
|
107
151
|
this.set('user', user);
|
|
108
152
|
this.trigger('user.loaded', user);
|
|
153
|
+
|
|
154
|
+
// Set permissions
|
|
155
|
+
this.permissions = this.getUserPermissions(user);
|
|
156
|
+
|
|
157
|
+
return user;
|
|
109
158
|
}
|
|
159
|
+
|
|
160
|
+
return null;
|
|
110
161
|
}
|
|
111
162
|
|
|
112
163
|
/**
|
|
113
164
|
* Resolves a user model.
|
|
114
165
|
*
|
|
115
|
-
* @return {Promise}
|
|
166
|
+
* @return {Promise<User>}
|
|
116
167
|
*/
|
|
117
168
|
@action promiseUser(options = {}) {
|
|
118
169
|
const NoUserAuthenticatedError = new Error('Failed to authenticate user.');
|
|
119
170
|
|
|
120
171
|
return new Promise((resolve, reject) => {
|
|
121
172
|
if (this.session.isAuthenticated) {
|
|
122
|
-
|
|
123
|
-
.queryRecord('user', { me: true })
|
|
124
|
-
.then((user) => {
|
|
173
|
+
try {
|
|
174
|
+
this.store.queryRecord('user', { me: true }).then((user) => {
|
|
125
175
|
// set the `current user`
|
|
126
176
|
this.set('user', user);
|
|
127
177
|
this.trigger('user.loaded', user);
|
|
128
178
|
|
|
179
|
+
// Set permissions
|
|
180
|
+
this.permissions = this.getUserPermissions(user);
|
|
181
|
+
|
|
129
182
|
// set environment from user option
|
|
130
183
|
this.theme.setEnvironment();
|
|
131
184
|
|
|
@@ -135,10 +188,10 @@ export default class CurrentUserService extends Service.extend(Evented) {
|
|
|
135
188
|
}
|
|
136
189
|
|
|
137
190
|
resolve(user);
|
|
138
|
-
})
|
|
139
|
-
.catch(() => {
|
|
140
|
-
reject(NoUserAuthenticatedError);
|
|
141
191
|
});
|
|
192
|
+
} catch (error) {
|
|
193
|
+
reject(NoUserAuthenticatedError);
|
|
194
|
+
}
|
|
142
195
|
} else {
|
|
143
196
|
reject(NoUserAuthenticatedError);
|
|
144
197
|
}
|
|
@@ -146,60 +199,56 @@ export default class CurrentUserService extends Service.extend(Evented) {
|
|
|
146
199
|
}
|
|
147
200
|
|
|
148
201
|
/**
|
|
149
|
-
*
|
|
202
|
+
* Gets all user permissions.
|
|
150
203
|
*
|
|
151
|
-
* @
|
|
204
|
+
* @param {UserModel} user
|
|
205
|
+
* @return {Array}
|
|
206
|
+
* @memberof CurrentUserService
|
|
152
207
|
*/
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
this.fetch
|
|
156
|
-
.get('fleet-ops/order-configs/get-installed', params)
|
|
157
|
-
.then((configs) => {
|
|
158
|
-
const serialized = [];
|
|
208
|
+
getUserPermissions(user) {
|
|
209
|
+
const permissions = [];
|
|
159
210
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
211
|
+
// get direct applied permissions
|
|
212
|
+
if (user.get('permissions')) {
|
|
213
|
+
permissions.pushObjects(user.get('permissions').toArray());
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// get role permissions and role policies permissions
|
|
217
|
+
if (user.get('role')) {
|
|
218
|
+
if (user.get('role.permissions')) {
|
|
219
|
+
permissions.pushObjects(user.get('role.permissions').toArray());
|
|
220
|
+
}
|
|
164
221
|
|
|
165
|
-
|
|
222
|
+
if (user.get('role.policies')) {
|
|
223
|
+
for (let i = 0; i < user.get('role.policies').length; i++) {
|
|
224
|
+
const policy = user.get('role.policies').objectAt(i);
|
|
225
|
+
if (policy.get('permissions')) {
|
|
226
|
+
permissions.pushObjects(policy.get('permissions').toArray());
|
|
166
227
|
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
167
231
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
232
|
+
// get direct applied policy permissions
|
|
233
|
+
if (user.get('policies')) {
|
|
234
|
+
for (let i = 0; i < user.get('policies').length; i++) {
|
|
235
|
+
const policy = user.get('policies').objectAt(i);
|
|
236
|
+
if (policy.get('permissions')) {
|
|
237
|
+
permissions.pushObjects(policy.get('permissions').toArray());
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return permissions;
|
|
172
243
|
}
|
|
173
244
|
|
|
174
245
|
/**
|
|
175
|
-
*
|
|
246
|
+
* Alias to get a user's whois property
|
|
176
247
|
*
|
|
177
|
-
* @
|
|
248
|
+
* @param {String} key
|
|
249
|
+
* @return {Mixed}
|
|
250
|
+
* @memberof CurrentUserService
|
|
178
251
|
*/
|
|
179
|
-
@computed('id') get optionsPrefix() {
|
|
180
|
-
return `${this.id}:`;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
get latitude() {
|
|
184
|
-
return this.whois('latitude');
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
get longitude() {
|
|
188
|
-
return this.whois('longitude');
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
get currency() {
|
|
192
|
-
return this.whois('currency.code');
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
get city() {
|
|
196
|
-
return this.whois('city');
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
get country() {
|
|
200
|
-
return this.whois('country_code');
|
|
201
|
-
}
|
|
202
|
-
|
|
203
252
|
@action whois(key) {
|
|
204
253
|
return this.getWhoisProperty(key);
|
|
205
254
|
}
|
|
@@ -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;
|
|
@@ -846,7 +847,7 @@ export default class UniverseService extends Service.extend(Evented) {
|
|
|
846
847
|
// If component is a definition register to host application
|
|
847
848
|
if (typeof component === 'function') {
|
|
848
849
|
const owner = getOwner(this);
|
|
849
|
-
|
|
850
|
+
widgetId = component.widgetId || widgetId || this._createUniqueWidgetHashFromDefinition(component);
|
|
850
851
|
|
|
851
852
|
if (owner) {
|
|
852
853
|
owner.register(`component:${widgetId}`, component);
|
|
@@ -1058,29 +1059,88 @@ export default class UniverseService extends Service.extend(Evented) {
|
|
|
1058
1059
|
}
|
|
1059
1060
|
|
|
1060
1061
|
/**
|
|
1061
|
-
*
|
|
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
|
-
* @
|
|
1064
|
-
* @
|
|
1065
|
-
* @
|
|
1066
|
-
* @param {
|
|
1067
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
* @
|
|
1080
|
-
* @
|
|
1081
|
-
* @
|
|
1082
|
-
*
|
|
1083
|
-
* @
|
|
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
|
-
|
|
1365
|
+
if (booted.includes(extension.name)) {
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1300
1368
|
|
|
1301
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
}
|
|
@@ -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 = [
|
|
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/abilities/dynamic';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/ember-core/decorators/engine-service';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/ember-core/services/abilities';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/ember-core/utils/is-string';
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,128 +1,126 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
"
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
"
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
"
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
"
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
]
|
|
127
|
-
}
|
|
2
|
+
"name": "@fleetbase/ember-core",
|
|
3
|
+
"version": "0.2.15",
|
|
4
|
+
"description": "Provides all the core services, decorators and utilities for building a Fleetbase extension for the Console.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"fleetbase-core",
|
|
7
|
+
"fleetbase-services",
|
|
8
|
+
"fleetbase",
|
|
9
|
+
"ember-addon"
|
|
10
|
+
],
|
|
11
|
+
"repository": "https://github.com/fleetbase/ember-core",
|
|
12
|
+
"license": "AGPL-3.0-or-later",
|
|
13
|
+
"author": "Fleetbase Pte Ltd <hello@fleetbase.io>",
|
|
14
|
+
"directories": {
|
|
15
|
+
"app": "app",
|
|
16
|
+
"addon": "addon",
|
|
17
|
+
"tests": "tests"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "ember build --environment=production",
|
|
21
|
+
"lint": "concurrently \"npm:lint:*(!fix)\" --names \"lint:\"",
|
|
22
|
+
"lint:css": "stylelint \"**/*.css\"",
|
|
23
|
+
"lint:css:fix": "concurrently \"npm:lint:css -- --fix\"",
|
|
24
|
+
"lint:fix": "concurrently \"npm:lint:*:fix\" --names \"fix:\"",
|
|
25
|
+
"lint:hbs": "ember-template-lint .",
|
|
26
|
+
"lint:hbs:fix": "ember-template-lint . --fix",
|
|
27
|
+
"lint:js": "eslint . --cache",
|
|
28
|
+
"lint:js:fix": "eslint . --fix",
|
|
29
|
+
"start": "ember serve",
|
|
30
|
+
"test": "concurrently \"npm:lint\" \"npm:test:*\" --names \"lint,test:\"",
|
|
31
|
+
"test:ember": "ember test",
|
|
32
|
+
"test:ember-compatibility": "ember try:each",
|
|
33
|
+
"publish:npm": "npm config set registry https://registry.npmjs.org/ && npm publish",
|
|
34
|
+
"publish:github": "npm config set '@fleetbase:registry' https://npm.pkg.github.com/ && npm publish"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@babel/core": "^7.23.2",
|
|
38
|
+
"compress-json": "^3.0.0",
|
|
39
|
+
"date-fns": "^2.30.0",
|
|
40
|
+
"ember-auto-import": "^2.7.4",
|
|
41
|
+
"ember-can": "^6.0.0",
|
|
42
|
+
"ember-cli-babel": "^8.2.0",
|
|
43
|
+
"ember-cli-htmlbars": "^6.3.0",
|
|
44
|
+
"ember-cli-notifications": "^9.0.0",
|
|
45
|
+
"ember-concurrency": "^3.1.1",
|
|
46
|
+
"ember-concurrency-decorators": "^2.0.3",
|
|
47
|
+
"ember-decorators": "^6.1.1",
|
|
48
|
+
"ember-get-config": "^2.1.1",
|
|
49
|
+
"ember-inflector": "^4.0.2",
|
|
50
|
+
"ember-intl": "6.3.2",
|
|
51
|
+
"ember-loading": "^2.0.0",
|
|
52
|
+
"ember-local-storage": "^2.0.4",
|
|
53
|
+
"ember-simple-auth": "^6.0.0",
|
|
54
|
+
"ember-wormhole": "^0.6.0",
|
|
55
|
+
"socketcluster-client": "^17.1.1"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@babel/eslint-parser": "^7.22.15",
|
|
59
|
+
"@babel/plugin-proposal-decorators": "^7.23.2",
|
|
60
|
+
"@ember/optional-features": "^2.0.0",
|
|
61
|
+
"@ember/test-helpers": "^3.2.0",
|
|
62
|
+
"@embroider/test-setup": "^3.0.2",
|
|
63
|
+
"@glimmer/component": "^1.1.2",
|
|
64
|
+
"@glimmer/tracking": "^1.1.2",
|
|
65
|
+
"broccoli-asset-rev": "^3.0.0",
|
|
66
|
+
"broccoli-funnel": "^3.0.8",
|
|
67
|
+
"broccoli-merge-trees": "^4.2.0",
|
|
68
|
+
"broccoli-persistent-filter": "^3.1.3",
|
|
69
|
+
"concurrently": "^8.2.2",
|
|
70
|
+
"ember-cli": "~5.4.1",
|
|
71
|
+
"ember-cli-clean-css": "^3.0.0",
|
|
72
|
+
"ember-cli-dependency-checker": "^3.3.2",
|
|
73
|
+
"ember-cli-inject-live-reload": "^2.1.0",
|
|
74
|
+
"ember-cli-sri": "^2.1.1",
|
|
75
|
+
"ember-cli-terser": "^4.0.2",
|
|
76
|
+
"ember-data": "^4.12.5",
|
|
77
|
+
"ember-file-upload": "8.4.0",
|
|
78
|
+
"ember-load-initializers": "^2.1.2",
|
|
79
|
+
"ember-page-title": "^8.0.0",
|
|
80
|
+
"ember-qunit": "^8.0.1",
|
|
81
|
+
"ember-resolver": "^11.0.1",
|
|
82
|
+
"ember-source": "~5.4.0",
|
|
83
|
+
"ember-source-channel-url": "^3.0.0",
|
|
84
|
+
"ember-template-lint": "^5.11.2",
|
|
85
|
+
"ember-try": "^3.0.0",
|
|
86
|
+
"eslint": "^8.52.0",
|
|
87
|
+
"eslint-config-prettier": "^9.0.0",
|
|
88
|
+
"eslint-plugin-ember": "^11.11.1",
|
|
89
|
+
"eslint-plugin-n": "^16.2.0",
|
|
90
|
+
"eslint-plugin-prettier": "^5.0.1",
|
|
91
|
+
"eslint-plugin-qunit": "^8.0.1",
|
|
92
|
+
"loader.js": "^4.7.0",
|
|
93
|
+
"prettier": "^3.0.3",
|
|
94
|
+
"qunit": "^2.20.0",
|
|
95
|
+
"qunit-dom": "^2.0.0",
|
|
96
|
+
"resolve": "^1.22.2",
|
|
97
|
+
"stylelint": "^15.11.0",
|
|
98
|
+
"stylelint-config-standard": "^34.0.0",
|
|
99
|
+
"stylelint-prettier": "^4.0.2",
|
|
100
|
+
"webpack": "^5.89.0"
|
|
101
|
+
},
|
|
102
|
+
"engines": {
|
|
103
|
+
"node": ">= 18"
|
|
104
|
+
},
|
|
105
|
+
"ember": {
|
|
106
|
+
"edition": "octane"
|
|
107
|
+
},
|
|
108
|
+
"ember-addon": {
|
|
109
|
+
"configPath": "tests/dummy/config"
|
|
110
|
+
},
|
|
111
|
+
"prettier": {
|
|
112
|
+
"trailingComma": "es5",
|
|
113
|
+
"tabWidth": 4,
|
|
114
|
+
"semi": true,
|
|
115
|
+
"singleQuote": true,
|
|
116
|
+
"printWidth": 190,
|
|
117
|
+
"overrides": [
|
|
118
|
+
{
|
|
119
|
+
"files": "*.hbs",
|
|
120
|
+
"options": {
|
|
121
|
+
"singleQuote": false
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
}
|
|
128
126
|
}
|