@smess/networking 1.0.22 → 1.0.24
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/scripts/dev-test.d.ts +1 -0
- package/dist/scripts/dev-test.js +803 -0
- package/dist/scripts/real-test.d.ts +1 -0
- package/dist/scripts/real-test.js +341 -0
- package/dist/services/customer.service.js +7 -0
- package/dist/src/core/axios.d.ts +51 -0
- package/dist/src/core/axios.js +65 -0
- package/dist/src/core/axios.observable.d.ts +11 -0
- package/dist/src/core/axios.observable.js +58 -0
- package/dist/src/core/base.provider.advanced.test.d.ts +1 -0
- package/dist/src/core/base.provider.advanced.test.js +29 -0
- package/dist/src/core/base.provider.d.ts +15 -0
- package/dist/src/core/base.provider.js +13 -0
- package/dist/src/core/base.provider.test.d.ts +1 -0
- package/dist/src/core/base.provider.test.js +36 -0
- package/dist/src/core/base.service.wrapper.advanced.test.d.ts +1 -0
- package/dist/src/core/base.service.wrapper.advanced.test.js +39 -0
- package/dist/src/core/base.service.wrapper.d.ts +7 -0
- package/dist/src/core/base.service.wrapper.js +11 -0
- package/dist/src/core/base.service.wrapper.test.d.ts +1 -0
- package/dist/src/core/base.service.wrapper.test.js +25 -0
- package/dist/src/core/rx.http.service.d.ts +51 -0
- package/dist/src/core/rx.http.service.js +65 -0
- package/dist/src/index.d.ts +17 -0
- package/dist/src/index.js +21 -0
- package/dist/src/services/auth.service.d.ts +65 -0
- package/dist/src/services/auth.service.js +197 -0
- package/dist/src/services/auth.service.test.d.ts +1 -0
- package/dist/src/services/auth.service.test.js +334 -0
- package/dist/src/services/cache.service.d.ts +31 -0
- package/dist/src/services/cache.service.js +77 -0
- package/dist/src/services/cache.service.test.d.ts +1 -0
- package/dist/src/services/cache.service.test.js +88 -0
- package/dist/src/services/customer.service.d.ts +71 -0
- package/dist/src/services/customer.service.js +114 -0
- package/dist/src/services/customer.service.test.d.ts +1 -0
- package/dist/src/services/customer.service.test.js +274 -0
- package/dist/src/services/employee.service.d.ts +73 -0
- package/dist/src/services/employee.service.js +73 -0
- package/dist/src/services/employee.service.test.d.ts +1 -0
- package/dist/src/services/employee.service.test.js +120 -0
- package/dist/src/services/logger.service.d.ts +31 -0
- package/dist/src/services/logger.service.js +93 -0
- package/dist/src/services/logger.service.test.d.ts +1 -0
- package/dist/src/services/logger.service.test.js +31 -0
- package/dist/src/services/order.service.d.ts +73 -0
- package/dist/src/services/order.service.js +97 -0
- package/dist/src/services/order.service.test.d.ts +1 -0
- package/dist/src/services/order.service.test.js +163 -0
- package/dist/src/services/product.service.d.ts +21 -0
- package/dist/src/services/product.service.js +42 -0
- package/dist/src/services/product.service.test.d.ts +1 -0
- package/dist/src/services/product.service.test.js +61 -0
- package/dist/src/services/smess.service.d.ts +36 -0
- package/dist/src/services/smess.service.js +128 -0
- package/dist/src/services/smess.service.test.d.ts +1 -0
- package/dist/src/services/smess.service.test.js +103 -0
- package/dist/src/services/store.cart.service.d.ts +158 -0
- package/dist/src/services/store.cart.service.js +177 -0
- package/dist/src/services/store.cart.service.test.d.ts +1 -0
- package/dist/src/services/store.cart.service.test.js +394 -0
- package/dist/src/services/store.product.service.d.ts +123 -0
- package/dist/src/services/store.product.service.js +102 -0
- package/dist/src/services/store.product.service.test.d.ts +1 -0
- package/dist/src/services/store.product.service.test.js +319 -0
- package/dist/src/services/store.service.d.ts +79 -0
- package/dist/src/services/store.service.js +235 -0
- package/dist/src/services/store.service.test.d.ts +1 -0
- package/dist/src/services/store.service.test.js +274 -0
- package/dist/src/services/user.service.d.ts +16 -0
- package/dist/src/services/user.service.js +37 -0
- package/dist/src/services/user.service.test.d.ts +1 -0
- package/dist/src/services/user.service.test.js +52 -0
- package/dist/src/types/types.d.ts +93 -0
- package/dist/src/types/types.js +82 -0
- package/dist/src/types/types.test.d.ts +1 -0
- package/dist/src/types/types.test.js +25 -0
- package/dist/src/utils/cache-import.d.ts +3 -0
- package/dist/src/utils/cache-import.js +2 -0
- package/dist/src/utils/networking-import.d.ts +4 -0
- package/dist/src/utils/networking-import.js +3 -0
- package/dist/src/utils/rxjs-import.d.ts +5 -0
- package/dist/src/utils/rxjs-import.js +5 -0
- package/package.json +1 -1
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import { AuthService, } from "./auth.service";
|
|
5
|
+
import { NetworkingUIEventWrapped, AlertModel, } from "../types/types";
|
|
6
|
+
import { of } from "rxjs";
|
|
7
|
+
describe("AuthService", () => {
|
|
8
|
+
const config = {
|
|
9
|
+
baseUrl: "https://api.example.com",
|
|
10
|
+
isProd: false,
|
|
11
|
+
clientId: "test-client-id",
|
|
12
|
+
apiKey: "test-api-key",
|
|
13
|
+
};
|
|
14
|
+
let service;
|
|
15
|
+
let mockLocalStorage;
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
// Mock localStorage for all tests
|
|
18
|
+
mockLocalStorage = {
|
|
19
|
+
setItem: jest.fn(),
|
|
20
|
+
getItem: jest.fn(),
|
|
21
|
+
removeItem: jest.fn(),
|
|
22
|
+
};
|
|
23
|
+
Object.defineProperty(window, 'localStorage', {
|
|
24
|
+
value: mockLocalStorage,
|
|
25
|
+
writable: true,
|
|
26
|
+
});
|
|
27
|
+
service = new AuthService(config);
|
|
28
|
+
});
|
|
29
|
+
it("login success", async () => {
|
|
30
|
+
const payload = { identifier: "user", password: "pass" };
|
|
31
|
+
const response = {
|
|
32
|
+
token_type: "Bearer",
|
|
33
|
+
expires_in: 3600,
|
|
34
|
+
token: "token",
|
|
35
|
+
refresh_token: "refresh",
|
|
36
|
+
};
|
|
37
|
+
jest.spyOn(service, "provider").mockReturnValue({
|
|
38
|
+
request: () => of(NetworkingUIEventWrapped.succeeded(response)),
|
|
39
|
+
});
|
|
40
|
+
const result = await service.login(payload).toPromise();
|
|
41
|
+
expect(result?.hasSuccess).toBe(true);
|
|
42
|
+
expect(result?.response).toEqual(response);
|
|
43
|
+
// Verify that auth token is set after successful login
|
|
44
|
+
expect(service.currentAuthToken).toBe("token");
|
|
45
|
+
expect(service.$authToken.value).toBe("token");
|
|
46
|
+
});
|
|
47
|
+
it("login error", async () => {
|
|
48
|
+
const payload = { identifier: "user", password: "wrong" };
|
|
49
|
+
jest.spyOn(service, "provider").mockReturnValue({
|
|
50
|
+
request: () => of(NetworkingUIEventWrapped.failed(AlertModel.default())),
|
|
51
|
+
});
|
|
52
|
+
const result = await service.login(payload).toPromise();
|
|
53
|
+
expect(result?.hasFailed).toBe(true);
|
|
54
|
+
expect(result?.alertModel).toBeInstanceOf(AlertModel);
|
|
55
|
+
});
|
|
56
|
+
it("login success with rememberMe", async () => {
|
|
57
|
+
const payload = {
|
|
58
|
+
identifier: "user",
|
|
59
|
+
password: "pass",
|
|
60
|
+
rememberMe: true
|
|
61
|
+
};
|
|
62
|
+
const response = {
|
|
63
|
+
token_type: "Bearer",
|
|
64
|
+
expires_in: 3600,
|
|
65
|
+
token: "token",
|
|
66
|
+
refresh_token: "refresh",
|
|
67
|
+
};
|
|
68
|
+
jest.spyOn(service, "provider").mockReturnValue({
|
|
69
|
+
request: () => of(NetworkingUIEventWrapped.succeeded(response)),
|
|
70
|
+
});
|
|
71
|
+
const result = await service.login(payload).toPromise();
|
|
72
|
+
expect(result?.hasSuccess).toBe(true);
|
|
73
|
+
expect(result?.response).toEqual(response);
|
|
74
|
+
// Verify that auth token is set after successful login
|
|
75
|
+
expect(service.currentAuthToken).toBe("token");
|
|
76
|
+
expect(service.$authToken.value).toBe("token");
|
|
77
|
+
// Verify that localStorage was called to save the auth data
|
|
78
|
+
expect(mockLocalStorage.setItem).toHaveBeenCalledWith('smess_auth_token', 'token');
|
|
79
|
+
expect(mockLocalStorage.setItem).toHaveBeenCalledWith('smess_auth_response', JSON.stringify(response));
|
|
80
|
+
});
|
|
81
|
+
it("login success without rememberMe", async () => {
|
|
82
|
+
const payload = {
|
|
83
|
+
identifier: "user",
|
|
84
|
+
password: "pass",
|
|
85
|
+
rememberMe: false
|
|
86
|
+
};
|
|
87
|
+
const response = {
|
|
88
|
+
token_type: "Bearer",
|
|
89
|
+
expires_in: 3600,
|
|
90
|
+
token: "token",
|
|
91
|
+
refresh_token: "refresh",
|
|
92
|
+
};
|
|
93
|
+
jest.spyOn(service, "provider").mockReturnValue({
|
|
94
|
+
request: () => of(NetworkingUIEventWrapped.succeeded(response)),
|
|
95
|
+
});
|
|
96
|
+
const result = await service.login(payload).toPromise();
|
|
97
|
+
expect(result?.hasSuccess).toBe(true);
|
|
98
|
+
expect(result?.response).toEqual(response);
|
|
99
|
+
// Verify that auth token is set after successful login
|
|
100
|
+
expect(service.currentAuthToken).toBe("token");
|
|
101
|
+
expect(service.$authToken.value).toBe("token");
|
|
102
|
+
// Verify that localStorage was NOT called to save the auth data
|
|
103
|
+
expect(mockLocalStorage.setItem).not.toHaveBeenCalled();
|
|
104
|
+
});
|
|
105
|
+
it("logout success", async () => {
|
|
106
|
+
// First set a token to test clearing it
|
|
107
|
+
service.$authToken.next("test-token");
|
|
108
|
+
expect(service.currentAuthToken).toBe("test-token");
|
|
109
|
+
jest.spyOn(service, "provider").mockReturnValue({
|
|
110
|
+
request: () => of(NetworkingUIEventWrapped.succeeded(undefined)),
|
|
111
|
+
});
|
|
112
|
+
const result = await service.logout().toPromise();
|
|
113
|
+
expect(result?.hasSuccess).toBe(true);
|
|
114
|
+
// Verify that auth token is cleared after successful logout
|
|
115
|
+
expect(service.currentAuthToken).toBe(null);
|
|
116
|
+
expect(service.$authToken.value).toBe(null);
|
|
117
|
+
// Verify that localStorage was cleared
|
|
118
|
+
expect(mockLocalStorage.removeItem).toHaveBeenCalledWith('smess_auth_token');
|
|
119
|
+
expect(mockLocalStorage.removeItem).toHaveBeenCalledWith('smess_auth_response');
|
|
120
|
+
});
|
|
121
|
+
it("loads auth token from localStorage on initialization", () => {
|
|
122
|
+
// Mock localStorage to return a stored auth response
|
|
123
|
+
const storedResponse = {
|
|
124
|
+
token_type: "Bearer",
|
|
125
|
+
expires_in: 3600,
|
|
126
|
+
token: "stored-token",
|
|
127
|
+
refresh_token: "refresh",
|
|
128
|
+
};
|
|
129
|
+
const mockLocalStorage = {
|
|
130
|
+
setItem: jest.fn(),
|
|
131
|
+
getItem: jest.fn().mockImplementation((key) => {
|
|
132
|
+
if (key === 'smess_auth_response') {
|
|
133
|
+
return JSON.stringify(storedResponse);
|
|
134
|
+
}
|
|
135
|
+
if (key === 'smess_auth_response_stored_at') {
|
|
136
|
+
return Date.now().toString();
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}),
|
|
140
|
+
removeItem: jest.fn(),
|
|
141
|
+
};
|
|
142
|
+
Object.defineProperty(window, 'localStorage', {
|
|
143
|
+
value: mockLocalStorage,
|
|
144
|
+
writable: true,
|
|
145
|
+
});
|
|
146
|
+
// Create a new service instance
|
|
147
|
+
const newService = new AuthService(config);
|
|
148
|
+
// Verify that the stored token was loaded
|
|
149
|
+
expect(newService.currentAuthToken).toBe('stored-token');
|
|
150
|
+
expect(newService.$authToken.value).toBe('stored-token');
|
|
151
|
+
expect(mockLocalStorage.getItem).toHaveBeenCalledWith('smess_auth_response');
|
|
152
|
+
});
|
|
153
|
+
it("handles localStorage errors gracefully", () => {
|
|
154
|
+
// Mock localStorage to throw an error
|
|
155
|
+
const mockLocalStorage = {
|
|
156
|
+
setItem: jest.fn().mockImplementation(() => {
|
|
157
|
+
throw new Error('localStorage error');
|
|
158
|
+
}),
|
|
159
|
+
getItem: jest.fn().mockImplementation(() => {
|
|
160
|
+
throw new Error('localStorage error');
|
|
161
|
+
}),
|
|
162
|
+
removeItem: jest.fn().mockImplementation(() => {
|
|
163
|
+
throw new Error('localStorage error');
|
|
164
|
+
}),
|
|
165
|
+
};
|
|
166
|
+
Object.defineProperty(window, 'localStorage', {
|
|
167
|
+
value: mockLocalStorage,
|
|
168
|
+
writable: true,
|
|
169
|
+
});
|
|
170
|
+
// Should not throw an error during initialization
|
|
171
|
+
expect(() => new AuthService(config)).not.toThrow();
|
|
172
|
+
// Should not throw an error during login with rememberMe
|
|
173
|
+
const service = new AuthService(config);
|
|
174
|
+
const payload = {
|
|
175
|
+
identifier: "user",
|
|
176
|
+
password: "pass",
|
|
177
|
+
rememberMe: true
|
|
178
|
+
};
|
|
179
|
+
const response = {
|
|
180
|
+
token_type: "Bearer",
|
|
181
|
+
expires_in: 3600,
|
|
182
|
+
token: "token",
|
|
183
|
+
refresh_token: "refresh",
|
|
184
|
+
};
|
|
185
|
+
jest.spyOn(service, "provider").mockReturnValue({
|
|
186
|
+
request: () => of(NetworkingUIEventWrapped.succeeded(response)),
|
|
187
|
+
});
|
|
188
|
+
expect(async () => {
|
|
189
|
+
await service.login(payload).toPromise();
|
|
190
|
+
}).not.toThrow();
|
|
191
|
+
});
|
|
192
|
+
it("handles expired token from localStorage", () => {
|
|
193
|
+
// Mock localStorage with expired token
|
|
194
|
+
const expiredResponse = {
|
|
195
|
+
token_type: "Bearer",
|
|
196
|
+
expires_in: 1, // 1 second
|
|
197
|
+
token: "expired-token",
|
|
198
|
+
refresh_token: "refresh",
|
|
199
|
+
};
|
|
200
|
+
mockLocalStorage.getItem.mockImplementation((key) => {
|
|
201
|
+
if (key === 'smess_auth_response') {
|
|
202
|
+
return JSON.stringify(expiredResponse);
|
|
203
|
+
}
|
|
204
|
+
if (key === 'smess_auth_response_stored_at') {
|
|
205
|
+
// Set stored time to 2 seconds ago (expired)
|
|
206
|
+
return (Date.now() - 2000).toString();
|
|
207
|
+
}
|
|
208
|
+
return null;
|
|
209
|
+
});
|
|
210
|
+
// Create a new service instance
|
|
211
|
+
const newService = new AuthService(config);
|
|
212
|
+
// Verify that expired token is not returned
|
|
213
|
+
expect(newService.currentAuthToken).toBe(null);
|
|
214
|
+
expect(newService.$authToken.value).toBe(null);
|
|
215
|
+
// Verify that localStorage was cleared
|
|
216
|
+
expect(mockLocalStorage.removeItem).toHaveBeenCalledWith('smess_auth_token');
|
|
217
|
+
expect(mockLocalStorage.removeItem).toHaveBeenCalledWith('smess_auth_response');
|
|
218
|
+
expect(mockLocalStorage.removeItem).toHaveBeenCalledWith('smess_auth_response_stored_at');
|
|
219
|
+
});
|
|
220
|
+
it("handles valid token from localStorage", () => {
|
|
221
|
+
// Mock localStorage with valid token
|
|
222
|
+
const validResponse = {
|
|
223
|
+
token_type: "Bearer",
|
|
224
|
+
expires_in: 3600, // 1 hour
|
|
225
|
+
token: "valid-token",
|
|
226
|
+
refresh_token: "refresh",
|
|
227
|
+
};
|
|
228
|
+
mockLocalStorage.getItem.mockImplementation((key) => {
|
|
229
|
+
if (key === 'smess_auth_response') {
|
|
230
|
+
return JSON.stringify(validResponse);
|
|
231
|
+
}
|
|
232
|
+
if (key === 'smess_auth_response_stored_at') {
|
|
233
|
+
// Set stored time to 30 minutes ago (still valid)
|
|
234
|
+
return (Date.now() - 1800000).toString();
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
237
|
+
});
|
|
238
|
+
// Create a new service instance
|
|
239
|
+
const newService = new AuthService(config);
|
|
240
|
+
// Verify that valid token is returned
|
|
241
|
+
expect(newService.currentAuthToken).toBe("valid-token");
|
|
242
|
+
expect(newService.$authToken.value).toBe("valid-token");
|
|
243
|
+
});
|
|
244
|
+
it("forgotPassword success", async () => {
|
|
245
|
+
const payload = { identifier: "user" };
|
|
246
|
+
const response = { data: "ok" };
|
|
247
|
+
jest.spyOn(service, "provider").mockReturnValue({
|
|
248
|
+
request: () => of(NetworkingUIEventWrapped.succeeded(response)),
|
|
249
|
+
});
|
|
250
|
+
const result = await service.forgotPassword(payload).toPromise();
|
|
251
|
+
expect(result?.hasSuccess).toBe(true);
|
|
252
|
+
expect(result?.response).toEqual(response);
|
|
253
|
+
});
|
|
254
|
+
it("changePassword success", async () => {
|
|
255
|
+
const payload = {
|
|
256
|
+
old_password: "old",
|
|
257
|
+
password: "new",
|
|
258
|
+
};
|
|
259
|
+
const response = { data: "changed" };
|
|
260
|
+
jest.spyOn(service, "provider").mockReturnValue({
|
|
261
|
+
request: () => of(NetworkingUIEventWrapped.succeeded(response)),
|
|
262
|
+
});
|
|
263
|
+
const result = await service.changePassword(payload).toPromise();
|
|
264
|
+
expect(result?.hasSuccess).toBe(true);
|
|
265
|
+
expect(result?.response).toEqual(response);
|
|
266
|
+
});
|
|
267
|
+
it("resetPassword success", async () => {
|
|
268
|
+
const payload = {
|
|
269
|
+
token: "token",
|
|
270
|
+
password: "newpass",
|
|
271
|
+
};
|
|
272
|
+
const response = { data: "reset" };
|
|
273
|
+
jest.spyOn(service, "provider").mockReturnValue({
|
|
274
|
+
request: () => of(NetworkingUIEventWrapped.succeeded(response)),
|
|
275
|
+
});
|
|
276
|
+
const result = await service.resetPassword(payload).toPromise();
|
|
277
|
+
expect(result?.hasSuccess).toBe(true);
|
|
278
|
+
expect(result?.response).toEqual(response);
|
|
279
|
+
});
|
|
280
|
+
it("verifyResetPasswordToken success", async () => {
|
|
281
|
+
const token = "token";
|
|
282
|
+
const response = {
|
|
283
|
+
token_type: "Bearer",
|
|
284
|
+
expires_in: 3600,
|
|
285
|
+
token: "token",
|
|
286
|
+
refresh_token: "refresh",
|
|
287
|
+
};
|
|
288
|
+
jest.spyOn(service, "provider").mockReturnValue({
|
|
289
|
+
request: () => of(NetworkingUIEventWrapped.succeeded(response)),
|
|
290
|
+
});
|
|
291
|
+
const result = await service.verifyResetPasswordToken(token).toPromise();
|
|
292
|
+
expect(result?.hasSuccess).toBe(true);
|
|
293
|
+
expect(result?.response).toEqual(response);
|
|
294
|
+
});
|
|
295
|
+
it("forgotPassword error", async () => {
|
|
296
|
+
const payload = { identifier: "user" };
|
|
297
|
+
jest.spyOn(service, "provider").mockReturnValue({
|
|
298
|
+
request: () => of(NetworkingUIEventWrapped.failed(AlertModel.default())),
|
|
299
|
+
});
|
|
300
|
+
const result = await service.forgotPassword(payload).toPromise();
|
|
301
|
+
expect(result?.hasFailed).toBe(true);
|
|
302
|
+
expect(result?.alertModel).toBeInstanceOf(AlertModel);
|
|
303
|
+
});
|
|
304
|
+
it("changePassword error", async () => {
|
|
305
|
+
const payload = {
|
|
306
|
+
old_password: "old",
|
|
307
|
+
password: "bad",
|
|
308
|
+
};
|
|
309
|
+
jest.spyOn(service, "provider").mockReturnValue({
|
|
310
|
+
request: () => of(NetworkingUIEventWrapped.failed(AlertModel.default())),
|
|
311
|
+
});
|
|
312
|
+
const result = await service.changePassword(payload).toPromise();
|
|
313
|
+
expect(result?.hasFailed).toBe(true);
|
|
314
|
+
expect(result?.alertModel).toBeInstanceOf(AlertModel);
|
|
315
|
+
});
|
|
316
|
+
it("resetPassword error", async () => {
|
|
317
|
+
const payload = { token: "bad", password: "bad" };
|
|
318
|
+
jest.spyOn(service, "provider").mockReturnValue({
|
|
319
|
+
request: () => of(NetworkingUIEventWrapped.failed(AlertModel.default())),
|
|
320
|
+
});
|
|
321
|
+
const result = await service.resetPassword(payload).toPromise();
|
|
322
|
+
expect(result?.hasFailed).toBe(true);
|
|
323
|
+
expect(result?.alertModel).toBeInstanceOf(AlertModel);
|
|
324
|
+
});
|
|
325
|
+
it("verifyResetPasswordToken error", async () => {
|
|
326
|
+
const token = "bad";
|
|
327
|
+
jest.spyOn(service, "provider").mockReturnValue({
|
|
328
|
+
request: () => of(NetworkingUIEventWrapped.failed(AlertModel.default())),
|
|
329
|
+
});
|
|
330
|
+
const result = await service.verifyResetPasswordToken(token).toPromise();
|
|
331
|
+
expect(result?.hasFailed).toBe(true);
|
|
332
|
+
expect(result?.alertModel).toBeInstanceOf(AlertModel);
|
|
333
|
+
});
|
|
334
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Dexie, Table } from "../utils/cache-import";
|
|
2
|
+
export interface CacheMetadata {
|
|
3
|
+
key: string;
|
|
4
|
+
cachedAt: number;
|
|
5
|
+
expiresAt: number;
|
|
6
|
+
version: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class GenericDB<T> extends Dexie {
|
|
9
|
+
data: Table<T>;
|
|
10
|
+
cache: Table<CacheMetadata>;
|
|
11
|
+
constructor(name: string, dataStoreSchema: string);
|
|
12
|
+
}
|
|
13
|
+
export declare class GenericIndexedDBService<T, TCached> {
|
|
14
|
+
private toCached;
|
|
15
|
+
private fromCached;
|
|
16
|
+
private readonly CACHE_DURATION;
|
|
17
|
+
readonly db: GenericDB<TCached>;
|
|
18
|
+
constructor(dbName: string, storeSchema: string, toCached: (raw: T) => TCached, fromCached: (cached: TCached) => T);
|
|
19
|
+
saveAll(items: T[], cacheKey: string): Promise<void>;
|
|
20
|
+
getAll(cacheKey: string): Promise<T[] | null>;
|
|
21
|
+
saveOne(item: T): Promise<void>;
|
|
22
|
+
getOne(id: string): Promise<T | null>;
|
|
23
|
+
delete(id: string): Promise<void>;
|
|
24
|
+
clearAll(): Promise<void>;
|
|
25
|
+
getCacheInfo(key: string): Promise<{
|
|
26
|
+
count: number;
|
|
27
|
+
lastUpdated?: Date;
|
|
28
|
+
}>;
|
|
29
|
+
setStats(key: string, stats: unknown): Promise<void>;
|
|
30
|
+
getStats(key: string): Promise<unknown | null>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Dexie } from "../utils/cache-import";
|
|
2
|
+
export class GenericDB extends Dexie {
|
|
3
|
+
constructor(name, dataStoreSchema) {
|
|
4
|
+
super(name);
|
|
5
|
+
this.version(1).stores({
|
|
6
|
+
data: dataStoreSchema,
|
|
7
|
+
cache: "key, cachedAt, expiresAt",
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class GenericIndexedDBService {
|
|
12
|
+
constructor(dbName, storeSchema, toCached, fromCached) {
|
|
13
|
+
this.toCached = toCached;
|
|
14
|
+
this.fromCached = fromCached;
|
|
15
|
+
this.CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours
|
|
16
|
+
this.db = new GenericDB(dbName, storeSchema);
|
|
17
|
+
}
|
|
18
|
+
async saveAll(items, cacheKey) {
|
|
19
|
+
const cachedItems = items.map(this.toCached);
|
|
20
|
+
await this.db.transaction("rw", this.db.data, this.db.cache, async () => {
|
|
21
|
+
await this.db.data.clear();
|
|
22
|
+
await this.db.data.bulkAdd(cachedItems);
|
|
23
|
+
await this.db.cache.put({
|
|
24
|
+
key: cacheKey,
|
|
25
|
+
cachedAt: Date.now(),
|
|
26
|
+
expiresAt: Date.now() + this.CACHE_DURATION,
|
|
27
|
+
version: "1.0",
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async getAll(cacheKey) {
|
|
32
|
+
const cacheInfo = await this.db.cache.get(cacheKey);
|
|
33
|
+
if (!cacheInfo || Date.now() > cacheInfo.expiresAt)
|
|
34
|
+
return null;
|
|
35
|
+
const cachedItems = await this.db.data.toArray();
|
|
36
|
+
return cachedItems.map(this.fromCached);
|
|
37
|
+
}
|
|
38
|
+
async saveOne(item) {
|
|
39
|
+
const cached = this.toCached(item);
|
|
40
|
+
await this.db.data.put(cached);
|
|
41
|
+
}
|
|
42
|
+
async getOne(id) {
|
|
43
|
+
const cached = await this.db.data.get(id);
|
|
44
|
+
return cached ? this.fromCached(cached) : null;
|
|
45
|
+
}
|
|
46
|
+
async delete(id) {
|
|
47
|
+
await this.db.data.delete(id);
|
|
48
|
+
}
|
|
49
|
+
async clearAll() {
|
|
50
|
+
await this.db.transaction("rw", this.db.data, this.db.cache, async () => {
|
|
51
|
+
await this.db.data.clear();
|
|
52
|
+
await this.db.cache.clear();
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
async getCacheInfo(key) {
|
|
56
|
+
const count = await this.db.data.count();
|
|
57
|
+
const cacheInfo = await this.db.cache.get(key);
|
|
58
|
+
return {
|
|
59
|
+
count,
|
|
60
|
+
lastUpdated: cacheInfo ? new Date(cacheInfo.cachedAt) : undefined,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async setStats(key, stats) {
|
|
64
|
+
await this.db.cache.put({
|
|
65
|
+
key,
|
|
66
|
+
cachedAt: Date.now(),
|
|
67
|
+
expiresAt: Date.now() + this.CACHE_DURATION,
|
|
68
|
+
version: JSON.stringify(stats),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
async getStats(key) {
|
|
72
|
+
const cacheInfo = await this.db.cache.get(key);
|
|
73
|
+
if (!cacheInfo || Date.now() > cacheInfo.expiresAt)
|
|
74
|
+
return null;
|
|
75
|
+
return JSON.parse(cacheInfo.version);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "fake-indexeddb/auto";
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import "fake-indexeddb/auto";
|
|
2
|
+
import { GenericIndexedDBService } from "./cache.service";
|
|
3
|
+
const toCached = (entity) => ({
|
|
4
|
+
id: entity.id,
|
|
5
|
+
name: entity.name,
|
|
6
|
+
createdAt: entity.createdAt.toISOString(),
|
|
7
|
+
cachedAt: Date.now(),
|
|
8
|
+
});
|
|
9
|
+
const fromCached = (cached) => ({
|
|
10
|
+
id: cached.id,
|
|
11
|
+
name: cached.name,
|
|
12
|
+
createdAt: new Date(cached.createdAt),
|
|
13
|
+
});
|
|
14
|
+
describe("GenericIndexedDBService", () => {
|
|
15
|
+
let service;
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
service = new GenericIndexedDBService("TestDB", "id, name", toCached, fromCached);
|
|
18
|
+
});
|
|
19
|
+
afterEach(async () => {
|
|
20
|
+
await service.db.delete();
|
|
21
|
+
});
|
|
22
|
+
it("should save and retrieve one item", async () => {
|
|
23
|
+
const item = {
|
|
24
|
+
id: "1",
|
|
25
|
+
name: "Test Item",
|
|
26
|
+
createdAt: new Date(),
|
|
27
|
+
};
|
|
28
|
+
await service.saveOne(item);
|
|
29
|
+
const result = await service.getOne("1");
|
|
30
|
+
expect(result).not.toBeNull();
|
|
31
|
+
expect(result?.id).toBe("1");
|
|
32
|
+
});
|
|
33
|
+
it("should save and retrieve all items", async () => {
|
|
34
|
+
const now = new Date();
|
|
35
|
+
const items = [
|
|
36
|
+
{ id: "1", name: "One", createdAt: now },
|
|
37
|
+
{ id: "2", name: "Two", createdAt: now },
|
|
38
|
+
];
|
|
39
|
+
await service.saveAll(items, "all");
|
|
40
|
+
const results = await service.getAll("all");
|
|
41
|
+
expect(results).not.toBeNull();
|
|
42
|
+
expect(results.length).toBe(2);
|
|
43
|
+
});
|
|
44
|
+
it("should return null when cache is expired", async () => {
|
|
45
|
+
const cacheTable = service.db.cache;
|
|
46
|
+
await cacheTable.put({
|
|
47
|
+
key: "expired",
|
|
48
|
+
cachedAt: Date.now() - 10000,
|
|
49
|
+
expiresAt: Date.now() - 5000,
|
|
50
|
+
version: "1.0",
|
|
51
|
+
});
|
|
52
|
+
const result = await service.getAll("expired");
|
|
53
|
+
expect(result).toBeNull();
|
|
54
|
+
});
|
|
55
|
+
it("should delete one item", async () => {
|
|
56
|
+
const item = {
|
|
57
|
+
id: "1",
|
|
58
|
+
name: "Delete Me",
|
|
59
|
+
createdAt: new Date(),
|
|
60
|
+
};
|
|
61
|
+
await service.saveOne(item);
|
|
62
|
+
await service.delete("1");
|
|
63
|
+
const result = await service.getOne("1");
|
|
64
|
+
expect(result).toBeNull();
|
|
65
|
+
});
|
|
66
|
+
it("should set and retrieve stats", async () => {
|
|
67
|
+
const stats = { total: 99 };
|
|
68
|
+
await service.setStats("mystats", stats);
|
|
69
|
+
const result = await service.getStats("mystats");
|
|
70
|
+
expect(result).toEqual(stats);
|
|
71
|
+
});
|
|
72
|
+
it("should get cache info", async () => {
|
|
73
|
+
await service.saveOne({
|
|
74
|
+
id: "123",
|
|
75
|
+
name: "Info Item",
|
|
76
|
+
createdAt: new Date(),
|
|
77
|
+
});
|
|
78
|
+
await service.db.cache.put({
|
|
79
|
+
key: "info",
|
|
80
|
+
cachedAt: Date.now(),
|
|
81
|
+
expiresAt: Date.now() + 10000,
|
|
82
|
+
version: "1.0",
|
|
83
|
+
});
|
|
84
|
+
const info = await service.getCacheInfo("info");
|
|
85
|
+
expect(info.count).toBeGreaterThanOrEqual(1);
|
|
86
|
+
expect(info.lastUpdated).toBeInstanceOf(Date);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ProviderType, NetworkingConfig, NetworkingUIEventWrapped, HttpResponse } from "../types/types";
|
|
2
|
+
import { SmessService } from "./smess.service";
|
|
3
|
+
import { BehaviorSubject, Observable } from "rxjs";
|
|
4
|
+
export type CustomerTarget = "register" | "get-profile" | "update-profile" | "cart-collection" | "customer-product-collection" | "customer-product-create" | "customer-product-remove" | "customer-product-update-notes";
|
|
5
|
+
export interface CustomerRegisterRequest {
|
|
6
|
+
client_id?: string;
|
|
7
|
+
email: string;
|
|
8
|
+
password: string;
|
|
9
|
+
phone_number?: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
address?: string;
|
|
12
|
+
tax_identification_number?: string | null;
|
|
13
|
+
}
|
|
14
|
+
export interface CustomerUpdateRequest {
|
|
15
|
+
name: string;
|
|
16
|
+
address: string;
|
|
17
|
+
postal_code: string;
|
|
18
|
+
city: string;
|
|
19
|
+
state: string | null;
|
|
20
|
+
country: string | null;
|
|
21
|
+
contact_email: string | null;
|
|
22
|
+
phone_number: string | null;
|
|
23
|
+
}
|
|
24
|
+
export interface CustomerCartCollectionQuery {
|
|
25
|
+
page?: number;
|
|
26
|
+
per_page?: number;
|
|
27
|
+
status?: string;
|
|
28
|
+
search?: string;
|
|
29
|
+
sort_by?: string;
|
|
30
|
+
sort_order?: "asc" | "desc";
|
|
31
|
+
}
|
|
32
|
+
export interface CustomerProductCollectionQuery {
|
|
33
|
+
page?: number;
|
|
34
|
+
per_page?: number;
|
|
35
|
+
}
|
|
36
|
+
export interface CustomerProductCreateRequest {
|
|
37
|
+
product_id: string;
|
|
38
|
+
notes?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface CustomerProductRemoveRequest {
|
|
41
|
+
product_id: string;
|
|
42
|
+
}
|
|
43
|
+
export interface CustomerProductUpdateNotesRequest {
|
|
44
|
+
product_id: string;
|
|
45
|
+
notes: string;
|
|
46
|
+
}
|
|
47
|
+
export declare class CustomerProvider implements ProviderType {
|
|
48
|
+
private readonly config;
|
|
49
|
+
private readonly target;
|
|
50
|
+
private readonly $authToken;
|
|
51
|
+
constructor(config: NetworkingConfig, target: CustomerTarget, $authToken: BehaviorSubject<string | null>);
|
|
52
|
+
get baseURL(): string;
|
|
53
|
+
get path(): string;
|
|
54
|
+
get method(): string;
|
|
55
|
+
get headers(): Record<string, string>;
|
|
56
|
+
getPath(...queryParams: unknown[]): string;
|
|
57
|
+
}
|
|
58
|
+
export declare class CustomerService {
|
|
59
|
+
private readonly config;
|
|
60
|
+
private readonly $authToken;
|
|
61
|
+
constructor(config: NetworkingConfig, $authToken: BehaviorSubject<string | null>);
|
|
62
|
+
provider(target: CustomerTarget): SmessService<CustomerProvider>;
|
|
63
|
+
register(payload: CustomerRegisterRequest): Observable<NetworkingUIEventWrapped<HttpResponse>>;
|
|
64
|
+
getProfile(): Observable<NetworkingUIEventWrapped<HttpResponse>>;
|
|
65
|
+
updateProfile(payload: CustomerUpdateRequest): Observable<NetworkingUIEventWrapped<HttpResponse>>;
|
|
66
|
+
getCartCollection(query?: CustomerCartCollectionQuery): Observable<NetworkingUIEventWrapped<HttpResponse>>;
|
|
67
|
+
getCustomerProductCollection(query?: CustomerProductCollectionQuery): Observable<NetworkingUIEventWrapped<HttpResponse>>;
|
|
68
|
+
createCustomerProduct(payload: CustomerProductCreateRequest): Observable<NetworkingUIEventWrapped<HttpResponse>>;
|
|
69
|
+
removeCustomerProduct(payload: CustomerProductRemoveRequest): Observable<NetworkingUIEventWrapped<HttpResponse>>;
|
|
70
|
+
updateCustomerProductNotes(payload: CustomerProductUpdateNotesRequest): Observable<NetworkingUIEventWrapped<HttpResponse>>;
|
|
71
|
+
}
|