@crimson-education/sdk 0.3.7 → 0.3.8

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.
@@ -1,5 +1,5 @@
1
1
  import type { CrimsonClient } from "./client";
2
- import type { CurrentUserRoles, StudentSummary, UserProfile } from "./types";
2
+ import type { CurrentUserRoles, StudentSummary, UserProfile, LinkedTenantsResponse } from "./types";
3
3
  export declare class AccountApi {
4
4
  private client;
5
5
  constructor(client: CrimsonClient);
@@ -37,4 +37,10 @@ export declare class AccountApi {
37
37
  * @returns User profile information
38
38
  */
39
39
  getMyProfile(): Promise<UserProfile>;
40
+ /**
41
+ * Get the list of tenants linked to the current user
42
+ *
43
+ * @returns List of linked tenants and current tenant info
44
+ */
45
+ getLinkedTenants(): Promise<LinkedTenantsResponse>;
40
46
  }
@@ -62,5 +62,15 @@ class AccountApi {
62
62
  return this.client.fetch("/api/v1/account/me/profile");
63
63
  });
64
64
  }
65
+ /**
66
+ * Get the list of tenants linked to the current user
67
+ *
68
+ * @returns List of linked tenants and current tenant info
69
+ */
70
+ getLinkedTenants() {
71
+ return __awaiter(this, void 0, void 0, function* () {
72
+ return this.client.fetch("/api/v1/account/linked-tenants");
73
+ });
74
+ }
65
75
  }
66
76
  exports.AccountApi = AccountApi;
@@ -19,12 +19,19 @@ export declare class CrimsonClient {
19
19
  private appName;
20
20
  private appTenant;
21
21
  private oauthAdapter?;
22
+ private tenantDomain?;
23
+ private isResolvingDomain;
24
+ private tenantDomainPromise?;
22
25
  constructor(config: CrimsonClientConfig);
23
26
  /**
24
- * Initialize the client (required for OAuth mode)
25
- * Call this after creating the client to load stored tokens
27
+ * Initialize the client (required for OAuth mode and tenant resolution)
28
+ * Call this after creating the client to load stored tokens and resolve tenant
26
29
  */
27
30
  initialize(): Promise<void>;
31
+ /**
32
+ * Resolve the x-tenant-domain dynamically
33
+ */
34
+ resolveTenantDomain(): Promise<void>;
28
35
  /**
29
36
  * Get the OAuth adapter (only available in OAuth mode)
30
37
  */
@@ -45,6 +45,7 @@ function isOAuthConfig(config) {
45
45
  class CrimsonClient {
46
46
  constructor(config) {
47
47
  this.config = config;
48
+ this.isResolvingDomain = false;
48
49
  this.missions = new missions_1.MissionsApi(this);
49
50
  this.tasks = new tasks_1.TasksApi(this);
50
51
  this.roadmap = new roadmap_1.RoadmapApi(this);
@@ -81,14 +82,56 @@ class CrimsonClient {
81
82
  }
82
83
  }
83
84
  /**
84
- * Initialize the client (required for OAuth mode)
85
- * Call this after creating the client to load stored tokens
85
+ * Initialize the client (required for OAuth mode and tenant resolution)
86
+ * Call this after creating the client to load stored tokens and resolve tenant
86
87
  */
87
88
  initialize() {
88
89
  return __awaiter(this, void 0, void 0, function* () {
89
90
  if (this.oauthAdapter) {
90
91
  yield this.oauthAdapter.initialize();
91
92
  }
93
+ yield this.resolveTenantDomain();
94
+ });
95
+ }
96
+ /**
97
+ * Resolve the x-tenant-domain dynamically
98
+ */
99
+ resolveTenantDomain() {
100
+ return __awaiter(this, void 0, void 0, function* () {
101
+ if (this.tenantDomain)
102
+ return;
103
+ if (!this.tenantDomainPromise) {
104
+ this.isResolvingDomain = true;
105
+ this.tenantDomainPromise = (() => __awaiter(this, void 0, void 0, function* () {
106
+ var _a, _b;
107
+ try {
108
+ // A newer version of profile will return domain
109
+ const profile = yield this.account.getMyProfile();
110
+ if ((_a = profile.tenant) === null || _a === void 0 ? void 0 : _a.domain) {
111
+ this.tenantDomain = profile.tenant.domain;
112
+ }
113
+ else {
114
+ // Use getLinkedtenants as a safe fallback when getMyProfile does not return a domain.
115
+ // Can be removed later.
116
+ const linkedRes = yield this.account.getLinkedTenants();
117
+ if (linkedRes.linkedTenants && linkedRes.linkedTenants.length > 0) {
118
+ // find domain.tenantId equals to currentTenantId
119
+ const domain = (_b = linkedRes.linkedTenants.find(t => t.tenantId === linkedRes.currentTenantId)) === null || _b === void 0 ? void 0 : _b.tenantDomain;
120
+ if (domain) {
121
+ this.tenantDomain = domain;
122
+ }
123
+ }
124
+ }
125
+ }
126
+ catch (err) {
127
+ console.warn('[CrimsonSDK] Failed to resolve tenant domain:', err);
128
+ }
129
+ finally {
130
+ this.isResolvingDomain = false;
131
+ }
132
+ }))();
133
+ }
134
+ return this.tenantDomainPromise;
92
135
  });
93
136
  }
94
137
  /**
@@ -192,6 +235,10 @@ class CrimsonClient {
192
235
  }
193
236
  fetch(path_1) {
194
237
  return __awaiter(this, arguments, void 0, function* (path, options = {}) {
238
+ // If it's a roadmap endpoint, wait for tenant domain to be resolved first
239
+ if (path.includes("/roadmap") && this.tenantDomainPromise) {
240
+ yield this.tenantDomainPromise;
241
+ }
195
242
  const token = yield this.getToken();
196
243
  if (!token) {
197
244
  throw new Error("No authentication token available");
@@ -207,6 +254,10 @@ class CrimsonClient {
207
254
  if (this.appTenant) {
208
255
  headers["X-App-Tenant"] = this.appTenant;
209
256
  }
257
+ console.log(`tenantDomain = ${this.tenantDomain}`);
258
+ if (this.tenantDomain) {
259
+ headers["x-tenant-domain"] = this.tenantDomain;
260
+ }
210
261
  const url = `${this.baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
211
262
  const response = yield fetch(url, Object.assign(Object.assign({}, options), { headers }));
212
263
  if (!response.ok) {
@@ -494,8 +494,22 @@ export interface UserProfile {
494
494
  tenant: {
495
495
  id: string;
496
496
  name: string;
497
+ domain?: string;
497
498
  };
498
499
  }
500
+ export interface LinkedTenant {
501
+ tenantId: string;
502
+ tenantName: string;
503
+ tenantDomain: string | null;
504
+ tenantLevel: number;
505
+ isCurrent: boolean;
506
+ userEmail: string;
507
+ }
508
+ export interface LinkedTenantsResponse {
509
+ currentUserId: string;
510
+ currentTenantId: string;
511
+ linkedTenants: LinkedTenant[];
512
+ }
499
513
  /**
500
514
  * Prefilled field with value and source information
501
515
  */
@@ -34,6 +34,10 @@ function CrimsonProvider({ children, apiUrl, allowedParentOrigins, queryClient:
34
34
  appName,
35
35
  appTenant,
36
36
  }), [apiUrl, clientId, appName, appTenant]);
37
+ // trigger initialization, we need to determine tenant domain at first place.
38
+ (0, react_1.useEffect)(() => {
39
+ client.initialize();
40
+ }, [client]);
37
41
  (0, react_1.useEffect)(() => {
38
42
  const origins = allowedParentOrigins || (0, iframe_1.getDefaultAllowedOrigins)();
39
43
  const cleanup = (0, iframe_1.setupIframeListener)(origins);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crimson-education/sdk",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "description": "Crimson SDK for accessing Crimson App APIs",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",