@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.
Files changed (2) hide show
  1. package/package.json +4 -4
  2. 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.19",
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
- "./package.json": "./package.json",
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
+ }