roboto-js 3.0.0 → 3.0.2
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/README.md +2 -3
- package/dist/cjs/index.cjs +185 -111
- package/dist/cjs/rbt_api.cjs +649 -454
- package/dist/cjs/rbt_object.cjs +65 -21
- package/dist/cjs/version.cjs +2 -2
- package/dist/esm/index.js +185 -111
- package/dist/esm/rbt_api.js +650 -454
- package/dist/esm/rbt_object.js +65 -21
- package/dist/esm/version.js +2 -2
- package/package.json +1 -1
- package/src/index.js +13 -5
- package/src/rbt_api.js +129 -65
- package/src/rbt_object.js +46 -0
- package/src/version.js +2 -2
package/dist/esm/rbt_object.js
CHANGED
|
@@ -351,11 +351,47 @@ var RbtObject = /*#__PURE__*/function () {
|
|
|
351
351
|
});
|
|
352
352
|
return clonedObject;
|
|
353
353
|
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Rebuild read_ids/write_ids record meta from iac grant arrays.
|
|
357
|
+
* Keeps denormalized columns in sync with dataJson.iac for delta saves.
|
|
358
|
+
*/
|
|
359
|
+
}, {
|
|
360
|
+
key: "_syncIacRecordMeta",
|
|
361
|
+
value: function _syncIacRecordMeta() {
|
|
362
|
+
var iac = _.get(this._data, 'iac');
|
|
363
|
+
if (!iac) return;
|
|
364
|
+
var readIds = new Set();
|
|
365
|
+
var writeIds = new Set();
|
|
366
|
+
if (this.type === '<@iac.user>') {
|
|
367
|
+
writeIds.add(this.id);
|
|
368
|
+
readIds.add(this.id);
|
|
369
|
+
}
|
|
370
|
+
if (iac.creator) {
|
|
371
|
+
writeIds.add(iac.creator);
|
|
372
|
+
readIds.add(iac.creator);
|
|
373
|
+
}
|
|
374
|
+
var collect = function collect(grants, target) {
|
|
375
|
+
if (!grants) return;
|
|
376
|
+
for (var _i = 0, _arr = ['users', 'userGroups', 'organizations', 'userSegments']; _i < _arr.length; _i++) {
|
|
377
|
+
var key = _arr[_i];
|
|
378
|
+
if (Array.isArray(grants[key])) {
|
|
379
|
+
grants[key].forEach(function (id) {
|
|
380
|
+
return target.add(id);
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
collect(iac.readGrants, readIds);
|
|
386
|
+
collect(iac.writeGrants, writeIds);
|
|
387
|
+
this._internalData.read_ids = Array.from(readIds).join(',');
|
|
388
|
+
this._internalData.write_ids = Array.from(writeIds).join(',');
|
|
389
|
+
}
|
|
354
390
|
}, {
|
|
355
391
|
key: "save",
|
|
356
392
|
value: function () {
|
|
357
393
|
var _save = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3() {
|
|
358
|
-
var _response$data, _response$data2, record, response;
|
|
394
|
+
var _response$data, _response$data2, iacGrantChange, record, response;
|
|
359
395
|
return _regeneratorRuntime().wrap(function _callee3$(_context3) {
|
|
360
396
|
while (1) switch (_context3.prev = _context3.next) {
|
|
361
397
|
case 0:
|
|
@@ -366,28 +402,34 @@ var RbtObject = /*#__PURE__*/function () {
|
|
|
366
402
|
throw new Error('Cannot save object without type');
|
|
367
403
|
case 2:
|
|
368
404
|
_context3.prev = 2;
|
|
405
|
+
iacGrantChange = (this.rpcMeta.changes || []).some(function (path) {
|
|
406
|
+
return path.startsWith('iac.readGrants') || path.startsWith('iac.writeGrants');
|
|
407
|
+
});
|
|
408
|
+
if (iacGrantChange) {
|
|
409
|
+
this._syncIacRecordMeta();
|
|
410
|
+
}
|
|
369
411
|
if (this.rpcMeta.isNew) {
|
|
370
412
|
record = this.toRecord();
|
|
371
413
|
} else {
|
|
372
414
|
record = this.toDeltaRecord();
|
|
373
415
|
}
|
|
374
|
-
_context3.next =
|
|
416
|
+
_context3.next = 8;
|
|
375
417
|
return this._axios.post('/object_service/saveObject', [record]);
|
|
376
|
-
case
|
|
418
|
+
case 8:
|
|
377
419
|
response = _context3.sent;
|
|
378
420
|
if (!(response.data.ok === false)) {
|
|
379
|
-
_context3.next =
|
|
421
|
+
_context3.next = 11;
|
|
380
422
|
break;
|
|
381
423
|
}
|
|
382
424
|
throw new Error(response.data.message);
|
|
383
|
-
case
|
|
425
|
+
case 11:
|
|
384
426
|
if (!(((_response$data = response.data) === null || _response$data === void 0 ? void 0 : _response$data.result) == 'NO_CHANGES')) {
|
|
385
|
-
_context3.next =
|
|
427
|
+
_context3.next = 14;
|
|
386
428
|
break;
|
|
387
429
|
}
|
|
388
430
|
this.rpcMeta.isNew = false;
|
|
389
431
|
return _context3.abrupt("return", this);
|
|
390
|
-
case
|
|
432
|
+
case 14:
|
|
391
433
|
if ((_response$data2 = response.data) !== null && _response$data2 !== void 0 && _response$data2.newData) {
|
|
392
434
|
// apply new incoming data
|
|
393
435
|
this.setData(response.newData);
|
|
@@ -397,17 +439,17 @@ var RbtObject = /*#__PURE__*/function () {
|
|
|
397
439
|
this.type = response.data.type;
|
|
398
440
|
this.rpcMeta.isNew = false;
|
|
399
441
|
return _context3.abrupt("return", this);
|
|
400
|
-
case
|
|
401
|
-
_context3.prev =
|
|
442
|
+
case 22:
|
|
443
|
+
_context3.prev = 22;
|
|
402
444
|
_context3.t0 = _context3["catch"](2);
|
|
403
445
|
this._error = _context3.t0;
|
|
404
446
|
//console.log(e.response.data);
|
|
405
447
|
throw _context3.t0;
|
|
406
|
-
case
|
|
448
|
+
case 26:
|
|
407
449
|
case "end":
|
|
408
450
|
return _context3.stop();
|
|
409
451
|
}
|
|
410
|
-
}, _callee3, this, [[2,
|
|
452
|
+
}, _callee3, this, [[2, 22]]);
|
|
411
453
|
}));
|
|
412
454
|
function save() {
|
|
413
455
|
return _save.apply(this, arguments);
|
|
@@ -524,19 +566,20 @@ var RbtObject = /*#__PURE__*/function () {
|
|
|
524
566
|
this.set(groupsPath, _toConsumableArray(new Set([].concat(_toConsumableArray(existingGroups), _toConsumableArray(groupIds)))));
|
|
525
567
|
}
|
|
526
568
|
}
|
|
569
|
+
this._syncIacRecordMeta();
|
|
527
570
|
|
|
528
571
|
// Save if requested
|
|
529
572
|
if (!save) {
|
|
530
|
-
_context4.next =
|
|
573
|
+
_context4.next = 16;
|
|
531
574
|
break;
|
|
532
575
|
}
|
|
533
|
-
_context4.next =
|
|
576
|
+
_context4.next = 15;
|
|
534
577
|
return this.save();
|
|
535
|
-
case 14:
|
|
536
|
-
return _context4.abrupt("return", _context4.sent);
|
|
537
578
|
case 15:
|
|
538
|
-
return _context4.abrupt("return",
|
|
579
|
+
return _context4.abrupt("return", _context4.sent);
|
|
539
580
|
case 16:
|
|
581
|
+
return _context4.abrupt("return", this);
|
|
582
|
+
case 17:
|
|
540
583
|
case "end":
|
|
541
584
|
return _context4.stop();
|
|
542
585
|
}
|
|
@@ -769,19 +812,20 @@ var RbtObject = /*#__PURE__*/function () {
|
|
|
769
812
|
return !groupIds.includes(id);
|
|
770
813
|
}));
|
|
771
814
|
}
|
|
815
|
+
this._syncIacRecordMeta();
|
|
772
816
|
|
|
773
817
|
// Save if requested
|
|
774
818
|
if (!save) {
|
|
775
|
-
_context7.next =
|
|
819
|
+
_context7.next = 16;
|
|
776
820
|
break;
|
|
777
821
|
}
|
|
778
|
-
_context7.next =
|
|
822
|
+
_context7.next = 15;
|
|
779
823
|
return this.save();
|
|
780
|
-
case 14:
|
|
781
|
-
return _context7.abrupt("return", _context7.sent);
|
|
782
824
|
case 15:
|
|
783
|
-
return _context7.abrupt("return",
|
|
825
|
+
return _context7.abrupt("return", _context7.sent);
|
|
784
826
|
case 16:
|
|
827
|
+
return _context7.abrupt("return", this);
|
|
828
|
+
case 17:
|
|
785
829
|
case "end":
|
|
786
830
|
return _context7.stop();
|
|
787
831
|
}
|
package/dist/esm/version.js
CHANGED
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -298,10 +298,18 @@ export default class Roboto{
|
|
|
298
298
|
async confirmUserEmail(params){
|
|
299
299
|
return this.api.confirmUserEmail(params);
|
|
300
300
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
301
|
+
async sendResetEmailViaFlows(email) {
|
|
302
|
+
return this.api.sendResetEmailViaFlows(email);
|
|
303
|
+
}
|
|
304
|
+
async sendRegistrationVerificationEmail(email) {
|
|
305
|
+
return this.api.sendRegistrationVerificationEmail(email);
|
|
306
|
+
}
|
|
307
|
+
async resendRegistrationCode(email) {
|
|
308
|
+
return this.api.resendRegistrationCode(email);
|
|
309
|
+
}
|
|
310
|
+
async completePasswordReset(params) {
|
|
311
|
+
return this.api.completePasswordReset(params);
|
|
312
|
+
}
|
|
305
313
|
async loadCurrentOrganization(forceReload = false){
|
|
306
314
|
return this.api.loadCurrentOrganization(forceReload);
|
|
307
315
|
}
|
|
@@ -315,7 +323,7 @@ export default class Roboto{
|
|
|
315
323
|
return this.api.getCurrentOrganization();
|
|
316
324
|
}
|
|
317
325
|
get currentOrganization(){
|
|
318
|
-
return this.api.
|
|
326
|
+
return this.api.getCurrentOrganization();
|
|
319
327
|
}
|
|
320
328
|
|
|
321
329
|
//
|
package/src/rbt_api.js
CHANGED
|
@@ -1,11 +1,36 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
-
import CryptoJS from 'crypto-js';
|
|
3
2
|
import RbtObject from './rbt_object.js';
|
|
4
3
|
import RbtUser from './rbt_user.js';
|
|
5
4
|
import RbtFile from './rbt_file.js';
|
|
6
5
|
import _ from 'lodash';
|
|
7
6
|
import { openDB } from 'idb';
|
|
8
7
|
|
|
8
|
+
/** IAC working org preference lives under module-scoped settings (`mod.iac`). Legacy reads still honor `mod.currentOrgId`. */
|
|
9
|
+
const USER_MOD_CURRENT_ORG_IAC = 'mod.iac.currentOrgId';
|
|
10
|
+
const USER_MOD_CURRENT_ORG_LEGACY = 'mod.currentOrgId';
|
|
11
|
+
|
|
12
|
+
/** @param {import('./rbt_user.js').default|null|undefined} user */
|
|
13
|
+
function readUserCurrentOrgPreference(user) {
|
|
14
|
+
if (!user || typeof user.get !== 'function') return '';
|
|
15
|
+
|
|
16
|
+
let v = user.get(USER_MOD_CURRENT_ORG_IAC);
|
|
17
|
+
if (v != null && String(v).trim() !== '') return String(v).trim();
|
|
18
|
+
v = user.get(USER_MOD_CURRENT_ORG_LEGACY);
|
|
19
|
+
if (v != null && String(v).trim() !== '') return String(v).trim();
|
|
20
|
+
|
|
21
|
+
const d = typeof user.getData === 'function' ? user.getData() : {};
|
|
22
|
+
v = _.get(d, 'mod.iac.currentOrgId') ?? _.get(d, 'mod.currentOrgId');
|
|
23
|
+
if (v != null && String(v).trim() !== '') return String(v).trim();
|
|
24
|
+
|
|
25
|
+
return '';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** @param {import('./rbt_user.js').default} user */
|
|
29
|
+
function persistUserCurrentOrgPreference(user, orgId) {
|
|
30
|
+
if (!user?.set || orgId == null || String(orgId).trim() === '') return;
|
|
31
|
+
user.set(USER_MOD_CURRENT_ORG_IAC, String(orgId).trim());
|
|
32
|
+
}
|
|
33
|
+
|
|
9
34
|
export default class RbtApi {
|
|
10
35
|
|
|
11
36
|
constructor({ baseUrl, accesskey, authtoken=null, apikey=null, localStorageAdaptor=null, withCredentials=true }) {
|
|
@@ -299,12 +324,11 @@ export default class RbtApi {
|
|
|
299
324
|
|
|
300
325
|
try {
|
|
301
326
|
|
|
302
|
-
const response = await this.axios.post('/api/iac/
|
|
327
|
+
const response = await this.axios.post('/api/iac/login', {
|
|
303
328
|
email: params.email,
|
|
304
|
-
password:
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}]);
|
|
329
|
+
password: params.password || null,
|
|
330
|
+
code: params.code || null,
|
|
331
|
+
});
|
|
308
332
|
|
|
309
333
|
if (response.data.ok === false) {
|
|
310
334
|
return this._handleError(response);
|
|
@@ -471,24 +495,64 @@ export default class RbtApi {
|
|
|
471
495
|
}
|
|
472
496
|
|
|
473
497
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
498
|
+
/**
|
|
499
|
+
* Confirm email with either legacy composite string or { email, code } (6-digit from mod-iac-flows).
|
|
500
|
+
* @param {string|{ email: string, code: string }|{ emailConfirmCode: string }} confirmCodeOrParams
|
|
501
|
+
*/
|
|
502
|
+
async confirmUserEmail(confirmCodeOrParams) {
|
|
503
|
+
let params;
|
|
504
|
+
if (typeof confirmCodeOrParams === 'string') {
|
|
505
|
+
params = { emailConfirmCode: confirmCodeOrParams };
|
|
506
|
+
} else if (confirmCodeOrParams && typeof confirmCodeOrParams === 'object') {
|
|
507
|
+
const { email, code, emailConfirmCode } = confirmCodeOrParams;
|
|
508
|
+
if (email && code != null && String(code).length > 0) {
|
|
509
|
+
params = { email, code: String(code) };
|
|
510
|
+
} else if (emailConfirmCode) {
|
|
511
|
+
params = { emailConfirmCode };
|
|
512
|
+
} else {
|
|
513
|
+
const err = new Error('confirmUserEmail: pass a legacy string, { emailConfirmCode }, or { email, code }');
|
|
514
|
+
return this._handleError(err);
|
|
515
|
+
}
|
|
516
|
+
} else {
|
|
517
|
+
const err = new Error('confirmUserEmail: invalid argument');
|
|
518
|
+
return this._handleError(err);
|
|
519
|
+
}
|
|
477
520
|
|
|
478
521
|
try {
|
|
479
|
-
|
|
480
522
|
const response = await this.axios.post('/api/iac/confirmUserEmail', [params]);
|
|
481
|
-
|
|
482
523
|
if (response.data.ok === false) {
|
|
483
524
|
return this._handleError(response);
|
|
484
525
|
}
|
|
485
|
-
|
|
486
526
|
return response.data;
|
|
487
|
-
|
|
488
527
|
} catch (e) {
|
|
489
528
|
return this._handleError(e);
|
|
490
529
|
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/** Password reset email — use mod-iac-flows (not deprecated /api/iac/sendPasswordReset). */
|
|
533
|
+
async sendResetEmailViaFlows(email) {
|
|
534
|
+
const response = await this.axios.post('/api/iac-flows/sendResetEmail', { email });
|
|
535
|
+
return response.data;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
async sendRegistrationVerificationEmail(email) {
|
|
539
|
+
const response = await this.axios.post('/api/iac-flows/sendRegistrationVerification', { email });
|
|
540
|
+
return response.data;
|
|
541
|
+
}
|
|
491
542
|
|
|
543
|
+
async resendRegistrationCode(email) {
|
|
544
|
+
const response = await this.axios.post('/api/iac-flows/resendRegistrationCode', { email });
|
|
545
|
+
return response.data;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
async completePasswordReset({ email, code, newPassword }) {
|
|
549
|
+
const response = await this.axios.post('/api/iac/completePasswordReset', [
|
|
550
|
+
{ email, code, newPassword },
|
|
551
|
+
]);
|
|
552
|
+
if (response.data?.ok === false) {
|
|
553
|
+
return this._handleError(response);
|
|
554
|
+
}
|
|
555
|
+
return response.data;
|
|
492
556
|
}
|
|
493
557
|
|
|
494
558
|
async loadCurrentUserExtended(){
|
|
@@ -516,55 +580,55 @@ export default class RbtApi {
|
|
|
516
580
|
}
|
|
517
581
|
|
|
518
582
|
/**
|
|
519
|
-
* Load current organization for the authenticated user
|
|
520
|
-
*
|
|
521
|
-
*
|
|
522
|
-
*
|
|
523
|
-
*
|
|
524
|
-
*
|
|
525
|
-
* @param {boolean} forceReload - Force reload from server even if cached
|
|
583
|
+
* Load current organization for the authenticated user.
|
|
584
|
+
*
|
|
585
|
+
* Returns an organization **only when** `user.mod.iac.currentOrgId` is set (explicit selection via
|
|
586
|
+
* `switchOrganization` / `selectCurrentOrganization`; legacy reads also accept `mod.currentOrgId`).
|
|
587
|
+
* There is no fallback to “first org”; callers must guide the user to select an org first.
|
|
588
|
+
*
|
|
589
|
+
* @param {boolean} forceReload - Force reload from server even if cached ID matches preference
|
|
526
590
|
* @returns {Promise<RbtObject|null>} Organization object or null
|
|
527
591
|
*/
|
|
528
592
|
async loadCurrentOrganization(forceReload = false) {
|
|
529
593
|
try {
|
|
530
|
-
// Return cached if available and not forcing reload
|
|
531
|
-
if (this.currentOrganization && !forceReload) {
|
|
532
|
-
//console.log('[RbtApi] Returning cached currentOrganization:', this.currentOrganization.get('name'));
|
|
533
|
-
return this.currentOrganization;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
594
|
// Prevent duplicate concurrent loads
|
|
537
595
|
if (this._loadCurrentOrgPromise) {
|
|
538
|
-
//console.log('[RbtApi] Organization load already in progress');
|
|
539
596
|
return this._loadCurrentOrgPromise;
|
|
540
597
|
}
|
|
541
598
|
|
|
542
599
|
this._loadCurrentOrgPromise = (async () => {
|
|
543
600
|
try {
|
|
544
|
-
// Ensure user is loaded first
|
|
545
601
|
const user = await this.loadCurrentUser();
|
|
546
602
|
if (!user) {
|
|
547
|
-
|
|
603
|
+
this.currentOrganization = null;
|
|
548
604
|
return null;
|
|
549
605
|
}
|
|
550
606
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
// organizations array format: [{ id: 'org123', roles: ['owner'] }, ...]
|
|
554
|
-
const firstOrg = userData.organizations?.[0];
|
|
555
|
-
const orgId = userData.mod?.currentOrgId || (typeof firstOrg === 'object' ? firstOrg?.id : firstOrg);
|
|
556
|
-
|
|
607
|
+
const orgId = readUserCurrentOrgPreference(user);
|
|
608
|
+
|
|
557
609
|
if (!orgId) {
|
|
558
|
-
//console.log('[RbtApi]
|
|
610
|
+
//console.log('[RbtApi] No current organization selected (mod.iac.currentOrgId unset)');
|
|
611
|
+
this.currentOrganization = null;
|
|
612
|
+
if (this.localStorageAdaptor) {
|
|
613
|
+
try {
|
|
614
|
+
await this.localStorageAdaptor.removeItem('currentOrgId');
|
|
615
|
+
} catch (_) {
|
|
616
|
+
/* noop */
|
|
617
|
+
}
|
|
618
|
+
}
|
|
559
619
|
return null;
|
|
560
620
|
}
|
|
561
|
-
|
|
562
|
-
console.log('[RbtApi] Resolved organization ID:', orgId, 'from:', {
|
|
563
|
-
modCurrentOrgId: userData.mod?.currentOrgId,
|
|
564
|
-
firstOrg: firstOrg
|
|
565
|
-
});
|
|
566
621
|
|
|
567
|
-
|
|
622
|
+
if (
|
|
623
|
+
!forceReload &&
|
|
624
|
+
this.currentOrganization &&
|
|
625
|
+
String(this.currentOrganization.id) === orgId
|
|
626
|
+
) {
|
|
627
|
+
return this.currentOrganization;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
console.log('[RbtApi] Resolved organization ID:', orgId);
|
|
631
|
+
|
|
568
632
|
if (this.localStorageAdaptor) {
|
|
569
633
|
const cachedOrgId = await this.localStorageAdaptor.getItem('currentOrgId');
|
|
570
634
|
if (cachedOrgId && cachedOrgId !== orgId) {
|
|
@@ -572,20 +636,16 @@ export default class RbtApi {
|
|
|
572
636
|
}
|
|
573
637
|
}
|
|
574
638
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
const org = await this.get('<@iac.organization>', orgId);
|
|
578
|
-
|
|
639
|
+
const org = await this.load('<@iac.organization>', orgId);
|
|
640
|
+
|
|
579
641
|
if (org) {
|
|
580
|
-
this.currentOrganization = org;
|
|
581
|
-
//console.log('[RbtApi] Current organization loaded:', org.get('name'));
|
|
582
|
-
|
|
583
|
-
// Cache in storage if available
|
|
642
|
+
this.currentOrganization = org;
|
|
584
643
|
if (this.localStorageAdaptor) {
|
|
585
644
|
await this.localStorageAdaptor.setItem('currentOrgId', orgId);
|
|
586
645
|
}
|
|
587
646
|
} else {
|
|
588
647
|
console.warn('[RbtApi] Organization not found or not accessible:', orgId);
|
|
648
|
+
this.currentOrganization = null;
|
|
589
649
|
}
|
|
590
650
|
|
|
591
651
|
return this.currentOrganization;
|
|
@@ -621,7 +681,7 @@ export default class RbtApi {
|
|
|
621
681
|
//console.log('[RbtApi] Switching to organization:', orgId);
|
|
622
682
|
|
|
623
683
|
// Load the new organization
|
|
624
|
-
const org = await this.
|
|
684
|
+
const org = await this.load('<@iac.organization>', orgId);
|
|
625
685
|
if (!org) {
|
|
626
686
|
throw new Error(`Organization ${orgId} not found or not accessible`);
|
|
627
687
|
}
|
|
@@ -631,7 +691,7 @@ export default class RbtApi {
|
|
|
631
691
|
// Save preference to user
|
|
632
692
|
try {
|
|
633
693
|
const userObj = await this.loadUser(this.currentUser.id);
|
|
634
|
-
userObj
|
|
694
|
+
persistUserCurrentOrgPreference(userObj, orgId);
|
|
635
695
|
await userObj.save();
|
|
636
696
|
|
|
637
697
|
// Update cache
|
|
@@ -652,13 +712,17 @@ export default class RbtApi {
|
|
|
652
712
|
}
|
|
653
713
|
|
|
654
714
|
/**
|
|
655
|
-
* Get current organization (null-safe)
|
|
656
|
-
* Returns
|
|
657
|
-
*
|
|
658
|
-
* @returns {RbtObject|null} Current organization or null
|
|
715
|
+
* Get cached current organization (sync, null-safe).
|
|
716
|
+
* Returns **null** unless `mod.iac.currentOrgId` (or legacy `mod.currentOrgId`) matches `currentOrganization`.
|
|
659
717
|
*/
|
|
660
718
|
getCurrentOrganization() {
|
|
661
|
-
|
|
719
|
+
const org = this.currentOrganization;
|
|
720
|
+
if (!org) return null;
|
|
721
|
+
const user = this.currentUser;
|
|
722
|
+
const selected = readUserCurrentOrgPreference(user);
|
|
723
|
+
if (!selected) return null;
|
|
724
|
+
if (String(org.id) !== selected) return null;
|
|
725
|
+
return org;
|
|
662
726
|
}
|
|
663
727
|
|
|
664
728
|
/**
|
|
@@ -683,8 +747,8 @@ export default class RbtApi {
|
|
|
683
747
|
|
|
684
748
|
//console.log('[RbtApi] Selecting organization:', orgId);
|
|
685
749
|
|
|
686
|
-
// Load the organization
|
|
687
|
-
const org = await this.
|
|
750
|
+
// Load the organization via object load (not HTTP `get` — passing orgId as params breaks axios).
|
|
751
|
+
const org = await this.load('<@iac.organization>', orgId);
|
|
688
752
|
if (!org) {
|
|
689
753
|
throw new Error(`Organization ${orgId} not found or not accessible`);
|
|
690
754
|
}
|
|
@@ -709,11 +773,9 @@ export default class RbtApi {
|
|
|
709
773
|
//console.log('[RbtApi] Organization already in user organizations array');
|
|
710
774
|
}
|
|
711
775
|
|
|
712
|
-
// Set
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
user.set('mod', modData);
|
|
716
|
-
|
|
776
|
+
// Set current organization preference under mod.iac (module-namespaced settings)
|
|
777
|
+
persistUserCurrentOrgPreference(user, orgId);
|
|
778
|
+
|
|
717
779
|
// Save user record
|
|
718
780
|
//console.log('[RbtApi] Saving user with organization:', orgId);
|
|
719
781
|
await user.save();
|
|
@@ -956,6 +1018,7 @@ export default class RbtApi {
|
|
|
956
1018
|
* - excludeAttrs: An array of attribute paths to exclude from the response (e.g., ['details.log', 'internalData']).
|
|
957
1019
|
* Excludes the specified paths and all their subpaths.
|
|
958
1020
|
* - resolveReferences: An array of attribute names whose references should be resolved in the returned objects.
|
|
1021
|
+
* - resolveAttrs: Attribute paths whose mod / reference values should be resolved (e.g., ['configs.mod.guide.completion']).
|
|
959
1022
|
* - timeout: A numerical value in milliseconds to set a maximum time limit for the query execution.
|
|
960
1023
|
* - cache: Boolean. When true, enables a 10-second response cache for identical queries. Default: false (no caching).
|
|
961
1024
|
*
|
|
@@ -967,6 +1030,7 @@ export default class RbtApi {
|
|
|
967
1030
|
* requestAttrs: ['id', 'configs.title'],
|
|
968
1031
|
* excludeAttrs: ['details.log'],
|
|
969
1032
|
* resolveReferences: ['translatableContent'],
|
|
1033
|
+
* resolveAttrs: ['configs.mod.guide.completion'],
|
|
970
1034
|
* cache: true // opt-in to 10s response caching
|
|
971
1035
|
* });
|
|
972
1036
|
*
|
|
@@ -980,7 +1044,7 @@ export default class RbtApi {
|
|
|
980
1044
|
//console.log('RBTAPI.query INIT', type, params);
|
|
981
1045
|
|
|
982
1046
|
// Validate parameters - reject invalid parameter names
|
|
983
|
-
const validParams = ['type', 'where', 'orderBy', 'limit', 'resolveReferences', 'requestAttrs', 'excludeAttrs', 'timeout', 'enableRealtime', 'cache'];
|
|
1047
|
+
const validParams = ['type', 'where', 'orderBy', 'limit', 'resolveReferences', 'resolveAttrs', 'requestAttrs', 'excludeAttrs', 'timeout', 'enableRealtime', 'cache'];
|
|
984
1048
|
const invalidParams = Object.keys(params).filter(key => !validParams.includes(key));
|
|
985
1049
|
if (invalidParams.length > 0) {
|
|
986
1050
|
throw new Error(`Invalid query parameter(s): ${invalidParams.join(', ')}. Valid parameters are: ${validParams.join(', ')}`);
|
package/src/rbt_object.js
CHANGED
|
@@ -259,12 +259,54 @@ export default class RbtObject{
|
|
|
259
259
|
return clonedObject;
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Rebuild read_ids/write_ids record meta from iac grant arrays.
|
|
264
|
+
* Keeps denormalized columns in sync with dataJson.iac for delta saves.
|
|
265
|
+
*/
|
|
266
|
+
_syncIacRecordMeta() {
|
|
267
|
+
const iac = _.get(this._data, 'iac');
|
|
268
|
+
if (!iac) return;
|
|
269
|
+
|
|
270
|
+
const readIds = new Set();
|
|
271
|
+
const writeIds = new Set();
|
|
272
|
+
|
|
273
|
+
if (this.type === '<@iac.user>') {
|
|
274
|
+
writeIds.add(this.id);
|
|
275
|
+
readIds.add(this.id);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (iac.creator) {
|
|
279
|
+
writeIds.add(iac.creator);
|
|
280
|
+
readIds.add(iac.creator);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const collect = (grants, target) => {
|
|
284
|
+
if (!grants) return;
|
|
285
|
+
for (const key of ['users', 'userGroups', 'organizations', 'userSegments']) {
|
|
286
|
+
if (Array.isArray(grants[key])) {
|
|
287
|
+
grants[key].forEach((id) => target.add(id));
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
collect(iac.readGrants, readIds);
|
|
292
|
+
collect(iac.writeGrants, writeIds);
|
|
293
|
+
|
|
294
|
+
this._internalData.read_ids = Array.from(readIds).join(',');
|
|
295
|
+
this._internalData.write_ids = Array.from(writeIds).join(',');
|
|
296
|
+
}
|
|
297
|
+
|
|
262
298
|
async save() {
|
|
263
299
|
if (!this._internalData.type) {
|
|
264
300
|
throw new Error('Cannot save object without type');
|
|
265
301
|
}
|
|
266
302
|
|
|
267
303
|
try {
|
|
304
|
+
const iacGrantChange = (this.rpcMeta.changes || []).some(
|
|
305
|
+
(path) => path.startsWith('iac.readGrants') || path.startsWith('iac.writeGrants')
|
|
306
|
+
);
|
|
307
|
+
if (iacGrantChange) {
|
|
308
|
+
this._syncIacRecordMeta();
|
|
309
|
+
}
|
|
268
310
|
|
|
269
311
|
let record;
|
|
270
312
|
if(this.rpcMeta.isNew){
|
|
@@ -399,6 +441,8 @@ export default class RbtObject{
|
|
|
399
441
|
}
|
|
400
442
|
}
|
|
401
443
|
|
|
444
|
+
this._syncIacRecordMeta();
|
|
445
|
+
|
|
402
446
|
// Save if requested
|
|
403
447
|
if (save) {
|
|
404
448
|
return await this.save();
|
|
@@ -558,6 +602,8 @@ export default class RbtObject{
|
|
|
558
602
|
this.set(groupsPath, existingGroups.filter(id => !groupIds.includes(id)));
|
|
559
603
|
}
|
|
560
604
|
|
|
605
|
+
this._syncIacRecordMeta();
|
|
606
|
+
|
|
561
607
|
// Save if requested
|
|
562
608
|
if (save) {
|
|
563
609
|
return await this.save();
|
package/src/version.js
CHANGED