@blazeo.com/calendar-client 1.0.24 → 1.0.26
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 +36 -1
- package/dist/index.d.ts +53 -2
- package/dist/index.js +263 -13
- package/dist/index.mjs +249 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,12 +22,26 @@ cd your-app && npm link @blazeo.com/calendar-client
|
|
|
22
22
|
Configure once at app startup, then use models and their methods:
|
|
23
23
|
|
|
24
24
|
```js
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
configure,
|
|
27
|
+
setBaseUrl,
|
|
28
|
+
setConsumer,
|
|
29
|
+
setApiCredentials,
|
|
30
|
+
fetchAccessToken,
|
|
31
|
+
CalendarModel,
|
|
32
|
+
createRootStore,
|
|
33
|
+
} from '@blazeo.com/calendar-client';
|
|
26
34
|
|
|
27
35
|
configure({ baseUrl: 'https://your-appointment-api.example.com' });
|
|
28
36
|
// or: setBaseUrl('https://localhost:7051');
|
|
29
37
|
setConsumer('my-app'); // optional: sent as Consumer header (e.g. for lead source)
|
|
30
38
|
|
|
39
|
+
// API JWT (for Authorized routes such as lead export)
|
|
40
|
+
setApiCredentials('your-api-key', 'your-api-secret');
|
|
41
|
+
await fetchAccessToken();
|
|
42
|
+
// Or set a token you obtained elsewhere:
|
|
43
|
+
// setAccessToken('eyJ...', '2026-05-19T12:00:00Z');
|
|
44
|
+
|
|
31
45
|
// Calendar static methods (no store needed)
|
|
32
46
|
const timezones = await CalendarModel.getTimeZones();
|
|
33
47
|
const calendar = await CalendarModel.get('calendar-guid');
|
|
@@ -38,12 +52,33 @@ const cal = store.addCalendar({ calendarId: 'my-cal', name: 'My Calendar' });
|
|
|
38
52
|
await cal.create(); // POST to backend
|
|
39
53
|
```
|
|
40
54
|
|
|
55
|
+
### API JWT authentication
|
|
56
|
+
|
|
57
|
+
The backend issues JWTs via `POST Api/Auth/Token` (`api_key` + `api_secret`). There is no refresh endpoint; when the token expires (~1 hour), call `fetchAccessToken()` again.
|
|
58
|
+
|
|
59
|
+
| Function | Purpose |
|
|
60
|
+
|----------|---------|
|
|
61
|
+
| `setApiCredentials(apiKey, apiSecret)` | Store credentials for token exchange |
|
|
62
|
+
| `fetchAccessToken()` | Get JWT from API and store on config |
|
|
63
|
+
| `setAccessToken(token, expiresAtUtc?)` | Use a token you already have |
|
|
64
|
+
| `ensureValidAccessToken()` | Fetch only if missing or near expiry |
|
|
65
|
+
| `clearAuth()` | Clear token and credentials |
|
|
66
|
+
|
|
67
|
+
All `reqGet` / `reqPost` calls automatically attach `Authorization: Bearer …` and re-issue the token before requests when credentials are configured.
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
configure({ baseUrl: 'https://your-api', apiKey: 'key', apiSecret: 'secret' });
|
|
71
|
+
await fetchAccessToken();
|
|
72
|
+
await LeadModel.requestExport('company-key');
|
|
73
|
+
```
|
|
74
|
+
|
|
41
75
|
## API overview
|
|
42
76
|
|
|
43
77
|
- **CalendarModel (static):** `get`, `getByCompany`, `getTimeZones`, `getTimeZone`, `getParticipants`, `getMonth`, `getEvents`, etc.
|
|
44
78
|
- **EventModel (instance):** `get`, `create`, `cancel`, `getCancellable`, `getAvailability`, `setReminder`
|
|
45
79
|
- **FlowModel:** Same pattern as Calendar — `FlowModel.create({}, { env })` with no fields; static `get`, `getRaw`, `list`, `createFlow`, `updateFlow`, `delete`, `duplicate`, appearance/embed/public/preview helpers; instance methods mirror those using `flowId` on the snapshot
|
|
46
80
|
- **LeadModel:** `LeadModel.create({}, { env })`; static `get`, `getRaw`, `getByEmail`, `getByCompany`; instance `get`, `getByEmail`, `getByCompany` (uses `leadId` / `email` / `companyKey` on the snapshot)
|
|
81
|
+
- **ParticipantModel:** static `get`, `getByEmail`, `getByIds`, `getAll`, …; instance `getByEmail` uses `email` + `companyKey` on the snapshot (see `GET /participant/getbyemail`)
|
|
47
82
|
- **AuthModel (calendar OAuth / Connect Calendar):** `getCalendarProviders`, `getAuthorizationUrl`, `getAuthorizationStatus`, `openOAuthPopup`, `onCalendarAuthMessage` — see [Calendar authorization flow](#calendar-authorization-direct-ui)
|
|
48
83
|
- **RootStore:** `addCalendar`, `addEvent`
|
|
49
84
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,59 @@
|
|
|
1
1
|
/** @blazeo.com/calendar-client - type declarations */
|
|
2
2
|
|
|
3
|
-
export
|
|
4
|
-
|
|
3
|
+
export type AccessTokenResult = {
|
|
4
|
+
status: string;
|
|
5
|
+
message?: string;
|
|
6
|
+
accessToken?: string;
|
|
7
|
+
expiresAtUtc?: string | null;
|
|
8
|
+
tokenType?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type AuthState = {
|
|
12
|
+
accessToken?: string;
|
|
13
|
+
tokenExpiresAt?: string;
|
|
14
|
+
hasApiCredentials: boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function configure(env: {
|
|
18
|
+
baseUrl?: string;
|
|
19
|
+
consumer?: string;
|
|
20
|
+
fetch?: typeof fetch;
|
|
21
|
+
getDefaultOffset?: () => number;
|
|
22
|
+
accessToken?: string;
|
|
23
|
+
expiresAtUtc?: string;
|
|
24
|
+
tokenExpiresAt?: string;
|
|
25
|
+
expires_at_utc?: string;
|
|
26
|
+
apiKey?: string;
|
|
27
|
+
api_key?: string;
|
|
28
|
+
apiSecret?: string;
|
|
29
|
+
api_secret?: string;
|
|
30
|
+
}): void;
|
|
31
|
+
|
|
32
|
+
export function getConfig(): {
|
|
33
|
+
baseUrl?: string;
|
|
34
|
+
consumer?: string;
|
|
35
|
+
fetch?: typeof fetch;
|
|
36
|
+
getDefaultOffset?: () => number;
|
|
37
|
+
accessToken?: string;
|
|
38
|
+
tokenExpiresAt?: string;
|
|
39
|
+
hasApiCredentials?: boolean;
|
|
40
|
+
} | null;
|
|
41
|
+
|
|
5
42
|
export function setBaseUrl(baseUrl: string): void;
|
|
6
43
|
export function setConsumer(consumer: string): void;
|
|
44
|
+
export function setAccessToken(accessToken: string, expiresAtUtc?: string): void;
|
|
45
|
+
export function clearAccessToken(): void;
|
|
46
|
+
export function setApiCredentials(apiKey: string, apiSecret: string): void;
|
|
47
|
+
export function clearApiCredentials(): void;
|
|
48
|
+
export function clearAuth(): void;
|
|
49
|
+
export function getAuth(): AuthState;
|
|
50
|
+
export function fetchAccessToken(apiKey?: string, apiSecret?: string): Promise<AccessTokenResult>;
|
|
51
|
+
export function ensureValidAccessToken(): Promise<string | undefined>;
|
|
52
|
+
export function requestAccessToken(apiKey: string, apiSecret: string, opts?: { baseUrl?: string; fetch?: typeof fetch }): Promise<AccessTokenResult>;
|
|
53
|
+
export function isAccessTokenExpired(expiresAtUtc?: string | number | Date | null, skewMs?: number): boolean;
|
|
54
|
+
export function buildAuthHeaders(extra?: Record<string, string>): Record<string, string>;
|
|
55
|
+
export const TOKEN_PATH: '/Api/Auth/Token';
|
|
56
|
+
export const DEFAULT_TOKEN_REFRESH_SKEW_MS: number;
|
|
7
57
|
export function getConfigStore(): unknown;
|
|
8
58
|
|
|
9
59
|
export const ConfigModel: unknown;
|
|
@@ -79,6 +129,7 @@ export const CalendarParticipantModel: {
|
|
|
79
129
|
};
|
|
80
130
|
export const ParticipantModel: {
|
|
81
131
|
get(participantId: string): Promise<unknown>;
|
|
132
|
+
getByEmail(email: string, companyKey: string): Promise<unknown>;
|
|
82
133
|
getByIds(participantIds: string[] | string): Promise<unknown[] | null>;
|
|
83
134
|
getAll(companyKey: string): Promise<unknown[] | null>;
|
|
84
135
|
add(payload: object, calendarId?: string): Promise<unknown>;
|
package/dist/index.js
CHANGED
|
@@ -48,14 +48,27 @@ __export(index_exports, {
|
|
|
48
48
|
RecurringFrequency: () => RecurringFrequency,
|
|
49
49
|
RootStore: () => RootStore,
|
|
50
50
|
SettingModel: () => Setting_default,
|
|
51
|
+
TOKEN_PATH: () => TOKEN_PATH,
|
|
51
52
|
TimeFrameModel: () => TimeFrame_default,
|
|
52
53
|
TimeSlotModel: () => TimeSlot_default,
|
|
53
54
|
Unit: () => Unit,
|
|
55
|
+
buildAuthHeaders: () => buildAuthHeaders,
|
|
56
|
+
clearAccessToken: () => clearAccessToken,
|
|
57
|
+
clearApiCredentials: () => clearApiCredentials,
|
|
58
|
+
clearAuth: () => clearAuth,
|
|
54
59
|
configure: () => configure,
|
|
55
60
|
createRootStore: () => createRootStore,
|
|
61
|
+
ensureValidAccessToken: () => ensureValidAccessToken,
|
|
62
|
+
fetchAccessToken: () => fetchAccessToken2,
|
|
63
|
+
getAuth: () => getAuth,
|
|
56
64
|
getConfig: () => getConfig,
|
|
57
65
|
getConfigStore: () => getConfigStore,
|
|
58
|
-
|
|
66
|
+
isAccessTokenExpired: () => isAccessTokenExpired,
|
|
67
|
+
requestAccessToken: () => requestAccessToken,
|
|
68
|
+
setAccessToken: () => setAccessToken,
|
|
69
|
+
setApiCredentials: () => setApiCredentials,
|
|
70
|
+
setBaseUrl: () => setBaseUrl,
|
|
71
|
+
setConsumer: () => setConsumer
|
|
59
72
|
});
|
|
60
73
|
module.exports = __toCommonJS(index_exports);
|
|
61
74
|
|
|
@@ -69,6 +82,10 @@ var ConfigModel = import_mobx_state_tree.types.model("Config", {
|
|
|
69
82
|
consumer: import_mobx_state_tree.types.optional(import_mobx_state_tree.types.string, "")
|
|
70
83
|
}).volatile(() => ({
|
|
71
84
|
fetch: void 0,
|
|
85
|
+
accessToken: void 0,
|
|
86
|
+
tokenExpiresAt: void 0,
|
|
87
|
+
apiKey: void 0,
|
|
88
|
+
apiSecret: void 0,
|
|
72
89
|
getDefaultOffset: () => -(/* @__PURE__ */ new Date()).getTimezoneOffset()
|
|
73
90
|
})).actions((self) => ({
|
|
74
91
|
setBaseUrl(url) {
|
|
@@ -83,19 +100,57 @@ var ConfigModel = import_mobx_state_tree.types.model("Config", {
|
|
|
83
100
|
setGetDefaultOffset(fn) {
|
|
84
101
|
self.getDefaultOffset = fn;
|
|
85
102
|
},
|
|
103
|
+
setAccessToken(token, expiresAtUtc = void 0) {
|
|
104
|
+
self.accessToken = token ? String(token) : void 0;
|
|
105
|
+
self.tokenExpiresAt = expiresAtUtc != null && expiresAtUtc !== "" ? String(expiresAtUtc) : void 0;
|
|
106
|
+
},
|
|
107
|
+
clearAccessToken() {
|
|
108
|
+
self.accessToken = void 0;
|
|
109
|
+
self.tokenExpiresAt = void 0;
|
|
110
|
+
},
|
|
111
|
+
setApiCredentials(apiKey, apiSecret) {
|
|
112
|
+
self.apiKey = apiKey != null ? String(apiKey) : void 0;
|
|
113
|
+
self.apiSecret = apiSecret != null ? String(apiSecret) : void 0;
|
|
114
|
+
},
|
|
115
|
+
clearApiCredentials() {
|
|
116
|
+
self.apiKey = void 0;
|
|
117
|
+
self.apiSecret = void 0;
|
|
118
|
+
},
|
|
119
|
+
clearAuth() {
|
|
120
|
+
self.clearAccessToken();
|
|
121
|
+
self.clearApiCredentials();
|
|
122
|
+
},
|
|
86
123
|
configure(env) {
|
|
87
124
|
if (env.baseUrl != null) self.baseUrl = env.baseUrl;
|
|
88
125
|
if (env.consumer != null) self.consumer = env.consumer;
|
|
89
126
|
if (env.fetch != null) self.fetch = env.fetch;
|
|
90
127
|
if (env.getDefaultOffset != null) self.getDefaultOffset = env.getDefaultOffset;
|
|
128
|
+
if (env.accessToken != null) {
|
|
129
|
+
self.setAccessToken(
|
|
130
|
+
env.accessToken,
|
|
131
|
+
env.expiresAtUtc ?? env.tokenExpiresAt ?? env.expires_at_utc
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
if (env.apiKey != null || env.api_key != null) {
|
|
135
|
+
self.apiKey = String(env.apiKey ?? env.api_key);
|
|
136
|
+
}
|
|
137
|
+
if (env.apiSecret != null || env.api_secret != null) {
|
|
138
|
+
self.apiSecret = String(env.apiSecret ?? env.api_secret);
|
|
139
|
+
}
|
|
91
140
|
}
|
|
92
141
|
})).views((self) => ({
|
|
142
|
+
get hasApiCredentials() {
|
|
143
|
+
return Boolean(self.apiKey && self.apiSecret);
|
|
144
|
+
},
|
|
93
145
|
getEnv() {
|
|
94
146
|
return {
|
|
95
147
|
baseUrl: self.baseUrl || void 0,
|
|
96
148
|
consumer: self.consumer || void 0,
|
|
97
149
|
fetch: self.fetch,
|
|
98
|
-
getDefaultOffset: self.getDefaultOffset
|
|
150
|
+
getDefaultOffset: self.getDefaultOffset,
|
|
151
|
+
accessToken: self.accessToken,
|
|
152
|
+
tokenExpiresAt: self.tokenExpiresAt,
|
|
153
|
+
hasApiCredentials: self.hasApiCredentials
|
|
99
154
|
};
|
|
100
155
|
}
|
|
101
156
|
}));
|
|
@@ -106,6 +161,122 @@ function getConfigStore() {
|
|
|
106
161
|
}
|
|
107
162
|
var ConfigModel_default = ConfigModel;
|
|
108
163
|
|
|
164
|
+
// src/apiAuth.js
|
|
165
|
+
var TOKEN_PATH = "/Api/Auth/Token";
|
|
166
|
+
var DEFAULT_TOKEN_REFRESH_SKEW_MS = 6e4;
|
|
167
|
+
function defaultFetch() {
|
|
168
|
+
if (typeof fetch === "undefined") {
|
|
169
|
+
throw new Error("fetch not available");
|
|
170
|
+
}
|
|
171
|
+
return fetch;
|
|
172
|
+
}
|
|
173
|
+
function pickTokenPayload(data) {
|
|
174
|
+
if (!data || typeof data !== "object") return null;
|
|
175
|
+
const nested = data.data && typeof data.data === "object" ? data.data : data;
|
|
176
|
+
const accessToken = nested.access_token ?? nested.accessToken ?? nested.AccessToken ?? null;
|
|
177
|
+
if (!accessToken) return null;
|
|
178
|
+
const expiresAtUtc = nested.expires_at_utc ?? nested.expiresAtUtc ?? nested.ExpiresAtUtc ?? null;
|
|
179
|
+
return {
|
|
180
|
+
accessToken: String(accessToken),
|
|
181
|
+
expiresAtUtc: expiresAtUtc != null ? String(expiresAtUtc) : null,
|
|
182
|
+
tokenType: nested.token_type ?? nested.tokenType ?? "Bearer"
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function isAccessTokenExpired(expiresAtUtc, skewMs = DEFAULT_TOKEN_REFRESH_SKEW_MS) {
|
|
186
|
+
if (expiresAtUtc == null || expiresAtUtc === "") return false;
|
|
187
|
+
const expMs = new Date(expiresAtUtc).getTime();
|
|
188
|
+
if (Number.isNaN(expMs)) return false;
|
|
189
|
+
return Date.now() >= expMs - skewMs;
|
|
190
|
+
}
|
|
191
|
+
function getAuthState() {
|
|
192
|
+
const store = getConfigStore();
|
|
193
|
+
return {
|
|
194
|
+
accessToken: store.accessToken ?? void 0,
|
|
195
|
+
tokenExpiresAt: store.tokenExpiresAt ?? void 0,
|
|
196
|
+
apiKey: store.apiKey ?? void 0,
|
|
197
|
+
apiSecret: store.apiSecret ?? void 0,
|
|
198
|
+
hasApiCredentials: Boolean(store.apiKey && store.apiSecret)
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
async function requestAccessToken(apiKey, apiSecret, opts = {}) {
|
|
202
|
+
const store = getConfigStore();
|
|
203
|
+
const baseUrl = opts.baseUrl ?? store.baseUrl;
|
|
204
|
+
if (!baseUrl) {
|
|
205
|
+
return { status: "failure", message: "baseUrl required. Call configure({ baseUrl }) first." };
|
|
206
|
+
}
|
|
207
|
+
const key = apiKey != null ? String(apiKey).trim() : "";
|
|
208
|
+
const secret = apiSecret != null ? String(apiSecret).trim() : "";
|
|
209
|
+
if (!key || !secret) {
|
|
210
|
+
return { status: "failure", message: "api_key and api_secret are required" };
|
|
211
|
+
}
|
|
212
|
+
const fetchFn = opts.fetch ?? store.fetch ?? defaultFetch();
|
|
213
|
+
const url = `${String(baseUrl).replace(/\/+$/, "")}${TOKEN_PATH}`;
|
|
214
|
+
const httpRes = await fetchFn(url, {
|
|
215
|
+
method: "POST",
|
|
216
|
+
headers: { "Content-Type": "application/json" },
|
|
217
|
+
body: JSON.stringify({ api_key: key, api_secret: secret })
|
|
218
|
+
});
|
|
219
|
+
const text = await httpRes.text();
|
|
220
|
+
let body;
|
|
221
|
+
try {
|
|
222
|
+
body = JSON.parse(text);
|
|
223
|
+
} catch {
|
|
224
|
+
body = { status: "failure", message: text || httpRes.statusText };
|
|
225
|
+
}
|
|
226
|
+
if (!httpRes.ok && body.status !== "failure") {
|
|
227
|
+
body.status = "failure";
|
|
228
|
+
body.message = body.message ?? `HTTP ${httpRes.status}`;
|
|
229
|
+
}
|
|
230
|
+
if (body.status !== "success") {
|
|
231
|
+
return {
|
|
232
|
+
status: "failure",
|
|
233
|
+
message: body.message ?? "Failed to obtain access token"
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
const parsed = pickTokenPayload(body);
|
|
237
|
+
if (!parsed) {
|
|
238
|
+
return { status: "failure", message: "Token response missing access_token" };
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
status: "success",
|
|
242
|
+
accessToken: parsed.accessToken,
|
|
243
|
+
expiresAtUtc: parsed.expiresAtUtc,
|
|
244
|
+
tokenType: parsed.tokenType
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
async function fetchAccessToken(apiKey, apiSecret) {
|
|
248
|
+
const store = getConfigStore();
|
|
249
|
+
const key = apiKey ?? store.apiKey;
|
|
250
|
+
const secret = apiSecret ?? store.apiSecret;
|
|
251
|
+
const result = await requestAccessToken(key, secret);
|
|
252
|
+
if (result.status === "success") {
|
|
253
|
+
store.setAccessToken(result.accessToken, result.expiresAtUtc);
|
|
254
|
+
}
|
|
255
|
+
return result;
|
|
256
|
+
}
|
|
257
|
+
async function ensureAccessToken() {
|
|
258
|
+
const store = getConfigStore();
|
|
259
|
+
const { accessToken, tokenExpiresAt, apiKey, apiSecret } = getAuthState();
|
|
260
|
+
if (accessToken && !isAccessTokenExpired(tokenExpiresAt)) {
|
|
261
|
+
return accessToken;
|
|
262
|
+
}
|
|
263
|
+
if (apiKey && apiSecret) {
|
|
264
|
+
const result = await fetchAccessToken(apiKey, apiSecret);
|
|
265
|
+
if (result.status === "success") return result.accessToken;
|
|
266
|
+
}
|
|
267
|
+
return accessToken;
|
|
268
|
+
}
|
|
269
|
+
function buildAuthHeaders(extra = {}) {
|
|
270
|
+
const headers = { ...extra };
|
|
271
|
+
const { consumer } = getConfigStore();
|
|
272
|
+
const { accessToken } = getAuthState();
|
|
273
|
+
if (!headers.Consumer && consumer) headers.Consumer = consumer;
|
|
274
|
+
if (!headers.Authorization && accessToken) {
|
|
275
|
+
headers.Authorization = `Bearer ${accessToken}`;
|
|
276
|
+
}
|
|
277
|
+
return headers;
|
|
278
|
+
}
|
|
279
|
+
|
|
109
280
|
// src/config.js
|
|
110
281
|
function configure(env) {
|
|
111
282
|
const store = getConfigStore();
|
|
@@ -119,6 +290,33 @@ function getConfig() {
|
|
|
119
290
|
function setBaseUrl(baseUrl) {
|
|
120
291
|
getConfigStore().setBaseUrl(baseUrl);
|
|
121
292
|
}
|
|
293
|
+
function setConsumer(consumer) {
|
|
294
|
+
getConfigStore().setConsumer(consumer);
|
|
295
|
+
}
|
|
296
|
+
function setAccessToken(accessToken, expiresAtUtc) {
|
|
297
|
+
getConfigStore().setAccessToken(accessToken, expiresAtUtc);
|
|
298
|
+
}
|
|
299
|
+
function clearAccessToken() {
|
|
300
|
+
getConfigStore().clearAccessToken();
|
|
301
|
+
}
|
|
302
|
+
function setApiCredentials(apiKey, apiSecret) {
|
|
303
|
+
getConfigStore().setApiCredentials(apiKey, apiSecret);
|
|
304
|
+
}
|
|
305
|
+
function clearApiCredentials() {
|
|
306
|
+
getConfigStore().clearApiCredentials();
|
|
307
|
+
}
|
|
308
|
+
function clearAuth() {
|
|
309
|
+
getConfigStore().clearAuth();
|
|
310
|
+
}
|
|
311
|
+
function getAuth() {
|
|
312
|
+
return getAuthState();
|
|
313
|
+
}
|
|
314
|
+
function fetchAccessToken2(apiKey, apiSecret) {
|
|
315
|
+
return fetchAccessToken(apiKey, apiSecret);
|
|
316
|
+
}
|
|
317
|
+
function ensureValidAccessToken() {
|
|
318
|
+
return ensureAccessToken();
|
|
319
|
+
}
|
|
122
320
|
|
|
123
321
|
// src/apiRequest.js
|
|
124
322
|
function buildQuery(params) {
|
|
@@ -135,10 +333,18 @@ function buildQuery(params) {
|
|
|
135
333
|
return q ? `?${q}` : "";
|
|
136
334
|
}
|
|
137
335
|
async function request(baseUrl, fetchFn, path, options = {}) {
|
|
138
|
-
const { method = "GET", headers = {}, body, query, skipContentType } = options;
|
|
336
|
+
const { method = "GET", headers = {}, body, query, skipContentType, skipAuth } = options;
|
|
139
337
|
const url = `${String(baseUrl).replace(/\/+$/, "")}${path}${buildQuery(query)}`;
|
|
140
338
|
const reqHeaders = { ...headers };
|
|
141
339
|
if (!skipContentType && typeof body === "string") reqHeaders["Content-Type"] = "application/json";
|
|
340
|
+
if (!skipAuth) {
|
|
341
|
+
const store = getConfigStore();
|
|
342
|
+
if (!reqHeaders["Consumer"] && store.consumer) reqHeaders["Consumer"] = store.consumer;
|
|
343
|
+
const { accessToken } = getAuthState();
|
|
344
|
+
if (!reqHeaders["Authorization"] && accessToken) {
|
|
345
|
+
reqHeaders["Authorization"] = `Bearer ${accessToken}`;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
142
348
|
const res = await fetchFn(url, { method, headers: reqHeaders, body });
|
|
143
349
|
const text = await res.text();
|
|
144
350
|
let data;
|
|
@@ -151,23 +357,35 @@ async function request(baseUrl, fetchFn, path, options = {}) {
|
|
|
151
357
|
data.status = "failure";
|
|
152
358
|
data.message = data.message ?? `HTTP ${res.status}`;
|
|
153
359
|
}
|
|
360
|
+
data._httpStatus = res.status;
|
|
154
361
|
return data;
|
|
155
362
|
}
|
|
156
|
-
function
|
|
363
|
+
function mergeRequestHeaders(env, opts) {
|
|
157
364
|
const headers = { ...opts.headers || {} };
|
|
158
|
-
|
|
365
|
+
const store = getConfigStore();
|
|
366
|
+
if (!headers["Consumer"] && ((env == null ? void 0 : env.consumer) || store.consumer)) {
|
|
367
|
+
headers["Consumer"] = (env == null ? void 0 : env.consumer) || store.consumer;
|
|
368
|
+
}
|
|
369
|
+
const { accessToken } = getAuthState();
|
|
370
|
+
if (!headers["Authorization"] && accessToken) {
|
|
371
|
+
headers["Authorization"] = `Bearer ${accessToken}`;
|
|
372
|
+
}
|
|
159
373
|
return { ...opts, headers };
|
|
160
374
|
}
|
|
375
|
+
async function executeRequest(baseUrl, fetchFn, path, opts) {
|
|
376
|
+
if (!opts.skipAuth) await ensureAccessToken();
|
|
377
|
+
return request(baseUrl, fetchFn, path, mergeRequestHeaders(null, opts));
|
|
378
|
+
}
|
|
161
379
|
function createRequestHelpers(self, getEnv12) {
|
|
162
380
|
const env = () => getEnv12(self);
|
|
163
381
|
const baseUrl = () => env().baseUrl;
|
|
164
|
-
const fetchFn = () => env().fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
382
|
+
const fetchFn = () => env().fetch ?? getConfigStore().fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
165
383
|
throw new Error("fetch not available");
|
|
166
384
|
});
|
|
167
385
|
const req = (path, opts = {}) => {
|
|
168
386
|
const url = baseUrl();
|
|
169
387
|
if (!url) throw new Error("Model env requires baseUrl. Call configure({ baseUrl }) at app startup.");
|
|
170
|
-
return
|
|
388
|
+
return executeRequest(url, fetchFn(), path, opts);
|
|
171
389
|
};
|
|
172
390
|
return {
|
|
173
391
|
req,
|
|
@@ -178,14 +396,14 @@ function createRequestHelpers(self, getEnv12) {
|
|
|
178
396
|
function createRequestHelpersFromEnv(env) {
|
|
179
397
|
const e = env ?? getConfig();
|
|
180
398
|
if (!e) throw new Error("Env required. Pass env to the method or call configure({ baseUrl }) at app startup.");
|
|
181
|
-
const baseUrl = () => e == null ? void 0 : e.baseUrl;
|
|
182
|
-
const fetchFn = () => (e == null ? void 0 : e.fetch) ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
399
|
+
const baseUrl = () => (e == null ? void 0 : e.baseUrl) ?? getConfigStore().baseUrl;
|
|
400
|
+
const fetchFn = () => (e == null ? void 0 : e.fetch) ?? getConfigStore().fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
183
401
|
throw new Error("fetch not available");
|
|
184
402
|
});
|
|
185
403
|
const req = (path, opts = {}) => {
|
|
186
404
|
const url = baseUrl();
|
|
187
405
|
if (!url) throw new Error("Env requires baseUrl. Call configure({ baseUrl }) at app startup.");
|
|
188
|
-
return
|
|
406
|
+
return executeRequest(url, fetchFn(), path, opts);
|
|
189
407
|
};
|
|
190
408
|
return {
|
|
191
409
|
env: e,
|
|
@@ -1300,6 +1518,15 @@ var ParticipantModel = import_mobx_state_tree11.types.model("Participant", {
|
|
|
1300
1518
|
if (res.status === "success" && res.data) (0, import_mobx_state_tree11.applySnapshot)(self, mapFromApi2(res.data));
|
|
1301
1519
|
return res;
|
|
1302
1520
|
},
|
|
1521
|
+
/** GET participant/getbyemail – fetch by companyKey + email on this snapshot */
|
|
1522
|
+
async getByEmail() {
|
|
1523
|
+
const res = await reqGet("/participant/getbyemail", {
|
|
1524
|
+
email: self.email,
|
|
1525
|
+
company_key: self.companyKey
|
|
1526
|
+
});
|
|
1527
|
+
if (res.status === "success" && res.data) (0, import_mobx_state_tree11.applySnapshot)(self, mapFromApi2(res.data));
|
|
1528
|
+
return res;
|
|
1529
|
+
},
|
|
1303
1530
|
/** POST participant/save – save participant (add or update) */
|
|
1304
1531
|
async save() {
|
|
1305
1532
|
const payload = toPayload(self);
|
|
@@ -1415,6 +1642,14 @@ function mapCalendarFromApi2(d) {
|
|
|
1415
1642
|
modifiedOn: pick2("modifiedOn", "ModifiedOn", "modified_on") ?? null
|
|
1416
1643
|
};
|
|
1417
1644
|
}
|
|
1645
|
+
ParticipantModel.getByEmail = async (email, companyKey) => {
|
|
1646
|
+
const { reqGet } = createRequestHelpersFromEnv(getConfig());
|
|
1647
|
+
const res = await reqGet("/participant/getbyemail", { email, company_key: companyKey });
|
|
1648
|
+
if (res.status === "success" && res.data) {
|
|
1649
|
+
return ParticipantModel.create(mapFromApi2(res.data), { env: getConfig() });
|
|
1650
|
+
}
|
|
1651
|
+
return null;
|
|
1652
|
+
};
|
|
1418
1653
|
ParticipantModel.get = async (participantId) => {
|
|
1419
1654
|
const { reqGet } = createRequestHelpersFromEnv(getConfig());
|
|
1420
1655
|
const res = await reqGet("/participant/get", { participant_id: participantId });
|
|
@@ -1593,6 +1828,7 @@ SettingModel.save = async (payload) => {
|
|
|
1593
1828
|
SettingModel.uploadLogo = async (calendarId, file) => {
|
|
1594
1829
|
const cfg = getConfig();
|
|
1595
1830
|
if (!(cfg == null ? void 0 : cfg.baseUrl)) throw new Error("Configure baseUrl before uploadLogo");
|
|
1831
|
+
await ensureAccessToken();
|
|
1596
1832
|
const fetchFn = cfg.fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
1597
1833
|
throw new Error("fetch not available");
|
|
1598
1834
|
});
|
|
@@ -1602,6 +1838,7 @@ SettingModel.uploadLogo = async (calendarId, file) => {
|
|
|
1602
1838
|
formData.append("file", file);
|
|
1603
1839
|
const res = await fetchFn(url, {
|
|
1604
1840
|
method: "POST",
|
|
1841
|
+
headers: buildAuthHeaders(),
|
|
1605
1842
|
body: formData
|
|
1606
1843
|
});
|
|
1607
1844
|
const text = await res.text();
|
|
@@ -1724,6 +1961,7 @@ AssetModel.upload = async (file, opts = {}) => {
|
|
|
1724
1961
|
if (!file) return { status: "failure", message: "file is required" };
|
|
1725
1962
|
const category = opts.category != null ? String(opts.category).trim() : "";
|
|
1726
1963
|
if (!category) return { status: "failure", message: "category is required" };
|
|
1964
|
+
await ensureAccessToken();
|
|
1727
1965
|
const fetchFn = cfg.fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
1728
1966
|
throw new Error("fetch not available");
|
|
1729
1967
|
});
|
|
@@ -1738,8 +1976,7 @@ AssetModel.upload = async (file, opts = {}) => {
|
|
|
1738
1976
|
if (opts.calendarId != null && String(opts.calendarId).trim() !== "") {
|
|
1739
1977
|
formData.append("calendar_id", String(opts.calendarId).trim());
|
|
1740
1978
|
}
|
|
1741
|
-
const headers =
|
|
1742
|
-
if (cfg.consumer) headers.Consumer = cfg.consumer;
|
|
1979
|
+
const headers = buildAuthHeaders();
|
|
1743
1980
|
if (opts.consumer != null && String(opts.consumer).trim() !== "") {
|
|
1744
1981
|
headers.Consumer = String(opts.consumer).trim();
|
|
1745
1982
|
}
|
|
@@ -2796,12 +3033,25 @@ function createRootStore(initialState = {}) {
|
|
|
2796
3033
|
RecurringFrequency,
|
|
2797
3034
|
RootStore,
|
|
2798
3035
|
SettingModel,
|
|
3036
|
+
TOKEN_PATH,
|
|
2799
3037
|
TimeFrameModel,
|
|
2800
3038
|
TimeSlotModel,
|
|
2801
3039
|
Unit,
|
|
3040
|
+
buildAuthHeaders,
|
|
3041
|
+
clearAccessToken,
|
|
3042
|
+
clearApiCredentials,
|
|
3043
|
+
clearAuth,
|
|
2802
3044
|
configure,
|
|
2803
3045
|
createRootStore,
|
|
3046
|
+
ensureValidAccessToken,
|
|
3047
|
+
fetchAccessToken,
|
|
3048
|
+
getAuth,
|
|
2804
3049
|
getConfig,
|
|
2805
3050
|
getConfigStore,
|
|
2806
|
-
|
|
3051
|
+
isAccessTokenExpired,
|
|
3052
|
+
requestAccessToken,
|
|
3053
|
+
setAccessToken,
|
|
3054
|
+
setApiCredentials,
|
|
3055
|
+
setBaseUrl,
|
|
3056
|
+
setConsumer
|
|
2807
3057
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -8,6 +8,10 @@ var ConfigModel = types.model("Config", {
|
|
|
8
8
|
consumer: types.optional(types.string, "")
|
|
9
9
|
}).volatile(() => ({
|
|
10
10
|
fetch: void 0,
|
|
11
|
+
accessToken: void 0,
|
|
12
|
+
tokenExpiresAt: void 0,
|
|
13
|
+
apiKey: void 0,
|
|
14
|
+
apiSecret: void 0,
|
|
11
15
|
getDefaultOffset: () => -(/* @__PURE__ */ new Date()).getTimezoneOffset()
|
|
12
16
|
})).actions((self) => ({
|
|
13
17
|
setBaseUrl(url) {
|
|
@@ -22,19 +26,57 @@ var ConfigModel = types.model("Config", {
|
|
|
22
26
|
setGetDefaultOffset(fn) {
|
|
23
27
|
self.getDefaultOffset = fn;
|
|
24
28
|
},
|
|
29
|
+
setAccessToken(token, expiresAtUtc = void 0) {
|
|
30
|
+
self.accessToken = token ? String(token) : void 0;
|
|
31
|
+
self.tokenExpiresAt = expiresAtUtc != null && expiresAtUtc !== "" ? String(expiresAtUtc) : void 0;
|
|
32
|
+
},
|
|
33
|
+
clearAccessToken() {
|
|
34
|
+
self.accessToken = void 0;
|
|
35
|
+
self.tokenExpiresAt = void 0;
|
|
36
|
+
},
|
|
37
|
+
setApiCredentials(apiKey, apiSecret) {
|
|
38
|
+
self.apiKey = apiKey != null ? String(apiKey) : void 0;
|
|
39
|
+
self.apiSecret = apiSecret != null ? String(apiSecret) : void 0;
|
|
40
|
+
},
|
|
41
|
+
clearApiCredentials() {
|
|
42
|
+
self.apiKey = void 0;
|
|
43
|
+
self.apiSecret = void 0;
|
|
44
|
+
},
|
|
45
|
+
clearAuth() {
|
|
46
|
+
self.clearAccessToken();
|
|
47
|
+
self.clearApiCredentials();
|
|
48
|
+
},
|
|
25
49
|
configure(env) {
|
|
26
50
|
if (env.baseUrl != null) self.baseUrl = env.baseUrl;
|
|
27
51
|
if (env.consumer != null) self.consumer = env.consumer;
|
|
28
52
|
if (env.fetch != null) self.fetch = env.fetch;
|
|
29
53
|
if (env.getDefaultOffset != null) self.getDefaultOffset = env.getDefaultOffset;
|
|
54
|
+
if (env.accessToken != null) {
|
|
55
|
+
self.setAccessToken(
|
|
56
|
+
env.accessToken,
|
|
57
|
+
env.expiresAtUtc ?? env.tokenExpiresAt ?? env.expires_at_utc
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
if (env.apiKey != null || env.api_key != null) {
|
|
61
|
+
self.apiKey = String(env.apiKey ?? env.api_key);
|
|
62
|
+
}
|
|
63
|
+
if (env.apiSecret != null || env.api_secret != null) {
|
|
64
|
+
self.apiSecret = String(env.apiSecret ?? env.api_secret);
|
|
65
|
+
}
|
|
30
66
|
}
|
|
31
67
|
})).views((self) => ({
|
|
68
|
+
get hasApiCredentials() {
|
|
69
|
+
return Boolean(self.apiKey && self.apiSecret);
|
|
70
|
+
},
|
|
32
71
|
getEnv() {
|
|
33
72
|
return {
|
|
34
73
|
baseUrl: self.baseUrl || void 0,
|
|
35
74
|
consumer: self.consumer || void 0,
|
|
36
75
|
fetch: self.fetch,
|
|
37
|
-
getDefaultOffset: self.getDefaultOffset
|
|
76
|
+
getDefaultOffset: self.getDefaultOffset,
|
|
77
|
+
accessToken: self.accessToken,
|
|
78
|
+
tokenExpiresAt: self.tokenExpiresAt,
|
|
79
|
+
hasApiCredentials: self.hasApiCredentials
|
|
38
80
|
};
|
|
39
81
|
}
|
|
40
82
|
}));
|
|
@@ -45,6 +87,122 @@ function getConfigStore() {
|
|
|
45
87
|
}
|
|
46
88
|
var ConfigModel_default = ConfigModel;
|
|
47
89
|
|
|
90
|
+
// src/apiAuth.js
|
|
91
|
+
var TOKEN_PATH = "/Api/Auth/Token";
|
|
92
|
+
var DEFAULT_TOKEN_REFRESH_SKEW_MS = 6e4;
|
|
93
|
+
function defaultFetch() {
|
|
94
|
+
if (typeof fetch === "undefined") {
|
|
95
|
+
throw new Error("fetch not available");
|
|
96
|
+
}
|
|
97
|
+
return fetch;
|
|
98
|
+
}
|
|
99
|
+
function pickTokenPayload(data) {
|
|
100
|
+
if (!data || typeof data !== "object") return null;
|
|
101
|
+
const nested = data.data && typeof data.data === "object" ? data.data : data;
|
|
102
|
+
const accessToken = nested.access_token ?? nested.accessToken ?? nested.AccessToken ?? null;
|
|
103
|
+
if (!accessToken) return null;
|
|
104
|
+
const expiresAtUtc = nested.expires_at_utc ?? nested.expiresAtUtc ?? nested.ExpiresAtUtc ?? null;
|
|
105
|
+
return {
|
|
106
|
+
accessToken: String(accessToken),
|
|
107
|
+
expiresAtUtc: expiresAtUtc != null ? String(expiresAtUtc) : null,
|
|
108
|
+
tokenType: nested.token_type ?? nested.tokenType ?? "Bearer"
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function isAccessTokenExpired(expiresAtUtc, skewMs = DEFAULT_TOKEN_REFRESH_SKEW_MS) {
|
|
112
|
+
if (expiresAtUtc == null || expiresAtUtc === "") return false;
|
|
113
|
+
const expMs = new Date(expiresAtUtc).getTime();
|
|
114
|
+
if (Number.isNaN(expMs)) return false;
|
|
115
|
+
return Date.now() >= expMs - skewMs;
|
|
116
|
+
}
|
|
117
|
+
function getAuthState() {
|
|
118
|
+
const store = getConfigStore();
|
|
119
|
+
return {
|
|
120
|
+
accessToken: store.accessToken ?? void 0,
|
|
121
|
+
tokenExpiresAt: store.tokenExpiresAt ?? void 0,
|
|
122
|
+
apiKey: store.apiKey ?? void 0,
|
|
123
|
+
apiSecret: store.apiSecret ?? void 0,
|
|
124
|
+
hasApiCredentials: Boolean(store.apiKey && store.apiSecret)
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
async function requestAccessToken(apiKey, apiSecret, opts = {}) {
|
|
128
|
+
const store = getConfigStore();
|
|
129
|
+
const baseUrl = opts.baseUrl ?? store.baseUrl;
|
|
130
|
+
if (!baseUrl) {
|
|
131
|
+
return { status: "failure", message: "baseUrl required. Call configure({ baseUrl }) first." };
|
|
132
|
+
}
|
|
133
|
+
const key = apiKey != null ? String(apiKey).trim() : "";
|
|
134
|
+
const secret = apiSecret != null ? String(apiSecret).trim() : "";
|
|
135
|
+
if (!key || !secret) {
|
|
136
|
+
return { status: "failure", message: "api_key and api_secret are required" };
|
|
137
|
+
}
|
|
138
|
+
const fetchFn = opts.fetch ?? store.fetch ?? defaultFetch();
|
|
139
|
+
const url = `${String(baseUrl).replace(/\/+$/, "")}${TOKEN_PATH}`;
|
|
140
|
+
const httpRes = await fetchFn(url, {
|
|
141
|
+
method: "POST",
|
|
142
|
+
headers: { "Content-Type": "application/json" },
|
|
143
|
+
body: JSON.stringify({ api_key: key, api_secret: secret })
|
|
144
|
+
});
|
|
145
|
+
const text = await httpRes.text();
|
|
146
|
+
let body;
|
|
147
|
+
try {
|
|
148
|
+
body = JSON.parse(text);
|
|
149
|
+
} catch {
|
|
150
|
+
body = { status: "failure", message: text || httpRes.statusText };
|
|
151
|
+
}
|
|
152
|
+
if (!httpRes.ok && body.status !== "failure") {
|
|
153
|
+
body.status = "failure";
|
|
154
|
+
body.message = body.message ?? `HTTP ${httpRes.status}`;
|
|
155
|
+
}
|
|
156
|
+
if (body.status !== "success") {
|
|
157
|
+
return {
|
|
158
|
+
status: "failure",
|
|
159
|
+
message: body.message ?? "Failed to obtain access token"
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const parsed = pickTokenPayload(body);
|
|
163
|
+
if (!parsed) {
|
|
164
|
+
return { status: "failure", message: "Token response missing access_token" };
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
status: "success",
|
|
168
|
+
accessToken: parsed.accessToken,
|
|
169
|
+
expiresAtUtc: parsed.expiresAtUtc,
|
|
170
|
+
tokenType: parsed.tokenType
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
async function fetchAccessToken(apiKey, apiSecret) {
|
|
174
|
+
const store = getConfigStore();
|
|
175
|
+
const key = apiKey ?? store.apiKey;
|
|
176
|
+
const secret = apiSecret ?? store.apiSecret;
|
|
177
|
+
const result = await requestAccessToken(key, secret);
|
|
178
|
+
if (result.status === "success") {
|
|
179
|
+
store.setAccessToken(result.accessToken, result.expiresAtUtc);
|
|
180
|
+
}
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
async function ensureAccessToken() {
|
|
184
|
+
const store = getConfigStore();
|
|
185
|
+
const { accessToken, tokenExpiresAt, apiKey, apiSecret } = getAuthState();
|
|
186
|
+
if (accessToken && !isAccessTokenExpired(tokenExpiresAt)) {
|
|
187
|
+
return accessToken;
|
|
188
|
+
}
|
|
189
|
+
if (apiKey && apiSecret) {
|
|
190
|
+
const result = await fetchAccessToken(apiKey, apiSecret);
|
|
191
|
+
if (result.status === "success") return result.accessToken;
|
|
192
|
+
}
|
|
193
|
+
return accessToken;
|
|
194
|
+
}
|
|
195
|
+
function buildAuthHeaders(extra = {}) {
|
|
196
|
+
const headers = { ...extra };
|
|
197
|
+
const { consumer } = getConfigStore();
|
|
198
|
+
const { accessToken } = getAuthState();
|
|
199
|
+
if (!headers.Consumer && consumer) headers.Consumer = consumer;
|
|
200
|
+
if (!headers.Authorization && accessToken) {
|
|
201
|
+
headers.Authorization = `Bearer ${accessToken}`;
|
|
202
|
+
}
|
|
203
|
+
return headers;
|
|
204
|
+
}
|
|
205
|
+
|
|
48
206
|
// src/config.js
|
|
49
207
|
function configure(env) {
|
|
50
208
|
const store = getConfigStore();
|
|
@@ -58,6 +216,33 @@ function getConfig() {
|
|
|
58
216
|
function setBaseUrl(baseUrl) {
|
|
59
217
|
getConfigStore().setBaseUrl(baseUrl);
|
|
60
218
|
}
|
|
219
|
+
function setConsumer(consumer) {
|
|
220
|
+
getConfigStore().setConsumer(consumer);
|
|
221
|
+
}
|
|
222
|
+
function setAccessToken(accessToken, expiresAtUtc) {
|
|
223
|
+
getConfigStore().setAccessToken(accessToken, expiresAtUtc);
|
|
224
|
+
}
|
|
225
|
+
function clearAccessToken() {
|
|
226
|
+
getConfigStore().clearAccessToken();
|
|
227
|
+
}
|
|
228
|
+
function setApiCredentials(apiKey, apiSecret) {
|
|
229
|
+
getConfigStore().setApiCredentials(apiKey, apiSecret);
|
|
230
|
+
}
|
|
231
|
+
function clearApiCredentials() {
|
|
232
|
+
getConfigStore().clearApiCredentials();
|
|
233
|
+
}
|
|
234
|
+
function clearAuth() {
|
|
235
|
+
getConfigStore().clearAuth();
|
|
236
|
+
}
|
|
237
|
+
function getAuth() {
|
|
238
|
+
return getAuthState();
|
|
239
|
+
}
|
|
240
|
+
function fetchAccessToken2(apiKey, apiSecret) {
|
|
241
|
+
return fetchAccessToken(apiKey, apiSecret);
|
|
242
|
+
}
|
|
243
|
+
function ensureValidAccessToken() {
|
|
244
|
+
return ensureAccessToken();
|
|
245
|
+
}
|
|
61
246
|
|
|
62
247
|
// src/apiRequest.js
|
|
63
248
|
function buildQuery(params) {
|
|
@@ -74,10 +259,18 @@ function buildQuery(params) {
|
|
|
74
259
|
return q ? `?${q}` : "";
|
|
75
260
|
}
|
|
76
261
|
async function request(baseUrl, fetchFn, path, options = {}) {
|
|
77
|
-
const { method = "GET", headers = {}, body, query, skipContentType } = options;
|
|
262
|
+
const { method = "GET", headers = {}, body, query, skipContentType, skipAuth } = options;
|
|
78
263
|
const url = `${String(baseUrl).replace(/\/+$/, "")}${path}${buildQuery(query)}`;
|
|
79
264
|
const reqHeaders = { ...headers };
|
|
80
265
|
if (!skipContentType && typeof body === "string") reqHeaders["Content-Type"] = "application/json";
|
|
266
|
+
if (!skipAuth) {
|
|
267
|
+
const store = getConfigStore();
|
|
268
|
+
if (!reqHeaders["Consumer"] && store.consumer) reqHeaders["Consumer"] = store.consumer;
|
|
269
|
+
const { accessToken } = getAuthState();
|
|
270
|
+
if (!reqHeaders["Authorization"] && accessToken) {
|
|
271
|
+
reqHeaders["Authorization"] = `Bearer ${accessToken}`;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
81
274
|
const res = await fetchFn(url, { method, headers: reqHeaders, body });
|
|
82
275
|
const text = await res.text();
|
|
83
276
|
let data;
|
|
@@ -90,23 +283,35 @@ async function request(baseUrl, fetchFn, path, options = {}) {
|
|
|
90
283
|
data.status = "failure";
|
|
91
284
|
data.message = data.message ?? `HTTP ${res.status}`;
|
|
92
285
|
}
|
|
286
|
+
data._httpStatus = res.status;
|
|
93
287
|
return data;
|
|
94
288
|
}
|
|
95
|
-
function
|
|
289
|
+
function mergeRequestHeaders(env, opts) {
|
|
96
290
|
const headers = { ...opts.headers || {} };
|
|
97
|
-
|
|
291
|
+
const store = getConfigStore();
|
|
292
|
+
if (!headers["Consumer"] && ((env == null ? void 0 : env.consumer) || store.consumer)) {
|
|
293
|
+
headers["Consumer"] = (env == null ? void 0 : env.consumer) || store.consumer;
|
|
294
|
+
}
|
|
295
|
+
const { accessToken } = getAuthState();
|
|
296
|
+
if (!headers["Authorization"] && accessToken) {
|
|
297
|
+
headers["Authorization"] = `Bearer ${accessToken}`;
|
|
298
|
+
}
|
|
98
299
|
return { ...opts, headers };
|
|
99
300
|
}
|
|
301
|
+
async function executeRequest(baseUrl, fetchFn, path, opts) {
|
|
302
|
+
if (!opts.skipAuth) await ensureAccessToken();
|
|
303
|
+
return request(baseUrl, fetchFn, path, mergeRequestHeaders(null, opts));
|
|
304
|
+
}
|
|
100
305
|
function createRequestHelpers(self, getEnv12) {
|
|
101
306
|
const env = () => getEnv12(self);
|
|
102
307
|
const baseUrl = () => env().baseUrl;
|
|
103
|
-
const fetchFn = () => env().fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
308
|
+
const fetchFn = () => env().fetch ?? getConfigStore().fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
104
309
|
throw new Error("fetch not available");
|
|
105
310
|
});
|
|
106
311
|
const req = (path, opts = {}) => {
|
|
107
312
|
const url = baseUrl();
|
|
108
313
|
if (!url) throw new Error("Model env requires baseUrl. Call configure({ baseUrl }) at app startup.");
|
|
109
|
-
return
|
|
314
|
+
return executeRequest(url, fetchFn(), path, opts);
|
|
110
315
|
};
|
|
111
316
|
return {
|
|
112
317
|
req,
|
|
@@ -117,14 +322,14 @@ function createRequestHelpers(self, getEnv12) {
|
|
|
117
322
|
function createRequestHelpersFromEnv(env) {
|
|
118
323
|
const e = env ?? getConfig();
|
|
119
324
|
if (!e) throw new Error("Env required. Pass env to the method or call configure({ baseUrl }) at app startup.");
|
|
120
|
-
const baseUrl = () => e == null ? void 0 : e.baseUrl;
|
|
121
|
-
const fetchFn = () => (e == null ? void 0 : e.fetch) ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
325
|
+
const baseUrl = () => (e == null ? void 0 : e.baseUrl) ?? getConfigStore().baseUrl;
|
|
326
|
+
const fetchFn = () => (e == null ? void 0 : e.fetch) ?? getConfigStore().fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
122
327
|
throw new Error("fetch not available");
|
|
123
328
|
});
|
|
124
329
|
const req = (path, opts = {}) => {
|
|
125
330
|
const url = baseUrl();
|
|
126
331
|
if (!url) throw new Error("Env requires baseUrl. Call configure({ baseUrl }) at app startup.");
|
|
127
|
-
return
|
|
332
|
+
return executeRequest(url, fetchFn(), path, opts);
|
|
128
333
|
};
|
|
129
334
|
return {
|
|
130
335
|
env: e,
|
|
@@ -1239,6 +1444,15 @@ var ParticipantModel = types11.model("Participant", {
|
|
|
1239
1444
|
if (res.status === "success" && res.data) applySnapshot3(self, mapFromApi2(res.data));
|
|
1240
1445
|
return res;
|
|
1241
1446
|
},
|
|
1447
|
+
/** GET participant/getbyemail – fetch by companyKey + email on this snapshot */
|
|
1448
|
+
async getByEmail() {
|
|
1449
|
+
const res = await reqGet("/participant/getbyemail", {
|
|
1450
|
+
email: self.email,
|
|
1451
|
+
company_key: self.companyKey
|
|
1452
|
+
});
|
|
1453
|
+
if (res.status === "success" && res.data) applySnapshot3(self, mapFromApi2(res.data));
|
|
1454
|
+
return res;
|
|
1455
|
+
},
|
|
1242
1456
|
/** POST participant/save – save participant (add or update) */
|
|
1243
1457
|
async save() {
|
|
1244
1458
|
const payload = toPayload(self);
|
|
@@ -1354,6 +1568,14 @@ function mapCalendarFromApi2(d) {
|
|
|
1354
1568
|
modifiedOn: pick2("modifiedOn", "ModifiedOn", "modified_on") ?? null
|
|
1355
1569
|
};
|
|
1356
1570
|
}
|
|
1571
|
+
ParticipantModel.getByEmail = async (email, companyKey) => {
|
|
1572
|
+
const { reqGet } = createRequestHelpersFromEnv(getConfig());
|
|
1573
|
+
const res = await reqGet("/participant/getbyemail", { email, company_key: companyKey });
|
|
1574
|
+
if (res.status === "success" && res.data) {
|
|
1575
|
+
return ParticipantModel.create(mapFromApi2(res.data), { env: getConfig() });
|
|
1576
|
+
}
|
|
1577
|
+
return null;
|
|
1578
|
+
};
|
|
1357
1579
|
ParticipantModel.get = async (participantId) => {
|
|
1358
1580
|
const { reqGet } = createRequestHelpersFromEnv(getConfig());
|
|
1359
1581
|
const res = await reqGet("/participant/get", { participant_id: participantId });
|
|
@@ -1532,6 +1754,7 @@ SettingModel.save = async (payload) => {
|
|
|
1532
1754
|
SettingModel.uploadLogo = async (calendarId, file) => {
|
|
1533
1755
|
const cfg = getConfig();
|
|
1534
1756
|
if (!(cfg == null ? void 0 : cfg.baseUrl)) throw new Error("Configure baseUrl before uploadLogo");
|
|
1757
|
+
await ensureAccessToken();
|
|
1535
1758
|
const fetchFn = cfg.fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
1536
1759
|
throw new Error("fetch not available");
|
|
1537
1760
|
});
|
|
@@ -1541,6 +1764,7 @@ SettingModel.uploadLogo = async (calendarId, file) => {
|
|
|
1541
1764
|
formData.append("file", file);
|
|
1542
1765
|
const res = await fetchFn(url, {
|
|
1543
1766
|
method: "POST",
|
|
1767
|
+
headers: buildAuthHeaders(),
|
|
1544
1768
|
body: formData
|
|
1545
1769
|
});
|
|
1546
1770
|
const text = await res.text();
|
|
@@ -1663,6 +1887,7 @@ AssetModel.upload = async (file, opts = {}) => {
|
|
|
1663
1887
|
if (!file) return { status: "failure", message: "file is required" };
|
|
1664
1888
|
const category = opts.category != null ? String(opts.category).trim() : "";
|
|
1665
1889
|
if (!category) return { status: "failure", message: "category is required" };
|
|
1890
|
+
await ensureAccessToken();
|
|
1666
1891
|
const fetchFn = cfg.fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
1667
1892
|
throw new Error("fetch not available");
|
|
1668
1893
|
});
|
|
@@ -1677,8 +1902,7 @@ AssetModel.upload = async (file, opts = {}) => {
|
|
|
1677
1902
|
if (opts.calendarId != null && String(opts.calendarId).trim() !== "") {
|
|
1678
1903
|
formData.append("calendar_id", String(opts.calendarId).trim());
|
|
1679
1904
|
}
|
|
1680
|
-
const headers =
|
|
1681
|
-
if (cfg.consumer) headers.Consumer = cfg.consumer;
|
|
1905
|
+
const headers = buildAuthHeaders();
|
|
1682
1906
|
if (opts.consumer != null && String(opts.consumer).trim() !== "") {
|
|
1683
1907
|
headers.Consumer = String(opts.consumer).trim();
|
|
1684
1908
|
}
|
|
@@ -2734,12 +2958,25 @@ export {
|
|
|
2734
2958
|
RecurringFrequency,
|
|
2735
2959
|
RootStore,
|
|
2736
2960
|
Setting_default as SettingModel,
|
|
2961
|
+
TOKEN_PATH,
|
|
2737
2962
|
TimeFrame_default as TimeFrameModel,
|
|
2738
2963
|
TimeSlot_default as TimeSlotModel,
|
|
2739
2964
|
Unit,
|
|
2965
|
+
buildAuthHeaders,
|
|
2966
|
+
clearAccessToken,
|
|
2967
|
+
clearApiCredentials,
|
|
2968
|
+
clearAuth,
|
|
2740
2969
|
configure,
|
|
2741
2970
|
createRootStore,
|
|
2971
|
+
ensureValidAccessToken,
|
|
2972
|
+
fetchAccessToken2 as fetchAccessToken,
|
|
2973
|
+
getAuth,
|
|
2742
2974
|
getConfig,
|
|
2743
2975
|
getConfigStore,
|
|
2744
|
-
|
|
2976
|
+
isAccessTokenExpired,
|
|
2977
|
+
requestAccessToken,
|
|
2978
|
+
setAccessToken,
|
|
2979
|
+
setApiCredentials,
|
|
2980
|
+
setBaseUrl,
|
|
2981
|
+
setConsumer
|
|
2745
2982
|
};
|