@iblai/data-layer 1.12.107 → 1.13.1
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/dist/index.d.ts +336 -3
- package/dist/index.esm.js +240 -27
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +245 -26
- package/dist/index.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/src/config.d.ts +6 -0
- package/dist/src/features/auth/__tests__/token-refresh.test.d.ts +1 -0
- package/dist/src/features/auth/token-refresh.d.ts +11 -0
- package/dist/src/features/user/__tests__/user-roles.test.d.ts +1 -0
- package/dist/src/features/user/api-slice.d.ts +276 -1
- package/dist/src/features/user/constants.d.ts +6 -0
- package/dist/src/features/user/types.d.ts +44 -0
- package/dist/src/features/user/utils.d.ts +12 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/utils/index.d.ts +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -39144,6 +39144,12 @@ Config.axdUrl = 'https://base.manager.iblai.app';
|
|
|
39144
39144
|
Config.mentorIframeUrl = 'https://mentor.iblai.tech';
|
|
39145
39145
|
Config.studioUrl = 'https://studio.iblai.app';
|
|
39146
39146
|
Config.httpErrorHandlers = {};
|
|
39147
|
+
/**
|
|
39148
|
+
* Optional override for how expired auth tokens are re-minted after a 401.
|
|
39149
|
+
* Resolve `true` once fresh tokens are in storage. Left unset, the data
|
|
39150
|
+
* layer mints them itself from the edX session.
|
|
39151
|
+
*/
|
|
39152
|
+
Config.refreshAuthToken = undefined;
|
|
39147
39153
|
|
|
39148
39154
|
class IblDataLayer {
|
|
39149
39155
|
}
|
|
@@ -39198,6 +39204,133 @@ const iblFakeBaseQuery = react.retry(fakeBaseQueryWithLogging, {
|
|
|
39198
39204
|
},
|
|
39199
39205
|
});
|
|
39200
39206
|
|
|
39207
|
+
const AUTH_ENDPOINTS = {
|
|
39208
|
+
GET_MFE_CONTEXT: {
|
|
39209
|
+
service: exports.SERVICES.LMS,
|
|
39210
|
+
path: () => `/api/mfe_context`,
|
|
39211
|
+
},
|
|
39212
|
+
APPLE_EXCHANGE_TOKEN: {
|
|
39213
|
+
service: exports.SERVICES.LMS,
|
|
39214
|
+
path: () => `/oauth2/exchange_access_token/apple-id/`,
|
|
39215
|
+
},
|
|
39216
|
+
GET_APP_TOKENS: {
|
|
39217
|
+
service: exports.SERVICES.LMS,
|
|
39218
|
+
path: () => '/api/ibl/manager/consolidated-token/proxy/',
|
|
39219
|
+
},
|
|
39220
|
+
REFRESH_JWT_TOKEN: {
|
|
39221
|
+
service: exports.SERVICES.LEGACY_LMS,
|
|
39222
|
+
path: () => '/ibl-auth/request-jwt/',
|
|
39223
|
+
},
|
|
39224
|
+
};
|
|
39225
|
+
const AUTH_REDUCER_PATH = 'authApiSlice';
|
|
39226
|
+
|
|
39227
|
+
/**
|
|
39228
|
+
* Expiry keys are written alongside the tokens so `hasNonExpiredAuthToken()`
|
|
39229
|
+
* and friends don't treat a freshly minted token as stale.
|
|
39230
|
+
*/
|
|
39231
|
+
const AXD_TOKEN_EXPIRES_KEY = 'axd_token_expires';
|
|
39232
|
+
const DM_TOKEN_EXPIRES_KEY = 'dm_token_expires';
|
|
39233
|
+
/** Reads the platform key the tokens should be minted for. */
|
|
39234
|
+
const readPlatformKey = async () => {
|
|
39235
|
+
var _a;
|
|
39236
|
+
const storage = IblDataLayer.storage;
|
|
39237
|
+
if (!storage)
|
|
39238
|
+
return null;
|
|
39239
|
+
const raw = await storage.getItem(STORAGE_KEYS.CURRENT_TENANT);
|
|
39240
|
+
if (raw) {
|
|
39241
|
+
try {
|
|
39242
|
+
const key = (_a = JSON.parse(raw)) === null || _a === void 0 ? void 0 : _a.key;
|
|
39243
|
+
if (key)
|
|
39244
|
+
return key;
|
|
39245
|
+
}
|
|
39246
|
+
catch (_b) {
|
|
39247
|
+
// `current_tenant` is occasionally stored as a bare slug rather than the
|
|
39248
|
+
// serialised tenant object; use it as-is.
|
|
39249
|
+
if (raw.trim())
|
|
39250
|
+
return raw;
|
|
39251
|
+
}
|
|
39252
|
+
}
|
|
39253
|
+
return (await storage.getItem(STORAGE_KEYS.TENANT)) || null;
|
|
39254
|
+
};
|
|
39255
|
+
/**
|
|
39256
|
+
* Mints a fresh set of app tokens from the edX session and writes them to
|
|
39257
|
+
* storage. Deliberately uses `fetch` rather than the data layer's own base
|
|
39258
|
+
* query: routing it through `iblFetchBaseQuery` would re-enter the 401
|
|
39259
|
+
* handling this function exists to serve.
|
|
39260
|
+
*/
|
|
39261
|
+
const defaultRefreshAuthToken = async () => {
|
|
39262
|
+
var _a, _b;
|
|
39263
|
+
const storage = IblDataLayer.storage;
|
|
39264
|
+
if (!storage)
|
|
39265
|
+
return false;
|
|
39266
|
+
const platformKey = await readPlatformKey();
|
|
39267
|
+
if (!platformKey) {
|
|
39268
|
+
console.warn('[token-refresh] No platform key in storage, cannot refresh');
|
|
39269
|
+
return false;
|
|
39270
|
+
}
|
|
39271
|
+
// The consolidated-token endpoint authenticates with the edX JWT, not with
|
|
39272
|
+
// the axd/dm tokens that just expired.
|
|
39273
|
+
const authHeaders = await getHeaders(AUTH_ENDPOINTS.GET_APP_TOKENS.service);
|
|
39274
|
+
if (!(authHeaders === null || authHeaders === void 0 ? void 0 : authHeaders.Authorization)) {
|
|
39275
|
+
console.warn('[token-refresh] No edX token available, cannot refresh');
|
|
39276
|
+
return false;
|
|
39277
|
+
}
|
|
39278
|
+
const formData = new FormData();
|
|
39279
|
+
formData.append('platform_key', platformKey);
|
|
39280
|
+
const url = `${getServiceUrl(AUTH_ENDPOINTS.GET_APP_TOKENS.service)}${AUTH_ENDPOINTS.GET_APP_TOKENS.path()}`;
|
|
39281
|
+
const response = await fetch(url, {
|
|
39282
|
+
method: 'POST',
|
|
39283
|
+
headers: { Authorization: authHeaders.Authorization },
|
|
39284
|
+
body: formData,
|
|
39285
|
+
});
|
|
39286
|
+
if (!response.ok) {
|
|
39287
|
+
console.warn(`[token-refresh] Mint failed with ${response.status}`);
|
|
39288
|
+
return false;
|
|
39289
|
+
}
|
|
39290
|
+
const body = (await response.json());
|
|
39291
|
+
const axdToken = (_a = body === null || body === void 0 ? void 0 : body.data) === null || _a === void 0 ? void 0 : _a.axd_token;
|
|
39292
|
+
const dmToken = (_b = body === null || body === void 0 ? void 0 : body.data) === null || _b === void 0 ? void 0 : _b.dm_token;
|
|
39293
|
+
if (!(axdToken === null || axdToken === void 0 ? void 0 : axdToken.token) || !(dmToken === null || dmToken === void 0 ? void 0 : dmToken.token)) {
|
|
39294
|
+
console.warn('[token-refresh] Mint returned no tokens');
|
|
39295
|
+
return false;
|
|
39296
|
+
}
|
|
39297
|
+
await storage.setItem(STORAGE_KEYS.AXD_TOKEN_KEY, axdToken.token);
|
|
39298
|
+
await storage.setItem(STORAGE_KEYS.DM_TOKEN_KEY, dmToken.token);
|
|
39299
|
+
if (axdToken.expires) {
|
|
39300
|
+
await storage.setItem(AXD_TOKEN_EXPIRES_KEY, axdToken.expires);
|
|
39301
|
+
}
|
|
39302
|
+
if (dmToken.expires) {
|
|
39303
|
+
await storage.setItem(DM_TOKEN_EXPIRES_KEY, dmToken.expires);
|
|
39304
|
+
}
|
|
39305
|
+
return true;
|
|
39306
|
+
};
|
|
39307
|
+
/** The refresh currently in flight, if any. */
|
|
39308
|
+
let refreshInFlight = null;
|
|
39309
|
+
/**
|
|
39310
|
+
* Refreshes the auth tokens, collapsing concurrent callers onto one request.
|
|
39311
|
+
* A page load that fires ten requests in parallel produces ten 401s; without
|
|
39312
|
+
* this they would mint ten token sets and race each other into storage.
|
|
39313
|
+
*
|
|
39314
|
+
* Never rejects — a thrown refresh resolves `false`, which callers read as
|
|
39315
|
+
* "give up and run the registered 401 handler" (i.e. log out).
|
|
39316
|
+
*/
|
|
39317
|
+
const refreshAuthTokenOnce = async () => {
|
|
39318
|
+
var _a;
|
|
39319
|
+
if (!refreshInFlight) {
|
|
39320
|
+
const refresh = (_a = Config.refreshAuthToken) !== null && _a !== void 0 ? _a : defaultRefreshAuthToken;
|
|
39321
|
+
refreshInFlight = Promise.resolve()
|
|
39322
|
+
.then(() => refresh())
|
|
39323
|
+
.catch((error) => {
|
|
39324
|
+
console.error('[token-refresh] Refresh threw:', error);
|
|
39325
|
+
return false;
|
|
39326
|
+
})
|
|
39327
|
+
.finally(() => {
|
|
39328
|
+
refreshInFlight = null;
|
|
39329
|
+
});
|
|
39330
|
+
}
|
|
39331
|
+
return refreshInFlight;
|
|
39332
|
+
};
|
|
39333
|
+
|
|
39201
39334
|
/**
|
|
39202
39335
|
* Get the service URL based on the provided service type.
|
|
39203
39336
|
*/
|
|
@@ -39292,6 +39425,7 @@ const buildEndpointFromService = (service, serviceFn, options) => {
|
|
|
39292
39425
|
async queryFn(args) {
|
|
39293
39426
|
var _a;
|
|
39294
39427
|
let lastError;
|
|
39428
|
+
let refreshAttempted = false;
|
|
39295
39429
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
39296
39430
|
try {
|
|
39297
39431
|
OpenAPI.BASE = getServiceUrl(service);
|
|
@@ -39303,6 +39437,17 @@ const buildEndpointFromService = (service, serviceFn, options) => {
|
|
|
39303
39437
|
catch (err) {
|
|
39304
39438
|
lastError = err;
|
|
39305
39439
|
const status = err === null || err === void 0 ? void 0 : err.status;
|
|
39440
|
+
// An expired token is recoverable: mint a new one and replay the
|
|
39441
|
+
// call (headers are rebuilt at the top of the loop) rather than
|
|
39442
|
+
// dropping straight to the 401 handler, which logs the user out.
|
|
39443
|
+
// One attempt per call, so a token the server keeps rejecting can't
|
|
39444
|
+
// loop.
|
|
39445
|
+
if (status === 401 && !refreshAttempted) {
|
|
39446
|
+
refreshAttempted = true;
|
|
39447
|
+
if (await refreshAuthTokenOnce()) {
|
|
39448
|
+
continue;
|
|
39449
|
+
}
|
|
39450
|
+
}
|
|
39306
39451
|
// Don't retry auth errors, statuses the endpoint expects as an
|
|
39307
39452
|
// empty state, or a spend-cap block (fail fast — no retry storm).
|
|
39308
39453
|
if (QUERYFN_NON_RETRYABLE_STATUSES.includes(status) ||
|
|
@@ -39457,10 +39602,23 @@ const withNonRetryableErrors = (query$1) => async (args, api, extraOptions) => {
|
|
|
39457
39602
|
const iblFetchBaseQuery = async (args, api, extraOptions) => {
|
|
39458
39603
|
var _a, _b, _c;
|
|
39459
39604
|
try {
|
|
39605
|
+
const runQuery = () => {
|
|
39606
|
+
var _a;
|
|
39607
|
+
return query.retry(withNonRetryableErrors(baseQuery(args.service, (_a = args === null || args === void 0 ? void 0 : args.isJson) !== null && _a !== void 0 ? _a : true, args === null || args === void 0 ? void 0 : args.contentType, args === null || args === void 0 ? void 0 : args.skipAuth)), {
|
|
39608
|
+
maxRetries: 3,
|
|
39609
|
+
})(args, api, extraOptions);
|
|
39610
|
+
};
|
|
39460
39611
|
// Executing base query
|
|
39461
|
-
|
|
39462
|
-
|
|
39463
|
-
|
|
39612
|
+
let result = await runQuery();
|
|
39613
|
+
// An expired token is recoverable: mint a new one and replay the request
|
|
39614
|
+
// (`prepareHeaders` re-reads storage per call) rather than dropping
|
|
39615
|
+
// straight to the 401 handler, which logs the user out. One attempt per
|
|
39616
|
+
// request, so a token the server keeps rejecting can't loop.
|
|
39617
|
+
if (((_a = result.error) === null || _a === void 0 ? void 0 : _a.status) === 401 && !(args === null || args === void 0 ? void 0 : args.skipAuth)) {
|
|
39618
|
+
if (await refreshAuthTokenOnce()) {
|
|
39619
|
+
result = await runQuery();
|
|
39620
|
+
}
|
|
39621
|
+
}
|
|
39464
39622
|
// Query completed
|
|
39465
39623
|
if (result.error) {
|
|
39466
39624
|
console.log('[MONETIZATION ERROR HANDLER]: ', { result });
|
|
@@ -39534,6 +39692,7 @@ const buildEndpointFromServiceLegacy = (service, serviceFn, options) => {
|
|
|
39534
39692
|
return {
|
|
39535
39693
|
async queryFn(args) {
|
|
39536
39694
|
let lastError;
|
|
39695
|
+
let refreshAttempted = false;
|
|
39537
39696
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
39538
39697
|
try {
|
|
39539
39698
|
OpenAPI.BASE = getServiceUrl(service);
|
|
@@ -39545,6 +39704,14 @@ const buildEndpointFromServiceLegacy = (service, serviceFn, options) => {
|
|
|
39545
39704
|
catch (err) {
|
|
39546
39705
|
lastError = err;
|
|
39547
39706
|
const status = err === null || err === void 0 ? void 0 : err.status;
|
|
39707
|
+
// See the note in `buildEndpointFromService`: refresh once and
|
|
39708
|
+
// replay before letting a 401 reach the logout handler.
|
|
39709
|
+
if (status === 401 && !refreshAttempted) {
|
|
39710
|
+
refreshAttempted = true;
|
|
39711
|
+
if (await refreshAuthTokenOnce()) {
|
|
39712
|
+
continue;
|
|
39713
|
+
}
|
|
39714
|
+
}
|
|
39548
39715
|
if (QUERYFN_NON_RETRYABLE_STATUSES.includes(status) ||
|
|
39549
39716
|
expectedErrorStatuses.includes(status)) {
|
|
39550
39717
|
break;
|
|
@@ -40229,26 +40396,6 @@ const promptsApiSlice = react.createApi({
|
|
|
40229
40396
|
});
|
|
40230
40397
|
const { useGetPromptCategoriesQuery, useLazyGetPromptCategoriesQuery, useGetPromptsQuery, useLazyGetPromptsQuery, useGetPromptsSearchQuery, useLazyGetPromptsSearchQuery, useCreatePromptMutation, useUpdatePromptMutation, useDeletePromptMutation, useGetGuidedPromptsQuery, useLazyGetGuidedPromptsQuery, } = promptsApiSlice;
|
|
40231
40398
|
|
|
40232
|
-
const AUTH_ENDPOINTS = {
|
|
40233
|
-
GET_MFE_CONTEXT: {
|
|
40234
|
-
service: exports.SERVICES.LMS,
|
|
40235
|
-
path: () => `/api/mfe_context`,
|
|
40236
|
-
},
|
|
40237
|
-
APPLE_EXCHANGE_TOKEN: {
|
|
40238
|
-
service: exports.SERVICES.LMS,
|
|
40239
|
-
path: () => `/oauth2/exchange_access_token/apple-id/`,
|
|
40240
|
-
},
|
|
40241
|
-
GET_APP_TOKENS: {
|
|
40242
|
-
service: exports.SERVICES.LMS,
|
|
40243
|
-
path: () => '/api/ibl/manager/consolidated-token/proxy/',
|
|
40244
|
-
},
|
|
40245
|
-
REFRESH_JWT_TOKEN: {
|
|
40246
|
-
service: exports.SERVICES.LEGACY_LMS,
|
|
40247
|
-
path: () => '/ibl-auth/request-jwt/',
|
|
40248
|
-
},
|
|
40249
|
-
};
|
|
40250
|
-
const AUTH_REDUCER_PATH = 'authApiSlice';
|
|
40251
|
-
|
|
40252
40399
|
const authApiSlice = react.createApi({
|
|
40253
40400
|
reducerPath: AUTH_REDUCER_PATH,
|
|
40254
40401
|
baseQuery: iblFetchBaseQuery,
|
|
@@ -40799,6 +40946,17 @@ const USERS_ENDPOINTS = {
|
|
|
40799
40946
|
service: exports.SERVICES.LMS,
|
|
40800
40947
|
path: () => `/api/ibl/users/manage/roles/`,
|
|
40801
40948
|
},
|
|
40949
|
+
// All roles assigned to a user across every scope (global, org, course).
|
|
40950
|
+
// The same path is served by the CMS/Studio host and, deprecated, by the LMS.
|
|
40951
|
+
// Studio is the default because course-creation roles are only fully
|
|
40952
|
+
// resolvable there; `lmsService` is the fallback for deployments without a
|
|
40953
|
+
// reachable Studio host. Both hosts share one cache tag, so this stays a
|
|
40954
|
+
// single endpoint rather than two.
|
|
40955
|
+
GET_USER_ROLES: {
|
|
40956
|
+
service: exports.SERVICES.STUDIO,
|
|
40957
|
+
lmsService: exports.SERVICES.LMS,
|
|
40958
|
+
path: () => `/api/ibl/users/manage/roles/`,
|
|
40959
|
+
},
|
|
40802
40960
|
UPLOAD_PROFILE_IMAGE: {
|
|
40803
40961
|
service: exports.SERVICES.LMS,
|
|
40804
40962
|
path: (username) => `/api/user/v1/accounts/${username}/image`,
|
|
@@ -40828,6 +40986,7 @@ const USERS_QUERY_KEYS = {
|
|
|
40828
40986
|
GET_USER_METADATA_EDX: () => ['USER_METADATA_EDX'],
|
|
40829
40987
|
RESET_PASSWORD: () => ['RESET_PASSWORD'],
|
|
40830
40988
|
UPDATE_USER_ROLES: () => ['UPDATE_USER_ROLES'],
|
|
40989
|
+
GET_USER_ROLES: () => ['USER_ROLES'],
|
|
40831
40990
|
UPLOAD_PROFILE_IMAGE: () => ['UPLOAD_PROFILE_IMAGE'],
|
|
40832
40991
|
EDX_UPDATE_USER_METADATA: () => ['EDX_UPDATE_USER_METADATA'],
|
|
40833
40992
|
EDX_GET_USER_METADATA: () => ['EDX_GET_USER_METADATA'],
|
|
@@ -40842,6 +41001,7 @@ const userApiSlice = react.createApi({
|
|
|
40842
41001
|
...USERS_QUERY_KEYS.GET_USER_METADATA(),
|
|
40843
41002
|
...USERS_QUERY_KEYS.GET_USER_METADATA_EDX(),
|
|
40844
41003
|
...USERS_QUERY_KEYS.GET_USER_PLATFORM_METADATA(),
|
|
41004
|
+
...USERS_QUERY_KEYS.GET_USER_ROLES(),
|
|
40845
41005
|
featureTags.PLATFORM_USERS,
|
|
40846
41006
|
],
|
|
40847
41007
|
endpoints: (builder) => ({
|
|
@@ -40902,7 +41062,39 @@ const userApiSlice = react.createApi({
|
|
|
40902
41062
|
body: args,
|
|
40903
41063
|
method: 'POST',
|
|
40904
41064
|
}),
|
|
40905
|
-
invalidatesTags: [featureTags.PLATFORM_USERS],
|
|
41065
|
+
invalidatesTags: [featureTags.PLATFORM_USERS, ...USERS_QUERY_KEYS.GET_USER_ROLES()],
|
|
41066
|
+
}),
|
|
41067
|
+
/**
|
|
41068
|
+
* Every role assigned to a user, across all scopes — global, organization
|
|
41069
|
+
* and individual course. A user with no roles returns `[]`.
|
|
41070
|
+
*
|
|
41071
|
+
* At least one of `username`, `email` or `userId` is required (400
|
|
41072
|
+
* otherwise). Non-staff users can only look up their own roles; asking for
|
|
41073
|
+
* someone else's returns 404.
|
|
41074
|
+
*
|
|
41075
|
+
* Defaults to the CMS/Studio host, where course-creation roles are fully
|
|
41076
|
+
* resolvable; pass `useLms` to fall back to the deprecated LMS host.
|
|
41077
|
+
*/
|
|
41078
|
+
getUserRoles: builder.query({
|
|
41079
|
+
query: ({ username, email, userId, useLms }) => {
|
|
41080
|
+
const endpoint = USERS_ENDPOINTS.GET_USER_ROLES;
|
|
41081
|
+
const params = {};
|
|
41082
|
+
if (username) {
|
|
41083
|
+
params.username = username;
|
|
41084
|
+
}
|
|
41085
|
+
if (email) {
|
|
41086
|
+
params.email = email;
|
|
41087
|
+
}
|
|
41088
|
+
if (userId !== undefined) {
|
|
41089
|
+
params.user_id = userId;
|
|
41090
|
+
}
|
|
41091
|
+
return {
|
|
41092
|
+
url: endpoint.path(),
|
|
41093
|
+
service: useLms ? endpoint.lmsService : endpoint.service,
|
|
41094
|
+
params,
|
|
41095
|
+
};
|
|
41096
|
+
},
|
|
41097
|
+
providesTags: USERS_QUERY_KEYS.GET_USER_ROLES(),
|
|
40906
41098
|
}),
|
|
40907
41099
|
getUserMetadataEdx: builder.query({
|
|
40908
41100
|
query: (args) => ({
|
|
@@ -40971,7 +41163,27 @@ const userApiSlice = react.createApi({
|
|
|
40971
41163
|
}),
|
|
40972
41164
|
}),
|
|
40973
41165
|
});
|
|
40974
|
-
const { useGetUserMetadataQuery, useLazyGetUserMetadataQuery, useUpdateUserMetadataMutation, useUpdateUserAccountMutation, useResetPasswordMutation, useUpdateUserRoleMutation, useUploadProfileImageMutation, useRemoveProfileImageMutation, useGetUserMetadataEdxQuery, useLazyGetUserMetadataEdxQuery, useUpdateUserMetadataEdxMutation, useGetUserPlatformMetadataQuery, useLazyGetUserPlatformMetadataQuery, useUpdateUserPlatformMetadataMutation, } = userApiSlice;
|
|
41166
|
+
const { useGetUserMetadataQuery, useLazyGetUserMetadataQuery, useUpdateUserMetadataMutation, useUpdateUserAccountMutation, useResetPasswordMutation, useUpdateUserRoleMutation, useGetUserRolesQuery, useLazyGetUserRolesQuery, useUploadProfileImageMutation, useRemoveProfileImageMutation, useGetUserMetadataEdxQuery, useLazyGetUserMetadataEdxQuery, useUpdateUserMetadataEdxMutation, useGetUserPlatformMetadataQuery, useLazyGetUserPlatformMetadataQuery, useUpdateUserPlatformMetadataMutation, } = userApiSlice;
|
|
41167
|
+
|
|
41168
|
+
/**
|
|
41169
|
+
* Scope a role row applies to, read off its `org`/`course` fields:
|
|
41170
|
+
* both empty → global, `org` only → org, both set → course.
|
|
41171
|
+
*/
|
|
41172
|
+
const getUserRoleScope = (role) => {
|
|
41173
|
+
if (role.course) {
|
|
41174
|
+
return 'course';
|
|
41175
|
+
}
|
|
41176
|
+
if (role.org) {
|
|
41177
|
+
return 'org';
|
|
41178
|
+
}
|
|
41179
|
+
return 'global';
|
|
41180
|
+
};
|
|
41181
|
+
/** Roles that apply platform-wide (e.g. `staff`). */
|
|
41182
|
+
const filterGlobalUserRoles = (roles) => roles.filter((role) => getUserRoleScope(role) === 'global');
|
|
41183
|
+
/** Roles scoped to an organization, optionally narrowed to one org short name. */
|
|
41184
|
+
const filterOrgUserRoles = (roles, org) => roles.filter((role) => getUserRoleScope(role) === 'org' && (org === undefined || role.org === org));
|
|
41185
|
+
/** Roles scoped to a course run, optionally narrowed to one course ID. */
|
|
41186
|
+
const filterCourseUserRoles = (roles, course) => roles.filter((role) => getUserRoleScope(role) === 'course' && (course === undefined || role.course === course));
|
|
40975
41187
|
|
|
40976
41188
|
/**
|
|
40977
41189
|
* Shared fetch for the paginated LTI provider list endpoints. Sends
|
|
@@ -48143,7 +48355,7 @@ const skillsMiddleware = [
|
|
|
48143
48355
|
userChatHistoryApiSlice.middleware,
|
|
48144
48356
|
];
|
|
48145
48357
|
|
|
48146
|
-
const initializeDataLayer = (dmUrl, lmsUrl, legacyLmsUrl, storageService, httpErrorHandler = {}, studioUrl) => {
|
|
48358
|
+
const initializeDataLayer = (dmUrl, lmsUrl, legacyLmsUrl, storageService, httpErrorHandler = {}, studioUrl, refreshAuthToken) => {
|
|
48147
48359
|
IblDataLayer.storage = storageService;
|
|
48148
48360
|
Config.dmUrl = dmUrl;
|
|
48149
48361
|
Config.lmsUrl = lmsUrl;
|
|
@@ -48152,6 +48364,7 @@ const initializeDataLayer = (dmUrl, lmsUrl, legacyLmsUrl, storageService, httpEr
|
|
|
48152
48364
|
if (studioUrl) {
|
|
48153
48365
|
Config.studioUrl = studioUrl;
|
|
48154
48366
|
}
|
|
48367
|
+
Config.refreshAuthToken = refreshAuthToken;
|
|
48155
48368
|
};
|
|
48156
48369
|
|
|
48157
48370
|
exports.AGENT_SKILLS_ENDPOINTS = AGENT_SKILLS_ENDPOINTS;
|
|
@@ -48300,9 +48513,13 @@ exports.evalTotalPages = evalTotalPages;
|
|
|
48300
48513
|
exports.evaluationsApiSlice = evaluationsApiSlice;
|
|
48301
48514
|
exports.fakeBaseQueryWithLogging = fakeBaseQueryWithLogging;
|
|
48302
48515
|
exports.featureTags = featureTags;
|
|
48516
|
+
exports.filterCourseUserRoles = filterCourseUserRoles;
|
|
48517
|
+
exports.filterGlobalUserRoles = filterGlobalUserRoles;
|
|
48518
|
+
exports.filterOrgUserRoles = filterOrgUserRoles;
|
|
48303
48519
|
exports.filterSlashSkills = filterSlashSkills;
|
|
48304
48520
|
exports.getNextRecentMessagesPageParam = getNextRecentMessagesPageParam;
|
|
48305
48521
|
exports.getSpendCapExceededError = getSpendCapExceededError;
|
|
48522
|
+
exports.getUserRoleScope = getUserRoleScope;
|
|
48306
48523
|
exports.gradingApiSlice = gradingApiSlice;
|
|
48307
48524
|
exports.iblFakeBaseQuery = iblFakeBaseQuery;
|
|
48308
48525
|
exports.initializeDataLayer = initializeDataLayer;
|
|
@@ -48764,6 +48981,7 @@ exports.useGetUserProjectDetailsQuery = useGetUserProjectDetailsQuery;
|
|
|
48764
48981
|
exports.useGetUserProjectsQuery = useGetUserProjectsQuery;
|
|
48765
48982
|
exports.useGetUserReportedSkillsQuery = useGetUserReportedSkillsQuery;
|
|
48766
48983
|
exports.useGetUserResumeQuery = useGetUserResumeQuery;
|
|
48984
|
+
exports.useGetUserRolesQuery = useGetUserRolesQuery;
|
|
48767
48985
|
exports.useGetUserSkillsPointsQuery = useGetUserSkillsPointsQuery;
|
|
48768
48986
|
exports.useGetUserSpendCapQuery = useGetUserSpendCapQuery;
|
|
48769
48987
|
exports.useGetUserTenantsQuery = useGetUserTenantsQuery;
|
|
@@ -48973,6 +49191,7 @@ exports.useLazyGetUserProjectDetailsQuery = useLazyGetUserProjectDetailsQuery;
|
|
|
48973
49191
|
exports.useLazyGetUserProjectsQuery = useLazyGetUserProjectsQuery;
|
|
48974
49192
|
exports.useLazyGetUserReportedSkillsQuery = useLazyGetUserReportedSkillsQuery;
|
|
48975
49193
|
exports.useLazyGetUserResumeQuery = useLazyGetUserResumeQuery;
|
|
49194
|
+
exports.useLazyGetUserRolesQuery = useLazyGetUserRolesQuery;
|
|
48976
49195
|
exports.useLazyGetUserSkillsPointsQuery = useLazyGetUserSkillsPointsQuery;
|
|
48977
49196
|
exports.useLazyGetUserSpendCapQuery = useLazyGetUserSpendCapQuery;
|
|
48978
49197
|
exports.useLazyGetUserTenantsQuery = useLazyGetUserTenantsQuery;
|