@bagelink/sdk 0.0.19 → 0.0.22
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/package.json +4 -4
- package/src/index.ts +318 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bagelink/sdk",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.22",
|
|
5
5
|
"description": "Bagel core sdk packages",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Neveh Allon",
|
|
@@ -19,8 +19,7 @@
|
|
|
19
19
|
"keywords": [],
|
|
20
20
|
"sideEffects": false,
|
|
21
21
|
"exports": {
|
|
22
|
-
"./
|
|
23
|
-
"./src/index": "./src/index.ts",
|
|
22
|
+
"./src": "./src/index.ts",
|
|
24
23
|
".": {
|
|
25
24
|
"types": "./dist/index.d.ts",
|
|
26
25
|
"require": "./dist/index.cjs",
|
|
@@ -39,7 +38,8 @@
|
|
|
39
38
|
}
|
|
40
39
|
},
|
|
41
40
|
"files": [
|
|
42
|
-
"dist"
|
|
41
|
+
"dist",
|
|
42
|
+
"src"
|
|
43
43
|
],
|
|
44
44
|
"publishConfig": {
|
|
45
45
|
"access": "public"
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import ax from 'axios';
|
|
2
|
+
|
|
3
|
+
export type Tables = '';
|
|
4
|
+
export type TableToTypeMapping = Record<Tables, any>;
|
|
5
|
+
|
|
6
|
+
export interface User {
|
|
7
|
+
id: string
|
|
8
|
+
first_name?: string
|
|
9
|
+
last_name?: string
|
|
10
|
+
email?: string
|
|
11
|
+
password?: string
|
|
12
|
+
is_verified?: boolean
|
|
13
|
+
locale: string
|
|
14
|
+
type: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface UploadOptions {
|
|
18
|
+
onUploadProgress?: (progressEvent: any) => void
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const axios = ax.create({
|
|
22
|
+
// withCredentials to true to send cookies with requests
|
|
23
|
+
withCredentials: true,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
class DataRequest<T extends Tables = Tables> {
|
|
27
|
+
data_table: T | Tables;
|
|
28
|
+
bagel: any;
|
|
29
|
+
itemID: string;
|
|
30
|
+
_filter: Record<string, any> = {};
|
|
31
|
+
constructor(table: T, bagel: Bagel) {
|
|
32
|
+
this.data_table = table;
|
|
33
|
+
this.bagel = bagel;
|
|
34
|
+
this.itemID = '';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async post(item: Record<string, any>): Promise<Record<string, any>> {
|
|
38
|
+
if (!this.data_table)
|
|
39
|
+
throw new Error('Data table not set');
|
|
40
|
+
const { data } = await axios.post(
|
|
41
|
+
`${this.bagel.host}/data/${this.data_table}`,
|
|
42
|
+
item,
|
|
43
|
+
);
|
|
44
|
+
return data;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
filter(filter: Record<string, any>) {
|
|
48
|
+
this._filter = filter;
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async get(): Promise<
|
|
53
|
+
TableToTypeMapping[T] | TableToTypeMapping[T][] | undefined
|
|
54
|
+
> {
|
|
55
|
+
if (!this.data_table)
|
|
56
|
+
throw new Error('Data table not set');
|
|
57
|
+
const filterStr = Object.keys(this._filter).length
|
|
58
|
+
? `?filter={${Object.entries(this._filter)
|
|
59
|
+
.map(([k, v]) => `${k}:${v}`)
|
|
60
|
+
.join(',')}}`
|
|
61
|
+
: '';
|
|
62
|
+
const url = `${this.bagel.host}/data/${this.data_table}${this.itemID ? `/${this.itemID}` : ''
|
|
63
|
+
}${filterStr}`;
|
|
64
|
+
try {
|
|
65
|
+
const { data } = await axios.get(url);
|
|
66
|
+
return data;
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
console.log(err);
|
|
70
|
+
this.bagel.onError?.(err);
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
item(id: string) {
|
|
76
|
+
this.itemID = id;
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async delete(): Promise<TableToTypeMapping[T][]> {
|
|
81
|
+
if (!this.data_table)
|
|
82
|
+
throw new Error('Data table not set');
|
|
83
|
+
const { data } = await axios.delete(
|
|
84
|
+
`${this.bagel.host}/data/${this.data_table}/${this.itemID}`,
|
|
85
|
+
);
|
|
86
|
+
return data;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async put(updatedItem: Record<string, any>): Promise<Record<string, any>> {
|
|
90
|
+
const { data_table, itemID, bagel } = this;
|
|
91
|
+
if (!data_table)
|
|
92
|
+
throw new Error('Data table not set');
|
|
93
|
+
if (!itemID)
|
|
94
|
+
throw new Error('Item ID not set');
|
|
95
|
+
const { data } = await axios.put(
|
|
96
|
+
`${bagel.host}/data/${data_table}/${itemID}`,
|
|
97
|
+
updatedItem,
|
|
98
|
+
);
|
|
99
|
+
return data;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function responses(key: string) {
|
|
104
|
+
const res: Record<string, string> = {
|
|
105
|
+
LOGIN_BAD_CREDENTIALS: 'Invalid username or password',
|
|
106
|
+
RESET_PASSWORD_BAD_TOKEN: 'This reset password link is invalid or expired.',
|
|
107
|
+
};
|
|
108
|
+
return res[key] || key;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
class BagelAuth {
|
|
112
|
+
constructor(private bagel: Bagel) {
|
|
113
|
+
this.bagel = bagel;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
user: User | null = null;
|
|
117
|
+
async validateUser(): Promise<User | null> {
|
|
118
|
+
try {
|
|
119
|
+
const usr = (await this.bagel.get('/users/me')) as User;
|
|
120
|
+
this.user = usr;
|
|
121
|
+
return usr;
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
// this.bagel.onError?.(err);
|
|
125
|
+
console.log(err);
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async resetPassword(ctx: { token: string; password: string }) {
|
|
131
|
+
return this.bagel.post('auth/reset-password', ctx).catch((err) => {
|
|
132
|
+
throw responses(err.response?.data?.detail);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async forgotPassword(email: string) {
|
|
137
|
+
try {
|
|
138
|
+
await this.bagel.post('auth/forgot-password', { email });
|
|
139
|
+
}
|
|
140
|
+
catch (err: any) {
|
|
141
|
+
throw responses(err?.response?.data?.detail);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async login(user: User): Promise<User | null> {
|
|
146
|
+
const formData = new FormData();
|
|
147
|
+
formData.append('username', user?.email || '');
|
|
148
|
+
formData.append('password', user?.password || '');
|
|
149
|
+
try {
|
|
150
|
+
await axios.post(`${this.bagel.host}/auth/cookie/login`, formData, {
|
|
151
|
+
headers: {
|
|
152
|
+
'Content-Type': 'multipart/form-data',
|
|
153
|
+
'withCredentials': true,
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
return this.validateUser();
|
|
157
|
+
}
|
|
158
|
+
catch (err: any) {
|
|
159
|
+
throw responses(err.response?.data?.detail || 'LOGIN_BAD_CREDENTIALS');
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async logout() {
|
|
164
|
+
try {
|
|
165
|
+
await axios.post(`${this.bagel.host}/auth/cookie/logout`);
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
this.bagel.onError?.(err);
|
|
169
|
+
console.log(err);
|
|
170
|
+
}
|
|
171
|
+
this.user = null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async acceptInvite(token: string, user: Record<string, any>) {
|
|
175
|
+
await axios.post(`${this.bagel.host}/auth/accept-invite/${token}`, user);
|
|
176
|
+
await this.login(user as User);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async register(
|
|
180
|
+
user: Record<string, any>,
|
|
181
|
+
errors: Record<string, any>,
|
|
182
|
+
) {
|
|
183
|
+
try {
|
|
184
|
+
await axios.post<User>(`${this.bagel.host}/auth/register`, user);
|
|
185
|
+
this.login(user as User);
|
|
186
|
+
}
|
|
187
|
+
catch (err: any) {
|
|
188
|
+
console.error(err);
|
|
189
|
+
|
|
190
|
+
errors.email = err.response.data.detail;
|
|
191
|
+
|
|
192
|
+
throw err;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
export class Bagel {
|
|
199
|
+
host: string;
|
|
200
|
+
onError?: (err: any) => void;
|
|
201
|
+
constructor({ host, onError }: Record<string, any>) {
|
|
202
|
+
this.host = host;
|
|
203
|
+
this.onError = onError;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
read_table: Tables | null = null;
|
|
207
|
+
data(table: Tables): DataRequest<Tables> {
|
|
208
|
+
return new DataRequest(table, this);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
auth = new BagelAuth(this);
|
|
212
|
+
_endpointCleaner(endpoint: string) {
|
|
213
|
+
const url = `${endpoint.replace(/^\//, '').replace(/\/$/g, '')}`;
|
|
214
|
+
return url;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async api(
|
|
218
|
+
endpoint: string,
|
|
219
|
+
query?: Record<string, string>,
|
|
220
|
+
): Promise<Record<string, any>> {
|
|
221
|
+
if (query) {
|
|
222
|
+
const queryParams = Object.entries(query)
|
|
223
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
224
|
+
.join('&');
|
|
225
|
+
endpoint = `${endpoint}?${queryParams}`;
|
|
226
|
+
}
|
|
227
|
+
return axios.get(`${this.host}/api/${endpoint}`).then(({ data }: { data: any }) => data);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async get<T = Record<string, any>>(
|
|
231
|
+
endpoint: string,
|
|
232
|
+
query?: Record<string, any>,
|
|
233
|
+
): Promise<T> {
|
|
234
|
+
endpoint = this._endpointCleaner(endpoint);
|
|
235
|
+
if (endpoint.match(/undefined|null/))
|
|
236
|
+
throw new Error('Invalid endpoint');
|
|
237
|
+
if (query) {
|
|
238
|
+
const queryParams = Object.entries(query)
|
|
239
|
+
.filter(([_, value]) => !!value)
|
|
240
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
241
|
+
.join('&');
|
|
242
|
+
if (queryParams)
|
|
243
|
+
endpoint = `${endpoint}?${queryParams}`;
|
|
244
|
+
}
|
|
245
|
+
const url = `${this.host}/${endpoint}`;
|
|
246
|
+
return axios
|
|
247
|
+
.get(url)
|
|
248
|
+
.then(({ data }: { data: any }) => data)
|
|
249
|
+
.catch((err: any) => {
|
|
250
|
+
this.onError?.(err);
|
|
251
|
+
throw err;
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async delete(endpoint: string): Promise<Record<string, any>> {
|
|
256
|
+
endpoint = this._endpointCleaner(endpoint);
|
|
257
|
+
return axios
|
|
258
|
+
.delete(`${this.host}/${endpoint}`)
|
|
259
|
+
.then(({ data }: { data: any }) => data)
|
|
260
|
+
.catch((err: any) => {
|
|
261
|
+
this.onError?.(err);
|
|
262
|
+
throw err;
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async put(endpoint: string, payload: any): Promise<Record<string, any>> {
|
|
267
|
+
endpoint = this._endpointCleaner(endpoint);
|
|
268
|
+
return axios
|
|
269
|
+
.put(`${this.host}/${endpoint}`, payload)
|
|
270
|
+
.then(({ data }: { data: any }) => data)
|
|
271
|
+
.catch((err: any) => {
|
|
272
|
+
this.onError?.(err);
|
|
273
|
+
throw err;
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async patch(
|
|
278
|
+
endpoint: string,
|
|
279
|
+
payload: any = {},
|
|
280
|
+
): Promise<Record<string, any>> {
|
|
281
|
+
endpoint = this._endpointCleaner(endpoint);
|
|
282
|
+
return axios
|
|
283
|
+
.patch(`${this.host}/${endpoint}`, payload)
|
|
284
|
+
.then(({ data }: { data: any }) => data)
|
|
285
|
+
.catch((err: any) => {
|
|
286
|
+
this.onError?.(err);
|
|
287
|
+
throw err;
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async post(
|
|
292
|
+
endpoint: string,
|
|
293
|
+
payload: any = {},
|
|
294
|
+
): Promise<Record<string, any>> {
|
|
295
|
+
endpoint = this._endpointCleaner(endpoint);
|
|
296
|
+
return axios
|
|
297
|
+
.post(`${this.host}/${endpoint}`, payload)
|
|
298
|
+
.then(({ data }: { data: any }) => data)
|
|
299
|
+
.catch((err: any) => {
|
|
300
|
+
this.onError?.(err);
|
|
301
|
+
throw err;
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async uploadFile(file: File, options?: UploadOptions) {
|
|
306
|
+
const formData = new FormData();
|
|
307
|
+
formData.append('file', file);
|
|
308
|
+
// get an indication of the progress
|
|
309
|
+
|
|
310
|
+
const { data } = await axios.post(`${this.host}/files/upload`, formData, {
|
|
311
|
+
headers: {
|
|
312
|
+
'Content-Type': 'multipart/form-data',
|
|
313
|
+
},
|
|
314
|
+
onUploadProgress: options?.onUploadProgress,
|
|
315
|
+
});
|
|
316
|
+
return data;
|
|
317
|
+
}
|
|
318
|
+
}
|