@smess/networking 1.0.15 → 1.0.18
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/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/services/auth.service.d.ts +8 -1
- package/dist/services/auth.service.js +89 -5
- package/dist/services/auth.service.test.js +189 -0
- package/dist/services/store.cart.service.d.ts +139 -0
- package/dist/services/store.cart.service.js +166 -0
- package/dist/services/store.cart.service.test.d.ts +1 -0
- package/dist/services/store.cart.service.test.js +335 -0
- package/dist/services/store.product.service.d.ts +161 -0
- package/dist/services/store.product.service.js +164 -0
- package/dist/services/store.product.service.test.d.ts +1 -0
- package/dist/services/store.product.service.test.js +388 -0
- package/dist/services/store.service.d.ts +9 -0
- package/dist/services/store.service.js +14 -1
- package/package.json +2 -1
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export * from "./services/user.service";
|
|
|
2
2
|
export * from "./services/product.service";
|
|
3
3
|
export * from "./services/auth.service";
|
|
4
4
|
export * from "./services/store.service";
|
|
5
|
+
export * from "./services/store.cart.service";
|
|
6
|
+
export * from "./services/store.product.service";
|
|
5
7
|
export * from "./services/customer.service";
|
|
6
8
|
export * from "./services/order.service";
|
|
7
9
|
export * from "./services/cache.service";
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,8 @@ export * from "./services/user.service";
|
|
|
2
2
|
export * from "./services/product.service";
|
|
3
3
|
export * from "./services/auth.service";
|
|
4
4
|
export * from "./services/store.service";
|
|
5
|
+
export * from "./services/store.cart.service";
|
|
6
|
+
export * from "./services/store.product.service";
|
|
5
7
|
export * from "./services/customer.service";
|
|
6
8
|
export * from "./services/order.service";
|
|
7
9
|
export * from "./services/cache.service";
|
|
@@ -4,6 +4,7 @@ import { BehaviorSubject, Observable } from "rxjs";
|
|
|
4
4
|
export interface AuthLoginRequest {
|
|
5
5
|
identifier: string;
|
|
6
6
|
password: string;
|
|
7
|
+
rememberMe?: boolean;
|
|
7
8
|
}
|
|
8
9
|
export interface ForgotPasswordRequest {
|
|
9
10
|
identifier: string;
|
|
@@ -45,9 +46,15 @@ export interface AuthServiceType {
|
|
|
45
46
|
}
|
|
46
47
|
export declare class AuthService implements AuthServiceType {
|
|
47
48
|
private readonly config;
|
|
48
|
-
readonly $
|
|
49
|
+
private readonly $_authToken;
|
|
49
50
|
constructor(config: NetworkingConfig);
|
|
51
|
+
get $authToken(): BehaviorSubject<string | null>;
|
|
50
52
|
get currentAuthToken(): string | null;
|
|
53
|
+
private getValidToken;
|
|
54
|
+
private loadFromStorage;
|
|
55
|
+
private loadAuthFromStorage;
|
|
56
|
+
private saveAuthToStorage;
|
|
57
|
+
private clearAuthFromStorage;
|
|
51
58
|
provider(target: AuthTarget): SmessService<AuthProvider>;
|
|
52
59
|
login(payload: AuthLoginRequest): Observable<NetworkingUIEventWrapped<AuthLoginResponse>>;
|
|
53
60
|
logout(): Observable<NetworkingUIEventWrapped<void>>;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { Accept, SMESSHeader, SmessService } from "./smess.service";
|
|
2
2
|
import { BehaviorSubject } from "rxjs";
|
|
3
3
|
import { tap } from "rxjs/operators";
|
|
4
|
+
// LocalStorage keys for auth persistence
|
|
5
|
+
const AUTH_TOKEN_KEY = 'smess_auth_token';
|
|
6
|
+
const AUTH_RESPONSE_KEY = 'smess_auth_response';
|
|
4
7
|
export class AuthProvider {
|
|
5
8
|
constructor(config, target, $authToken) {
|
|
6
9
|
this.config = config;
|
|
@@ -68,11 +71,88 @@ export class AuthProvider {
|
|
|
68
71
|
export class AuthService {
|
|
69
72
|
constructor(config) {
|
|
70
73
|
this.config = config;
|
|
71
|
-
this.$
|
|
74
|
+
this.$_authToken = new BehaviorSubject(null);
|
|
75
|
+
this.loadAuthFromStorage();
|
|
76
|
+
}
|
|
77
|
+
// Getter that returns BehaviorSubject with token validation
|
|
78
|
+
get $authToken() {
|
|
79
|
+
const currentToken = this.getValidToken();
|
|
80
|
+
if (currentToken !== this.$_authToken.value) {
|
|
81
|
+
this.$_authToken.next(currentToken);
|
|
82
|
+
}
|
|
83
|
+
return this.$_authToken;
|
|
72
84
|
}
|
|
73
85
|
// Get current auth token value
|
|
74
86
|
get currentAuthToken() {
|
|
75
|
-
return this
|
|
87
|
+
return this.getValidToken();
|
|
88
|
+
}
|
|
89
|
+
// Get valid token from memory or localStorage
|
|
90
|
+
getValidToken() {
|
|
91
|
+
// If we have a token in memory, return it (assume it's valid)
|
|
92
|
+
if (this.$_authToken.value) {
|
|
93
|
+
return this.$_authToken.value;
|
|
94
|
+
}
|
|
95
|
+
// Otherwise, try to load from localStorage
|
|
96
|
+
return this.loadFromStorage();
|
|
97
|
+
}
|
|
98
|
+
// Load token from localStorage and check expiration
|
|
99
|
+
loadFromStorage() {
|
|
100
|
+
try {
|
|
101
|
+
const storedResponse = localStorage.getItem(AUTH_RESPONSE_KEY);
|
|
102
|
+
const storedAt = localStorage.getItem(`${AUTH_RESPONSE_KEY}_stored_at`);
|
|
103
|
+
if (storedResponse && storedAt) {
|
|
104
|
+
const authResponse = JSON.parse(storedResponse);
|
|
105
|
+
const storedTime = parseInt(storedAt, 10);
|
|
106
|
+
// Check if token is expired
|
|
107
|
+
if (authResponse.expires_in && typeof authResponse.expires_in === 'number') {
|
|
108
|
+
const expirationTime = storedTime + (authResponse.expires_in * 1000);
|
|
109
|
+
if (Date.now() >= expirationTime) {
|
|
110
|
+
// Token expired, clear storage
|
|
111
|
+
this.clearAuthFromStorage();
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Token is valid, store in memory and return
|
|
116
|
+
const token = String(authResponse.token);
|
|
117
|
+
this.$_authToken.next(token);
|
|
118
|
+
return token;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
console.warn('Failed to load token from localStorage:', error);
|
|
123
|
+
this.clearAuthFromStorage();
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
// Load auth token from localStorage on initialization
|
|
128
|
+
loadAuthFromStorage() {
|
|
129
|
+
this.loadFromStorage();
|
|
130
|
+
}
|
|
131
|
+
// Save auth response to localStorage
|
|
132
|
+
saveAuthToStorage(authResponse) {
|
|
133
|
+
try {
|
|
134
|
+
localStorage.setItem(AUTH_TOKEN_KEY, String(authResponse.token));
|
|
135
|
+
localStorage.setItem(AUTH_RESPONSE_KEY, JSON.stringify(authResponse));
|
|
136
|
+
localStorage.setItem(`${AUTH_RESPONSE_KEY}_stored_at`, Date.now().toString());
|
|
137
|
+
// Update memory
|
|
138
|
+
this.$_authToken.next(String(authResponse.token));
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
console.warn('Failed to save auth to localStorage:', error);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Clear auth from localStorage
|
|
145
|
+
clearAuthFromStorage() {
|
|
146
|
+
try {
|
|
147
|
+
localStorage.removeItem(AUTH_TOKEN_KEY);
|
|
148
|
+
localStorage.removeItem(AUTH_RESPONSE_KEY);
|
|
149
|
+
localStorage.removeItem(`${AUTH_RESPONSE_KEY}_stored_at`);
|
|
150
|
+
// Clear memory
|
|
151
|
+
this.$_authToken.next(null);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
console.warn('Failed to clear auth from localStorage:', error);
|
|
155
|
+
}
|
|
76
156
|
}
|
|
77
157
|
provider(target) {
|
|
78
158
|
const targetWrapper = new AuthProvider(this.config, target, this.$authToken);
|
|
@@ -84,7 +164,11 @@ export class AuthService {
|
|
|
84
164
|
.pipe(tap((result) => {
|
|
85
165
|
if (result.hasSuccess && result.response?.token) {
|
|
86
166
|
// Set the auth token when login is successful
|
|
87
|
-
this.$
|
|
167
|
+
this.$_authToken.next(String(result.response.token));
|
|
168
|
+
// Save to localStorage if rememberMe is true
|
|
169
|
+
if (payload.rememberMe && result.response) {
|
|
170
|
+
this.saveAuthToStorage(result.response);
|
|
171
|
+
}
|
|
88
172
|
}
|
|
89
173
|
}));
|
|
90
174
|
}
|
|
@@ -93,8 +177,8 @@ export class AuthService {
|
|
|
93
177
|
.request()
|
|
94
178
|
.pipe(tap((result) => {
|
|
95
179
|
if (result.hasSuccess) {
|
|
96
|
-
// Clear
|
|
97
|
-
this
|
|
180
|
+
// Clear from localStorage (this will also clear memory)
|
|
181
|
+
this.clearAuthFromStorage();
|
|
98
182
|
}
|
|
99
183
|
}));
|
|
100
184
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
1
4
|
import { AuthService, } from "./auth.service";
|
|
2
5
|
import { NetworkingUIEventWrapped, AlertModel, } from "../types/types";
|
|
3
6
|
import { of } from "rxjs";
|
|
@@ -9,7 +12,18 @@ describe("AuthService", () => {
|
|
|
9
12
|
apiKey: "test-api-key",
|
|
10
13
|
};
|
|
11
14
|
let service;
|
|
15
|
+
let mockLocalStorage;
|
|
12
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
|
+
});
|
|
13
27
|
service = new AuthService(config);
|
|
14
28
|
});
|
|
15
29
|
it("login success", async () => {
|
|
@@ -39,6 +53,55 @@ describe("AuthService", () => {
|
|
|
39
53
|
expect(result?.hasFailed).toBe(true);
|
|
40
54
|
expect(result?.alertModel).toBeInstanceOf(AlertModel);
|
|
41
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
|
+
});
|
|
42
105
|
it("logout success", async () => {
|
|
43
106
|
// First set a token to test clearing it
|
|
44
107
|
service.$authToken.next("test-token");
|
|
@@ -51,6 +114,132 @@ describe("AuthService", () => {
|
|
|
51
114
|
// Verify that auth token is cleared after successful logout
|
|
52
115
|
expect(service.currentAuthToken).toBe(null);
|
|
53
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");
|
|
54
243
|
});
|
|
55
244
|
it("forgotPassword success", async () => {
|
|
56
245
|
const payload = { identifier: "user" };
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { ProviderType, NetworkingConfig, NetworkingUIEventWrapped } from "../types/types";
|
|
2
|
+
import { SmessService } from "./smess.service";
|
|
3
|
+
import { BehaviorSubject, Observable } from "rxjs";
|
|
4
|
+
export type StoreCartTarget = "cart-get" | "cart-update" | "cart-add-item" | "cart-update-item" | "cart-delete-item" | "cart-delete-all-items";
|
|
5
|
+
export interface BillingInformation {
|
|
6
|
+
email?: string;
|
|
7
|
+
phone_number?: string;
|
|
8
|
+
street?: string;
|
|
9
|
+
city?: string;
|
|
10
|
+
postal_code?: string;
|
|
11
|
+
state?: string;
|
|
12
|
+
country?: string;
|
|
13
|
+
[key: string]: unknown;
|
|
14
|
+
}
|
|
15
|
+
export interface ShippingInformation {
|
|
16
|
+
email?: string;
|
|
17
|
+
phone_number?: string;
|
|
18
|
+
street?: string;
|
|
19
|
+
city?: string;
|
|
20
|
+
postal_code?: string;
|
|
21
|
+
state?: string;
|
|
22
|
+
country?: string;
|
|
23
|
+
[key: string]: unknown;
|
|
24
|
+
}
|
|
25
|
+
export interface CartItem {
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
export interface StoreInfo {
|
|
29
|
+
[key: string]: unknown;
|
|
30
|
+
}
|
|
31
|
+
export interface Cart {
|
|
32
|
+
dateCreated: string;
|
|
33
|
+
dateModified: string;
|
|
34
|
+
discountTotal: number;
|
|
35
|
+
discountTax: number;
|
|
36
|
+
shippingTotal: number;
|
|
37
|
+
shippingTax: number;
|
|
38
|
+
cartTax: number;
|
|
39
|
+
total: number;
|
|
40
|
+
totalTax: number;
|
|
41
|
+
customerId: string | null;
|
|
42
|
+
billingInformation: BillingInformation | null;
|
|
43
|
+
shippingInformation: ShippingInformation | null;
|
|
44
|
+
paymentMethod: string;
|
|
45
|
+
deliveryType: string;
|
|
46
|
+
transactionId: string | null;
|
|
47
|
+
customerIpAddress: string;
|
|
48
|
+
customerUserAgent: string;
|
|
49
|
+
customerNote: string | null;
|
|
50
|
+
cartHash: string;
|
|
51
|
+
cartItems: CartItem[];
|
|
52
|
+
paymentUrl: string | null;
|
|
53
|
+
store: StoreInfo | null;
|
|
54
|
+
[key: string]: unknown;
|
|
55
|
+
}
|
|
56
|
+
export interface CartUpdateRequest {
|
|
57
|
+
status?: string;
|
|
58
|
+
customer_note?: string;
|
|
59
|
+
customer_id?: string;
|
|
60
|
+
payment_method?: string;
|
|
61
|
+
delivery_type?: string;
|
|
62
|
+
billing_information?: BillingInformation;
|
|
63
|
+
shipping_information?: ShippingInformation;
|
|
64
|
+
[key: string]: unknown;
|
|
65
|
+
}
|
|
66
|
+
export interface CartAddItemRequest {
|
|
67
|
+
product_id: string;
|
|
68
|
+
quantity: number;
|
|
69
|
+
[key: string]: unknown;
|
|
70
|
+
}
|
|
71
|
+
export interface CartUpdateItemQuantityRequest {
|
|
72
|
+
quantity: number;
|
|
73
|
+
}
|
|
74
|
+
export declare class StoreCartProvider implements ProviderType {
|
|
75
|
+
private readonly config;
|
|
76
|
+
private readonly target;
|
|
77
|
+
private readonly $cartHash;
|
|
78
|
+
private readonly $storeId;
|
|
79
|
+
constructor(config: NetworkingConfig, target: StoreCartTarget, $cartHash: BehaviorSubject<string | null>, $storeId: BehaviorSubject<string | null>);
|
|
80
|
+
get baseURL(): string;
|
|
81
|
+
get path(): string;
|
|
82
|
+
get method(): string;
|
|
83
|
+
get headers(): Record<string, string>;
|
|
84
|
+
getPath(...queryParams: unknown[]): string;
|
|
85
|
+
}
|
|
86
|
+
export interface StoreCartServiceType {
|
|
87
|
+
getCart(): Observable<NetworkingUIEventWrapped<Cart>>;
|
|
88
|
+
updateCart(payload: CartUpdateRequest): Observable<NetworkingUIEventWrapped<Cart>>;
|
|
89
|
+
addItem(payload: CartAddItemRequest): Observable<NetworkingUIEventWrapped<Cart>>;
|
|
90
|
+
updateItemQuantity(itemId: string, payload: CartUpdateItemQuantityRequest): Observable<NetworkingUIEventWrapped<Cart>>;
|
|
91
|
+
deleteItem(itemId: string): Observable<NetworkingUIEventWrapped<void>>;
|
|
92
|
+
deleteAllItems(): Observable<NetworkingUIEventWrapped<void>>;
|
|
93
|
+
}
|
|
94
|
+
export declare class StoreCartService implements StoreCartServiceType {
|
|
95
|
+
private readonly config;
|
|
96
|
+
private readonly $cartHash;
|
|
97
|
+
private readonly $storeId;
|
|
98
|
+
constructor(config: NetworkingConfig, $storeId?: BehaviorSubject<string | null>);
|
|
99
|
+
get currentCartHash(): string | null;
|
|
100
|
+
get cartHash$(): BehaviorSubject<string | null>;
|
|
101
|
+
get currentStoreId(): string | null;
|
|
102
|
+
get storeId$(): BehaviorSubject<string | null>;
|
|
103
|
+
setStoreId(storeId: string | null): void;
|
|
104
|
+
provider(target: StoreCartTarget): SmessService<StoreCartProvider>;
|
|
105
|
+
/**
|
|
106
|
+
* Update the cart hash from cart response
|
|
107
|
+
*/
|
|
108
|
+
private updateCartHash;
|
|
109
|
+
/**
|
|
110
|
+
* Get the current cart
|
|
111
|
+
* GET api/v1/store/cart
|
|
112
|
+
*/
|
|
113
|
+
getCart(): Observable<NetworkingUIEventWrapped<Cart>>;
|
|
114
|
+
/**
|
|
115
|
+
* Update cart (e.g., add notes, apply coupon)
|
|
116
|
+
* PUT api/v1/store/cart
|
|
117
|
+
*/
|
|
118
|
+
updateCart(payload: CartUpdateRequest): Observable<NetworkingUIEventWrapped<Cart>>;
|
|
119
|
+
/**
|
|
120
|
+
* Add item to cart
|
|
121
|
+
* POST api/v1/store/cart/items
|
|
122
|
+
*/
|
|
123
|
+
addItem(payload: CartAddItemRequest): Observable<NetworkingUIEventWrapped<Cart>>;
|
|
124
|
+
/**
|
|
125
|
+
* Update item quantity in cart
|
|
126
|
+
* PUT api/v1/store/cart/items/{id}
|
|
127
|
+
*/
|
|
128
|
+
updateItemQuantity(itemId: string, payload: CartUpdateItemQuantityRequest): Observable<NetworkingUIEventWrapped<Cart>>;
|
|
129
|
+
/**
|
|
130
|
+
* Delete item from cart
|
|
131
|
+
* DELETE api/v1/store/cart/items/{id}
|
|
132
|
+
*/
|
|
133
|
+
deleteItem(itemId: string): Observable<NetworkingUIEventWrapped<void>>;
|
|
134
|
+
/**
|
|
135
|
+
* Delete all items from cart
|
|
136
|
+
* DELETE api/v1/store/cart/items
|
|
137
|
+
*/
|
|
138
|
+
deleteAllItems(): Observable<NetworkingUIEventWrapped<void>>;
|
|
139
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { Accept, SMESSHeader, SmessService } from "./smess.service";
|
|
2
|
+
import { BehaviorSubject } from "rxjs";
|
|
3
|
+
import { tap } from "rxjs/operators";
|
|
4
|
+
export class StoreCartProvider {
|
|
5
|
+
constructor(config, target, $cartHash, $storeId) {
|
|
6
|
+
this.config = config;
|
|
7
|
+
this.target = target;
|
|
8
|
+
this.$cartHash = $cartHash;
|
|
9
|
+
this.$storeId = $storeId;
|
|
10
|
+
}
|
|
11
|
+
get baseURL() {
|
|
12
|
+
return this.config.version
|
|
13
|
+
? `${this.config.baseUrl}/${this.config.version}`
|
|
14
|
+
: this.config.baseUrl;
|
|
15
|
+
}
|
|
16
|
+
get path() {
|
|
17
|
+
switch (this.target) {
|
|
18
|
+
case "cart-get":
|
|
19
|
+
return "/store/cart";
|
|
20
|
+
case "cart-update":
|
|
21
|
+
return "/store/cart";
|
|
22
|
+
case "cart-add-item":
|
|
23
|
+
return "/store/cart/items";
|
|
24
|
+
case "cart-update-item":
|
|
25
|
+
return "/store/cart/items/\${id}";
|
|
26
|
+
case "cart-delete-item":
|
|
27
|
+
return "/store/cart/items/\${id}";
|
|
28
|
+
case "cart-delete-all-items":
|
|
29
|
+
return "/store/cart/items";
|
|
30
|
+
default:
|
|
31
|
+
return "/store/cart";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
get method() {
|
|
35
|
+
switch (this.target) {
|
|
36
|
+
case "cart-get":
|
|
37
|
+
return "GET";
|
|
38
|
+
case "cart-update":
|
|
39
|
+
return "PUT";
|
|
40
|
+
case "cart-add-item":
|
|
41
|
+
return "POST";
|
|
42
|
+
case "cart-update-item":
|
|
43
|
+
return "PUT";
|
|
44
|
+
case "cart-delete-item":
|
|
45
|
+
return "DELETE";
|
|
46
|
+
case "cart-delete-all-items":
|
|
47
|
+
return "DELETE";
|
|
48
|
+
default:
|
|
49
|
+
return "GET";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
get headers() {
|
|
53
|
+
let headers = SmessService.getHeaders(Accept.V1, null);
|
|
54
|
+
if (this.config.clientId) {
|
|
55
|
+
headers[SMESSHeader.ClientId] = this.config.clientId;
|
|
56
|
+
}
|
|
57
|
+
if (this.config.apiKey) {
|
|
58
|
+
headers[SMESSHeader.ApiKey] = this.config.apiKey;
|
|
59
|
+
}
|
|
60
|
+
const filteredHeaders = SmessService.filterHeaders(headers);
|
|
61
|
+
// Add X-Nonce-Token header if cartHash is available
|
|
62
|
+
if (this.$cartHash.value) {
|
|
63
|
+
filteredHeaders["X-Nonce-Token"] = this.$cartHash.value;
|
|
64
|
+
}
|
|
65
|
+
// Add X-Store-Id header if storeId is available
|
|
66
|
+
if (this.$storeId.value) {
|
|
67
|
+
filteredHeaders["X-Store-Id"] = this.$storeId.value;
|
|
68
|
+
}
|
|
69
|
+
return filteredHeaders;
|
|
70
|
+
}
|
|
71
|
+
getPath(...queryParams) {
|
|
72
|
+
let definedPath = this.path;
|
|
73
|
+
const params = definedPath.match(/\$\{(.+?)\}/g);
|
|
74
|
+
params?.forEach((value, index) => {
|
|
75
|
+
definedPath = definedPath.replace(value, String(queryParams[index]));
|
|
76
|
+
});
|
|
77
|
+
return definedPath;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export class StoreCartService {
|
|
81
|
+
constructor(config, $storeId) {
|
|
82
|
+
this.config = config;
|
|
83
|
+
this.$cartHash = new BehaviorSubject(null);
|
|
84
|
+
// Use the provided BehaviorSubject or create a new one
|
|
85
|
+
this.$storeId = $storeId || new BehaviorSubject(null);
|
|
86
|
+
}
|
|
87
|
+
// Getter to access the current cart hash
|
|
88
|
+
get currentCartHash() {
|
|
89
|
+
return this.$cartHash.value;
|
|
90
|
+
}
|
|
91
|
+
// Getter to access the cart hash as observable
|
|
92
|
+
get cartHash$() {
|
|
93
|
+
return this.$cartHash;
|
|
94
|
+
}
|
|
95
|
+
// Getter to access the current store ID
|
|
96
|
+
get currentStoreId() {
|
|
97
|
+
return this.$storeId.value;
|
|
98
|
+
}
|
|
99
|
+
// Getter to access the store ID as observable
|
|
100
|
+
get storeId$() {
|
|
101
|
+
return this.$storeId;
|
|
102
|
+
}
|
|
103
|
+
// Method to update the store ID
|
|
104
|
+
setStoreId(storeId) {
|
|
105
|
+
this.$storeId.next(storeId);
|
|
106
|
+
}
|
|
107
|
+
provider(target) {
|
|
108
|
+
const targetWrapper = new StoreCartProvider(this.config, target, this.$cartHash, this.$storeId);
|
|
109
|
+
return new SmessService(targetWrapper);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Update the cart hash from cart response
|
|
113
|
+
*/
|
|
114
|
+
updateCartHash(result) {
|
|
115
|
+
if (result.hasSuccess && result.response) {
|
|
116
|
+
const response = result.response;
|
|
117
|
+
// Try to extract cartHash from different possible structures
|
|
118
|
+
const cartHash = response.data?.cartHash || response.cartHash;
|
|
119
|
+
if (cartHash) {
|
|
120
|
+
this.$cartHash.next(cartHash);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Get the current cart
|
|
126
|
+
* GET api/v1/store/cart
|
|
127
|
+
*/
|
|
128
|
+
getCart() {
|
|
129
|
+
return this.provider("cart-get").request().pipe(tap((result) => this.updateCartHash(result)));
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Update cart (e.g., add notes, apply coupon)
|
|
133
|
+
* PUT api/v1/store/cart
|
|
134
|
+
*/
|
|
135
|
+
updateCart(payload) {
|
|
136
|
+
return this.provider("cart-update").request(payload).pipe(tap((result) => this.updateCartHash(result)));
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Add item to cart
|
|
140
|
+
* POST api/v1/store/cart/items
|
|
141
|
+
*/
|
|
142
|
+
addItem(payload) {
|
|
143
|
+
return this.provider("cart-add-item").request(payload).pipe(tap((result) => this.updateCartHash(result)));
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Update item quantity in cart
|
|
147
|
+
* PUT api/v1/store/cart/items/{id}
|
|
148
|
+
*/
|
|
149
|
+
updateItemQuantity(itemId, payload) {
|
|
150
|
+
return this.provider("cart-update-item").request(payload, undefined, itemId).pipe(tap((result) => this.updateCartHash(result)));
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Delete item from cart
|
|
154
|
+
* DELETE api/v1/store/cart/items/{id}
|
|
155
|
+
*/
|
|
156
|
+
deleteItem(itemId) {
|
|
157
|
+
return this.provider("cart-delete-item").request(undefined, undefined, itemId);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Delete all items from cart
|
|
161
|
+
* DELETE api/v1/store/cart/items
|
|
162
|
+
*/
|
|
163
|
+
deleteAllItems() {
|
|
164
|
+
return this.provider("cart-delete-all-items").request();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|