roboto-js 1.9.5 → 1.9.9
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/.last-build +1 -1
- package/README.md +580 -0
- package/dist/cjs/cookie_storage_adaptor.cjs +22 -7
- package/dist/cjs/index.cjs +156 -79
- package/dist/cjs/rbt_api.cjs +644 -298
- package/dist/cjs/version.cjs +2 -2
- package/dist/cookie_storage_adaptor.js +12 -26
- package/dist/esm/cookie_storage_adaptor.js +22 -7
- package/dist/esm/index.js +156 -79
- package/dist/esm/rbt_api.js +644 -298
- package/dist/esm/version.js +2 -2
- package/dist/index.js +118 -3
- package/dist/version.js +2 -2
- package/package.json +1 -1
- package/src/cookie_storage_adaptor.js +14 -15
- package/src/index.js +19 -0
- package/src/rbt_api.js +245 -0
- package/src/version.js +2 -2
package/dist/esm/version.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
2
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
3
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
1
4
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
5
|
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
3
6
|
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
@@ -232,6 +235,53 @@ var Roboto = /*#__PURE__*/function () {
|
|
|
232
235
|
value: function recordToInstance(recordHash) {
|
|
233
236
|
return new RbtObject(recordHash, this.api.axios);
|
|
234
237
|
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Wraps raw API response data as RbtObject(s)
|
|
241
|
+
* Use this when receiving raw JSON from custom API endpoints (e.g., /api/tasks/*)
|
|
242
|
+
* that return database objects instead of using roboto's built-in query methods.
|
|
243
|
+
*
|
|
244
|
+
* @param {Object|Array|null} data - Raw response data (single object or array)
|
|
245
|
+
* @returns {RbtObject|Array<RbtObject>|null} - Wrapped RbtObject(s) or original value if null/already wrapped
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* // Wrapping a single object
|
|
249
|
+
* const response = await fetch('/api/tasks/boards/123');
|
|
250
|
+
* const data = await response.json();
|
|
251
|
+
* const board = roboto.wrapAsRbtObjects(data.board);
|
|
252
|
+
* board.set('title', 'New Title'); // Now you can use RbtObject methods
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* // Wrapping an array
|
|
256
|
+
* const response = await fetch('/api/tasks/boards');
|
|
257
|
+
* const data = await response.json();
|
|
258
|
+
* const boards = roboto.wrapAsRbtObjects(data.boards);
|
|
259
|
+
* boards.forEach(board => console.log(board.get('title')));
|
|
260
|
+
*/
|
|
261
|
+
}, {
|
|
262
|
+
key: "wrapAsRbtObjects",
|
|
263
|
+
value: function wrapAsRbtObjects(data) {
|
|
264
|
+
var _this2 = this;
|
|
265
|
+
if (!data) return data;
|
|
266
|
+
|
|
267
|
+
// If it's already an RbtObject, return as-is (idempotent)
|
|
268
|
+
if (typeof data.get === 'function') {
|
|
269
|
+
return data;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// If it's an array, wrap each item
|
|
273
|
+
if (Array.isArray(data)) {
|
|
274
|
+
return data.map(function (item) {
|
|
275
|
+
if (typeof (item === null || item === void 0 ? void 0 : item.get) === 'function') {
|
|
276
|
+
return item;
|
|
277
|
+
}
|
|
278
|
+
return new RbtObject(item, _this2.api.axios);
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Single object - wrap it
|
|
283
|
+
return new RbtObject(data, this.api.axios);
|
|
284
|
+
}
|
|
235
285
|
}, {
|
|
236
286
|
key: "_stripHttpsForDomains",
|
|
237
287
|
value: function _stripHttpsForDomains(baseUrl, domains) {
|
|
@@ -585,17 +635,77 @@ var Roboto = /*#__PURE__*/function () {
|
|
|
585
635
|
return _pollTaskProgress.apply(this, arguments);
|
|
586
636
|
}
|
|
587
637
|
return pollTaskProgress;
|
|
588
|
-
}()
|
|
638
|
+
}()
|
|
639
|
+
/**
|
|
640
|
+
* Helper to detect if an object looks like a doctree object
|
|
641
|
+
* @private
|
|
642
|
+
*/
|
|
643
|
+
}, {
|
|
644
|
+
key: "_isDoctreeObject",
|
|
645
|
+
value: function _isDoctreeObject(obj) {
|
|
646
|
+
if (!obj || _typeof(obj) !== 'object') return false;
|
|
647
|
+
// Check for standard doctree fields
|
|
648
|
+
return obj.hasOwnProperty('id') && obj.hasOwnProperty('type') && obj.hasOwnProperty('timeCreated');
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Auto-wraps response data if it contains doctree objects
|
|
653
|
+
* @private
|
|
654
|
+
*/
|
|
655
|
+
}, {
|
|
656
|
+
key: "_autoWrapResponse",
|
|
657
|
+
value: function _autoWrapResponse(data) {
|
|
658
|
+
if (!data) return data;
|
|
659
|
+
|
|
660
|
+
// If response has an 'items' array (common pattern), wrap those
|
|
661
|
+
if (data.items && Array.isArray(data.items)) {
|
|
662
|
+
if (data.items.length > 0 && this._isDoctreeObject(data.items[0])) {
|
|
663
|
+
return _objectSpread(_objectSpread({}, data), {}, {
|
|
664
|
+
items: this.wrapAsRbtObjects(data.items)
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// If response has a single object property that looks like doctree
|
|
670
|
+
// Check common property names: board, ticket, task, note, etc.
|
|
671
|
+
for (var _i = 0, _Object$keys = Object.keys(data); _i < _Object$keys.length; _i++) {
|
|
672
|
+
var key = _Object$keys[_i];
|
|
673
|
+
if (this._isDoctreeObject(data[key])) {
|
|
674
|
+
return _objectSpread(_objectSpread({}, data), {}, _defineProperty({}, key, this.wrapAsRbtObjects(data[key])));
|
|
675
|
+
}
|
|
676
|
+
// Check if it's an array of doctree objects
|
|
677
|
+
if (Array.isArray(data[key]) && data[key].length > 0 && this._isDoctreeObject(data[key][0])) {
|
|
678
|
+
return _objectSpread(_objectSpread({}, data), {}, _defineProperty({}, key, this.wrapAsRbtObjects(data[key])));
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// If the root data itself is a doctree object or array of them
|
|
683
|
+
if (this._isDoctreeObject(data)) {
|
|
684
|
+
return this.wrapAsRbtObjects(data);
|
|
685
|
+
}
|
|
686
|
+
if (Array.isArray(data) && data.length > 0 && this._isDoctreeObject(data[0])) {
|
|
687
|
+
return this.wrapAsRbtObjects(data);
|
|
688
|
+
}
|
|
689
|
+
return data;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
//
|
|
589
693
|
// Get/Post to endpoint with Roboto authtoken
|
|
694
|
+
// Automatically wraps doctree objects in responses as RbtObjects
|
|
590
695
|
//
|
|
591
696
|
}, {
|
|
592
697
|
key: "get",
|
|
593
698
|
value: function () {
|
|
594
699
|
var _get = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee18(endpoint, params) {
|
|
700
|
+
var result;
|
|
595
701
|
return _regenerator().w(function (_context18) {
|
|
596
702
|
while (1) switch (_context18.n) {
|
|
597
703
|
case 0:
|
|
598
|
-
|
|
704
|
+
_context18.n = 1;
|
|
705
|
+
return this.api.get(endpoint, params);
|
|
706
|
+
case 1:
|
|
707
|
+
result = _context18.v;
|
|
708
|
+
return _context18.a(2, this._autoWrapResponse(result));
|
|
599
709
|
}
|
|
600
710
|
}, _callee18, this);
|
|
601
711
|
}));
|
|
@@ -608,10 +718,15 @@ var Roboto = /*#__PURE__*/function () {
|
|
|
608
718
|
key: "post",
|
|
609
719
|
value: function () {
|
|
610
720
|
var _post = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee19(endpoint, data) {
|
|
721
|
+
var result;
|
|
611
722
|
return _regenerator().w(function (_context19) {
|
|
612
723
|
while (1) switch (_context19.n) {
|
|
613
724
|
case 0:
|
|
614
|
-
|
|
725
|
+
_context19.n = 1;
|
|
726
|
+
return this.api.post(endpoint, data);
|
|
727
|
+
case 1:
|
|
728
|
+
result = _context19.v;
|
|
729
|
+
return _context19.a(2, this._autoWrapResponse(result));
|
|
615
730
|
}
|
|
616
731
|
}, _callee19, this);
|
|
617
732
|
}));
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -25,7 +25,7 @@ export default class CookieStorageAdaptor {
|
|
|
25
25
|
|
|
26
26
|
...options
|
|
27
27
|
}
|
|
28
|
-
|
|
28
|
+
/*
|
|
29
29
|
console.log('[CookieStorageAdaptor] Initialized with options:', {
|
|
30
30
|
secure: this.options.secure,
|
|
31
31
|
sameSite: this.options.sameSite,
|
|
@@ -34,7 +34,7 @@ export default class CookieStorageAdaptor {
|
|
|
34
34
|
domain: this.options.domain,
|
|
35
35
|
prefix: this.options.prefix,
|
|
36
36
|
serverAccessKeys: this.options.serverAccessKeys
|
|
37
|
-
})
|
|
37
|
+
})*/
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
@@ -42,7 +42,6 @@ export default class CookieStorageAdaptor {
|
|
|
42
42
|
*/
|
|
43
43
|
async getItem(key) {
|
|
44
44
|
if (typeof document === 'undefined') {
|
|
45
|
-
console.log(`[CookieStorageAdaptor] getItem(${key}): document undefined, returning null`)
|
|
46
45
|
return null
|
|
47
46
|
}
|
|
48
47
|
|
|
@@ -53,28 +52,28 @@ export default class CookieStorageAdaptor {
|
|
|
53
52
|
const decodedCookie = decodeURIComponent(document.cookie)
|
|
54
53
|
const cookies = decodedCookie.split(';')
|
|
55
54
|
|
|
56
|
-
console.log(`[CookieStorageAdaptor] getItem(${key}): looking for cookie "${cookieName}" ${usePrefix ? '(prefixed)' : '(server-accessible)'}`)
|
|
57
|
-
console.log(`[CookieStorageAdaptor] Available cookies:`, cookies.map(c => c.trim().split('=')[0]).join(', '))
|
|
55
|
+
//console.log(`[CookieStorageAdaptor] getItem(${key}): looking for cookie "${cookieName}" ${usePrefix ? '(prefixed)' : '(server-accessible)'}`)
|
|
56
|
+
//console.log(`[CookieStorageAdaptor] Available cookies:`, cookies.map(c => c.trim().split('=')[0]).join(', '))
|
|
58
57
|
|
|
59
58
|
for (let cookie of cookies) {
|
|
60
59
|
cookie = cookie.trim()
|
|
61
60
|
if (cookie.indexOf(name) === 0) {
|
|
62
61
|
const rawValue = cookie.substring(name.length, cookie.length)
|
|
63
|
-
console.log(`[CookieStorageAdaptor] Found cookie "${cookieName}" with raw value:`, rawValue)
|
|
62
|
+
//console.log(`[CookieStorageAdaptor] Found cookie "${cookieName}" with raw value:`, rawValue)
|
|
64
63
|
|
|
65
64
|
// Handle JSON values (like rbtUser)
|
|
66
65
|
try {
|
|
67
66
|
const parsedValue = JSON.parse(rawValue)
|
|
68
|
-
console.log(`[CookieStorageAdaptor] getItem(${key}): returning parsed JSON:`, parsedValue)
|
|
67
|
+
//console.log(`[CookieStorageAdaptor] getItem(${key}): returning parsed JSON:`, parsedValue)
|
|
69
68
|
return parsedValue
|
|
70
69
|
} catch {
|
|
71
|
-
console.log(`[CookieStorageAdaptor] getItem(${key}): returning string value:`, rawValue)
|
|
70
|
+
//console.log(`[CookieStorageAdaptor] getItem(${key}): returning string value:`, rawValue)
|
|
72
71
|
return rawValue
|
|
73
72
|
}
|
|
74
73
|
}
|
|
75
74
|
}
|
|
76
75
|
|
|
77
|
-
|
|
76
|
+
//.log(`[CookieStorageAdaptor] getItem(${key}): cookie "${cookieName}" not found, returning null`)
|
|
78
77
|
return null
|
|
79
78
|
}
|
|
80
79
|
|
|
@@ -94,9 +93,9 @@ export default class CookieStorageAdaptor {
|
|
|
94
93
|
// Stringify objects/arrays like localStorage does
|
|
95
94
|
const cookieValue = typeof value === 'object' ? JSON.stringify(value) : String(value)
|
|
96
95
|
|
|
97
|
-
console.log(`[CookieStorageAdaptor] setItem(${key}): storing as "${cookieName}" ${usePrefix ? '(prefixed)' : '(server-accessible)'}`)
|
|
98
|
-
console.log(`[CookieStorageAdaptor] Original value:`, value)
|
|
99
|
-
console.log(`[CookieStorageAdaptor] Cookie value:`, cookieValue)
|
|
96
|
+
//console.log(`[CookieStorageAdaptor] setItem(${key}): storing as "${cookieName}" ${usePrefix ? '(prefixed)' : '(server-accessible)'}`)
|
|
97
|
+
//console.log(`[CookieStorageAdaptor] Original value:`, value)
|
|
98
|
+
//console.log(`[CookieStorageAdaptor] Cookie value:`, cookieValue)
|
|
100
99
|
|
|
101
100
|
// Build cookie string with security options
|
|
102
101
|
const secureFlag = this.options.secure ? '; Secure' : ''
|
|
@@ -105,7 +104,7 @@ export default class CookieStorageAdaptor {
|
|
|
105
104
|
|
|
106
105
|
const cookieString = `${cookieName}=${encodeURIComponent(cookieValue)}; path=${this.options.path}; max-age=${this.options.maxAge}; SameSite=${this.options.sameSite}${secureFlag}${domainFlag}${httpOnlyFlag}`
|
|
107
106
|
|
|
108
|
-
console.log(`[CookieStorageAdaptor] Full cookie string:`, cookieString)
|
|
107
|
+
//console.log(`[CookieStorageAdaptor] Full cookie string:`, cookieString)
|
|
109
108
|
|
|
110
109
|
document.cookie = cookieString
|
|
111
110
|
|
|
@@ -131,7 +130,7 @@ export default class CookieStorageAdaptor {
|
|
|
131
130
|
const usePrefix = !this.options.serverAccessKeys.includes(key)
|
|
132
131
|
const cookieName = usePrefix ? this.options.prefix + key : key
|
|
133
132
|
|
|
134
|
-
console.log(`[CookieStorageAdaptor] removeItem(${key}): removing cookie "${cookieName}" ${usePrefix ? '(prefixed)' : '(server-accessible)'}`)
|
|
133
|
+
//console.log(`[CookieStorageAdaptor] removeItem(${key}): removing cookie "${cookieName}" ${usePrefix ? '(prefixed)' : '(server-accessible)'}`)
|
|
135
134
|
|
|
136
135
|
// Check if cookie exists before removal
|
|
137
136
|
const existingValue = await this.getItem(key)
|
|
@@ -145,7 +144,7 @@ export default class CookieStorageAdaptor {
|
|
|
145
144
|
const domainFlag = this.options.domain ? `; Domain=${this.options.domain}` : ''
|
|
146
145
|
|
|
147
146
|
const removalString = `${cookieName}=; path=${this.options.path}; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=${this.options.sameSite}${secureFlag}${domainFlag}`
|
|
148
|
-
console.log(`[CookieStorageAdaptor] Removal cookie string:`, removalString)
|
|
147
|
+
//console.log(`[CookieStorageAdaptor] Removal cookie string:`, removalString)
|
|
149
148
|
|
|
150
149
|
document.cookie = removalString
|
|
151
150
|
|
package/src/index.js
CHANGED
|
@@ -299,6 +299,25 @@ export default class Roboto{
|
|
|
299
299
|
return this.api.confirmUserEmail(params);
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
+
//
|
|
303
|
+
// Organization management
|
|
304
|
+
//
|
|
305
|
+
async loadCurrentOrganization(forceReload = false){
|
|
306
|
+
return this.api.loadCurrentOrganization(forceReload);
|
|
307
|
+
}
|
|
308
|
+
async switchOrganization(orgId){
|
|
309
|
+
return this.api.switchOrganization(orgId);
|
|
310
|
+
}
|
|
311
|
+
async selectCurrentOrganization(orgId, role = 'owner'){
|
|
312
|
+
return this.api.selectCurrentOrganization(orgId, role);
|
|
313
|
+
}
|
|
314
|
+
getCurrentOrganization(){
|
|
315
|
+
return this.api.getCurrentOrganization();
|
|
316
|
+
}
|
|
317
|
+
get currentOrganization(){
|
|
318
|
+
return this.api.currentOrganization;
|
|
319
|
+
}
|
|
320
|
+
|
|
302
321
|
//
|
|
303
322
|
// create and upload files
|
|
304
323
|
//
|
package/src/rbt_api.js
CHANGED
|
@@ -55,6 +55,8 @@ export default class RbtApi {
|
|
|
55
55
|
this.requestCache = {};
|
|
56
56
|
this._loadCurrentUserPromise = null;
|
|
57
57
|
this._loadCurrentUserExtendedPromise = null;
|
|
58
|
+
this.currentOrganization = null;
|
|
59
|
+
this._loadCurrentOrgPromise = null;
|
|
58
60
|
|
|
59
61
|
// Use the storageAdaptor to get the authToken, if available
|
|
60
62
|
this.initAuthToken(authtoken);
|
|
@@ -320,6 +322,14 @@ export default class RbtApi {
|
|
|
320
322
|
// authtoken is automatically stored for server-side access by the adapter
|
|
321
323
|
}
|
|
322
324
|
|
|
325
|
+
// Optionally load organization after login (can be disabled with loadOrganization: false)
|
|
326
|
+
if (params.loadOrganization !== false) {
|
|
327
|
+
// Kick off org load asynchronously but don't wait for it
|
|
328
|
+
this.loadCurrentOrganization().catch(err =>
|
|
329
|
+
console.warn('[RbtApi] Failed to load organization on login:', err)
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
323
333
|
return response.data;
|
|
324
334
|
|
|
325
335
|
} catch (e) {
|
|
@@ -348,6 +358,11 @@ export default class RbtApi {
|
|
|
348
358
|
if(this.localStorageAdaptor){
|
|
349
359
|
await this.localStorageAdaptor.setItem('authtoken', response.authToken);
|
|
350
360
|
}
|
|
361
|
+
|
|
362
|
+
// Load organization after OAuth login
|
|
363
|
+
this.loadCurrentOrganization().catch(err =>
|
|
364
|
+
console.warn('[RbtApi] Failed to load organization on OAuth login:', err)
|
|
365
|
+
);
|
|
351
366
|
|
|
352
367
|
return response;
|
|
353
368
|
|
|
@@ -371,6 +386,7 @@ export default class RbtApi {
|
|
|
371
386
|
// Clear the iac_session and remove the auth token from axios headers
|
|
372
387
|
this.iac_session = null;
|
|
373
388
|
this.currentUser = null;
|
|
389
|
+
this.currentOrganization = null;
|
|
374
390
|
this.authtoken = null;
|
|
375
391
|
if (this.axios.defaults.headers.common['authtoken']) {
|
|
376
392
|
delete this.axios.defaults.headers.common['authtoken'];
|
|
@@ -379,6 +395,8 @@ export default class RbtApi {
|
|
|
379
395
|
// Clear localStorage if it's being used
|
|
380
396
|
if (this.localStorageAdaptor) {
|
|
381
397
|
await this.localStorageAdaptor.removeItem('authtoken');
|
|
398
|
+
await this.localStorageAdaptor.removeItem('rbtUser');
|
|
399
|
+
await this.localStorageAdaptor.removeItem('currentOrgId');
|
|
382
400
|
}
|
|
383
401
|
|
|
384
402
|
// Return some kind of success response or the response from the server
|
|
@@ -494,6 +512,233 @@ export default class RbtApi {
|
|
|
494
512
|
|
|
495
513
|
}
|
|
496
514
|
|
|
515
|
+
/**
|
|
516
|
+
* Load current organization for the authenticated user
|
|
517
|
+
* Organization is determined by:
|
|
518
|
+
* 1. User's mod.currentOrgId preference
|
|
519
|
+
* 2. First organization in user.organizations array
|
|
520
|
+
* 3. null if user has no organizations
|
|
521
|
+
*
|
|
522
|
+
* @param {boolean} forceReload - Force reload from server even if cached
|
|
523
|
+
* @returns {Promise<RbtObject|null>} Organization object or null
|
|
524
|
+
*/
|
|
525
|
+
async loadCurrentOrganization(forceReload = false) {
|
|
526
|
+
try {
|
|
527
|
+
// Return cached if available and not forcing reload
|
|
528
|
+
if (this.currentOrganization && !forceReload) {
|
|
529
|
+
console.log('[RbtApi] Returning cached currentOrganization:', this.currentOrganization.get('name'));
|
|
530
|
+
return this.currentOrganization;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// Prevent duplicate concurrent loads
|
|
534
|
+
if (this._loadCurrentOrgPromise) {
|
|
535
|
+
console.log('[RbtApi] Organization load already in progress');
|
|
536
|
+
return this._loadCurrentOrgPromise;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
this._loadCurrentOrgPromise = (async () => {
|
|
540
|
+
try {
|
|
541
|
+
// Ensure user is loaded first
|
|
542
|
+
const user = await this.loadCurrentUser();
|
|
543
|
+
if (!user) {
|
|
544
|
+
console.log('[RbtApi] No current user, cannot load organization');
|
|
545
|
+
return null;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Get organization ID from user preferences or first org
|
|
549
|
+
const userData = user.getData();
|
|
550
|
+
// organizations array format: [{ id: 'org123', roles: ['owner'] }, ...]
|
|
551
|
+
const firstOrg = userData.organizations?.[0];
|
|
552
|
+
const orgId = userData.mod?.currentOrgId || (typeof firstOrg === 'object' ? firstOrg?.id : firstOrg);
|
|
553
|
+
|
|
554
|
+
if (!orgId) {
|
|
555
|
+
console.log('[RbtApi] User has no organizations');
|
|
556
|
+
return null;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
console.log('[RbtApi] Resolved organization ID:', orgId, 'from:', {
|
|
560
|
+
modCurrentOrgId: userData.mod?.currentOrgId,
|
|
561
|
+
firstOrg: firstOrg
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
// Check if we have a cached org ID and it matches
|
|
565
|
+
if (this.localStorageAdaptor) {
|
|
566
|
+
const cachedOrgId = await this.localStorageAdaptor.getItem('currentOrgId');
|
|
567
|
+
if (cachedOrgId && cachedOrgId !== orgId) {
|
|
568
|
+
console.log('[RbtApi] Organization changed from cache:', cachedOrgId, '→', orgId);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Load the organization object
|
|
573
|
+
console.log('[RbtApi] Loading organization:', orgId);
|
|
574
|
+
const org = await this.get('<@iac.organization>', orgId);
|
|
575
|
+
|
|
576
|
+
if (org) {
|
|
577
|
+
this.currentOrganization = org; // Already an RbtObject
|
|
578
|
+
console.log('[RbtApi] Current organization loaded:', org.get('name'));
|
|
579
|
+
|
|
580
|
+
// Cache in storage if available
|
|
581
|
+
if (this.localStorageAdaptor) {
|
|
582
|
+
await this.localStorageAdaptor.setItem('currentOrgId', orgId);
|
|
583
|
+
}
|
|
584
|
+
} else {
|
|
585
|
+
console.warn('[RbtApi] Organization not found or not accessible:', orgId);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
return this.currentOrganization;
|
|
589
|
+
} catch (error) {
|
|
590
|
+
console.error('[RbtApi] Failed to load current organization:', error);
|
|
591
|
+
this.currentOrganization = null;
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
})();
|
|
595
|
+
|
|
596
|
+
const result = await this._loadCurrentOrgPromise;
|
|
597
|
+
this._loadCurrentOrgPromise = null;
|
|
598
|
+
return result;
|
|
599
|
+
} catch (error) {
|
|
600
|
+
this._loadCurrentOrgPromise = null;
|
|
601
|
+
return this._handleError(error);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Switch to a different organization
|
|
607
|
+
* Updates user preference and loads the new organization
|
|
608
|
+
*
|
|
609
|
+
* @param {string} orgId - Organization ID to switch to
|
|
610
|
+
* @returns {Promise<RbtObject>} The new current organization
|
|
611
|
+
*/
|
|
612
|
+
async switchOrganization(orgId) {
|
|
613
|
+
try {
|
|
614
|
+
if (!this.currentUser) {
|
|
615
|
+
throw new Error('Must be logged in to switch organization');
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
console.log('[RbtApi] Switching to organization:', orgId);
|
|
619
|
+
|
|
620
|
+
// Load the new organization
|
|
621
|
+
const org = await this.get('<@iac.organization>', orgId);
|
|
622
|
+
if (!org) {
|
|
623
|
+
throw new Error(`Organization ${orgId} not found or not accessible`);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
this.currentOrganization = org;
|
|
627
|
+
|
|
628
|
+
// Save preference to user
|
|
629
|
+
try {
|
|
630
|
+
const userObj = await this.loadUser(this.currentUser.id);
|
|
631
|
+
userObj.set('mod.currentOrgId', orgId);
|
|
632
|
+
await userObj.save();
|
|
633
|
+
|
|
634
|
+
// Update cache
|
|
635
|
+
if (this.localStorageAdaptor) {
|
|
636
|
+
await this.localStorageAdaptor.setItem('currentOrgId', orgId);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
console.log('[RbtApi] Switched to organization:', org.get('name'));
|
|
640
|
+
} catch (error) {
|
|
641
|
+
console.warn('[RbtApi] Failed to save organization preference:', error);
|
|
642
|
+
// Don't throw - switch succeeded even if preference save failed
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
return org;
|
|
646
|
+
} catch (error) {
|
|
647
|
+
return this._handleError(error);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Get current organization (null-safe)
|
|
653
|
+
* Returns cached organization without triggering a load
|
|
654
|
+
*
|
|
655
|
+
* @returns {RbtObject|null} Current organization or null
|
|
656
|
+
*/
|
|
657
|
+
getCurrentOrganization() {
|
|
658
|
+
return this.currentOrganization;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Select and link an organization to the current user
|
|
663
|
+
* This adds the organization to the user's organizations array (if not already there)
|
|
664
|
+
* and sets it as the current organization preference
|
|
665
|
+
*
|
|
666
|
+
* @param {string} orgId - Organization ID to select
|
|
667
|
+
* @param {string} role - Role for the user in this organization (default: 'owner')
|
|
668
|
+
* @returns {Promise<RbtObject>} The selected organization
|
|
669
|
+
*/
|
|
670
|
+
async selectCurrentOrganization(orgId, role = 'owner') {
|
|
671
|
+
try {
|
|
672
|
+
// Ensure we have a current user
|
|
673
|
+
if (!this.currentUser) {
|
|
674
|
+
console.log('[RbtApi] Loading current user for organization selection...');
|
|
675
|
+
await this.loadCurrentUser();
|
|
676
|
+
if (!this.currentUser) {
|
|
677
|
+
throw new Error('Must be logged in to select organization');
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
console.log('[RbtApi] Selecting organization:', orgId);
|
|
682
|
+
|
|
683
|
+
// Load the organization to verify access
|
|
684
|
+
const org = await this.get('<@iac.organization>', orgId);
|
|
685
|
+
if (!org) {
|
|
686
|
+
throw new Error(`Organization ${orgId} not found or not accessible`);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Load the user object using loadUser to get a saveable instance
|
|
690
|
+
const user = await this.loadUser(this.currentUser.id);
|
|
691
|
+
if (!user) {
|
|
692
|
+
throw new Error('Failed to load user object');
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
console.log('[RbtApi] User loaded:', user.id);
|
|
696
|
+
|
|
697
|
+
// Update user's organizations array
|
|
698
|
+
const userOrgs = user.get('organizations') || [];
|
|
699
|
+
const orgExists = userOrgs.some(o => o?.id === orgId);
|
|
700
|
+
|
|
701
|
+
if (!orgExists) {
|
|
702
|
+
console.log('[RbtApi] Adding organization to user organizations array');
|
|
703
|
+
userOrgs.unshift({ id: orgId, roles: [role] });
|
|
704
|
+
user.set('organizations', userOrgs);
|
|
705
|
+
} else {
|
|
706
|
+
console.log('[RbtApi] Organization already in user organizations array');
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// Set as current organization preference - handle both nested path formats
|
|
710
|
+
const modData = user.get('mod') || {};
|
|
711
|
+
modData.currentOrgId = orgId;
|
|
712
|
+
user.set('mod', modData);
|
|
713
|
+
|
|
714
|
+
// Save user record
|
|
715
|
+
console.log('[RbtApi] Saving user with organization:', orgId);
|
|
716
|
+
await user.save();
|
|
717
|
+
console.log('[RbtApi] User saved successfully');
|
|
718
|
+
|
|
719
|
+
// Clear cached organization to force reload
|
|
720
|
+
this.currentOrganization = null;
|
|
721
|
+
|
|
722
|
+
// Update the current user reference
|
|
723
|
+
this.currentUser = user;
|
|
724
|
+
|
|
725
|
+
// Set the new current organization
|
|
726
|
+
this.currentOrganization = org;
|
|
727
|
+
|
|
728
|
+
// Update cache
|
|
729
|
+
if (this.localStorageAdaptor) {
|
|
730
|
+
await this.localStorageAdaptor.setItem('currentOrgId', orgId);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
console.log('[RbtApi] Selected organization:', org.get('name'));
|
|
734
|
+
|
|
735
|
+
return org;
|
|
736
|
+
} catch (error) {
|
|
737
|
+
console.error('[RbtApi] Error in selectCurrentOrganization:', error);
|
|
738
|
+
return this._handleError(error);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
497
742
|
|
|
498
743
|
async loadUser(userId){
|
|
499
744
|
|
package/src/version.js
CHANGED