@etsoo/react 1.3.41 → 1.3.45

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.
@@ -2,6 +2,10 @@
2
2
  * Refresh token request data
3
3
  */
4
4
  export declare type RefreshTokenRQ = {
5
+ /**
6
+ * Login password
7
+ */
8
+ pwd?: string;
5
9
  /**
6
10
  * Service id
7
11
  */
@@ -1,10 +1,10 @@
1
- import { IActionResult, IApi } from '@etsoo/appscript';
2
- import { ApiDataError } from '@etsoo/restclient';
1
+ import { IApi, RefreshTokenProps } from '@etsoo/appscript';
3
2
  import { IServiceAppSettings } from './IServiceAppSettings';
4
3
  import { IServicePageData } from './IServicePage';
5
4
  import { IServiceUser } from './IServiceUser';
6
5
  import { ISmartERPUser } from './ISmartERPUser';
7
6
  import { ReactApp } from './ReactApp';
7
+ import { RefreshTokenRQ } from './RefreshTokenRQ';
8
8
  /**
9
9
  * Core Service App
10
10
  * Service login to core system, get the refesh token and access token
@@ -31,14 +31,15 @@ export declare class ServiceApp<P extends IServicePageData = IServicePageData, U
31
31
  */
32
32
  toLoginPage(): void;
33
33
  /**
34
- * Try login result processing
35
- * @param result Result
34
+ * Refresh token
35
+ * @param props Props
36
36
  */
37
- protected tryLoginResult(result: string | ApiDataError | IActionResult): void;
37
+ refreshToken<D = RefreshTokenRQ>(props?: RefreshTokenProps<D>): Promise<boolean>;
38
+ private loginFailed;
38
39
  /**
39
40
  * Try login
40
41
  */
41
- tryLogin(): Promise<boolean>;
42
+ tryLogin<D extends {} = {}>(data?: D): Promise<boolean>;
42
43
  /**
43
44
  * User login extended
44
45
  * @param user New user
@@ -41,72 +41,142 @@ export class ServiceApp extends ReactApp {
41
41
  this.culture;
42
42
  }
43
43
  /**
44
- * Try login result processing
45
- * @param result Result
44
+ * Refresh token
45
+ * @param props Props
46
46
  */
47
- tryLoginResult(result) { }
48
- /**
49
- * Try login
50
- */
51
- async tryLogin() {
47
+ async refreshToken(props) {
48
+ // Destruct
49
+ const { callback, data, relogin = false, showLoading = false } = props !== null && props !== void 0 ? props : {};
52
50
  // Token
53
51
  const token = this.getCacheToken();
54
52
  if (token == null || token === '') {
55
- this.tryLoginResult('');
53
+ if (callback)
54
+ callback(false);
56
55
  return false;
57
56
  }
58
57
  // Reqest data
59
- const data = {
60
- timezone: this.getTimeZone()
58
+ // Merge additional data passed
59
+ const rq = {
60
+ timezone: this.getTimeZone(),
61
+ ...data
61
62
  };
62
63
  // Payload
63
64
  const payload = {
65
+ showLoading,
64
66
  config: { headers: { [CoreConstants.TokenHeaderRefresh]: token } },
65
67
  onError: (error) => {
66
- this.tryLoginResult(error);
68
+ if (callback)
69
+ callback(error);
67
70
  // Prevent further processing
68
71
  return false;
69
72
  }
70
73
  };
74
+ // Success callback
75
+ const success = async (result, failCallback) => {
76
+ // Token
77
+ const refreshToken = this.getResponseToken(payload.response);
78
+ if (refreshToken == null || result.data == null) {
79
+ if (failCallback)
80
+ failCallback(this.get('noData'));
81
+ return false;
82
+ }
83
+ // User data
84
+ const userData = result.data;
85
+ // Use core system access token to service api to exchange service access token
86
+ const serviceResult = await this.api.put('Auth/ExchangeToken', {
87
+ token: userData.token
88
+ }, {
89
+ showLoading,
90
+ onError: (error) => {
91
+ if (failCallback)
92
+ failCallback(error);
93
+ // Prevent further processing
94
+ return false;
95
+ }
96
+ });
97
+ if (serviceResult == null)
98
+ return false;
99
+ if (!serviceResult.ok) {
100
+ if (failCallback)
101
+ failCallback(serviceResult);
102
+ return false;
103
+ }
104
+ if (serviceResult.data == null) {
105
+ if (failCallback)
106
+ failCallback(this.get('noData'));
107
+ return false;
108
+ }
109
+ this.userLoginEx(serviceResult.data, refreshToken, userData);
110
+ return true;
111
+ };
71
112
  // Call API
72
- const result = await this.coreApi.put('Auth/RefreshToken', data, payload);
113
+ const result = await this.coreApi.put('Auth/RefreshToken', rq, payload);
73
114
  if (result == null)
74
115
  return false;
75
116
  if (!result.ok) {
76
- this.tryLoginResult(result);
77
- return false;
78
- }
79
- // Token
80
- const refreshToken = this.getResponseToken(payload.response);
81
- if (refreshToken == null || result.data == null) {
82
- this.tryLoginResult(this.get('noData'));
83
- return false;
84
- }
85
- // User data
86
- const userData = result.data;
87
- // Use core system access token to service api to exchange service access token
88
- const serviceResult = await this.api.put('Auth/ExchangeToken', {
89
- token: userData.token
90
- }, {
91
- onError: (error) => {
92
- // Set message
93
- this.tryLoginResult(error);
94
- // Prevent further processing
95
- return false;
117
+ if (result.type === 'TokenExpired' && relogin) {
118
+ // Try login
119
+ // Dialog to receive password
120
+ var labels = this.getLabels('reloginTip', 'login');
121
+ this.notifier.prompt(labels.reloginTip, async (pwd) => {
122
+ if (pwd == null) {
123
+ this.toLoginPage();
124
+ return;
125
+ }
126
+ // Set password for the action
127
+ rq.pwd = this.encrypt(pwd, this.settings.serviceId.toString());
128
+ // Submit again
129
+ const result = await this.api.put('Auth/RefreshToken', data, payload);
130
+ if (result == null)
131
+ return;
132
+ if (result.ok) {
133
+ await success(result, (loginResult) => {
134
+ const message = this.formatRefreshTokenResult(loginResult);
135
+ if (message)
136
+ this.notifier.alert(message);
137
+ });
138
+ return;
139
+ }
140
+ // Popup message
141
+ this.alertResult(result);
142
+ return false;
143
+ }, labels.login, { type: 'password' });
144
+ // Fake truth to avoid reloading
145
+ return true;
96
146
  }
97
- });
98
- if (serviceResult == null)
99
- return false;
100
- if (!serviceResult.ok) {
101
- this.tryLoginResult(result);
147
+ if (callback)
148
+ callback(result);
102
149
  return false;
103
150
  }
104
- if (serviceResult.data == null) {
105
- this.tryLoginResult(this.get('noData'));
151
+ return await success(result, callback);
152
+ }
153
+ loginFailed() {
154
+ this.userUnauthorized();
155
+ this.toLoginPage();
156
+ }
157
+ /**
158
+ * Try login
159
+ */
160
+ async tryLogin(data) {
161
+ // Reset user state
162
+ const result = await super.tryLogin(data);
163
+ if (!result)
106
164
  return false;
107
- }
108
- this.userLoginEx(serviceResult.data, refreshToken, userData);
109
- return true;
165
+ // Refresh token
166
+ return await this.refreshToken({
167
+ callback: (result) => {
168
+ const message = this.formatRefreshTokenResult(result);
169
+ if (message == null) {
170
+ this.loginFailed();
171
+ return;
172
+ }
173
+ this.notifier.alert(message, () => {
174
+ this.loginFailed();
175
+ });
176
+ },
177
+ data,
178
+ relogin: true
179
+ });
110
180
  }
111
181
  /**
112
182
  * User login extended
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/react",
3
- "version": "1.3.41",
3
+ "version": "1.3.45",
4
4
  "description": "TypeScript ReactJs framework",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -50,7 +50,7 @@
50
50
  "@emotion/react": "^11.7.0",
51
51
  "@emotion/style": "^0.8.0",
52
52
  "@emotion/styled": "^11.6.0",
53
- "@etsoo/appscript": "^1.1.53",
53
+ "@etsoo/appscript": "^1.1.63",
54
54
  "@etsoo/notificationbase": "^1.0.94",
55
55
  "@etsoo/shared": "^1.0.75",
56
56
  "@mui/icons-material": "^5.2.0",
@@ -83,13 +83,13 @@
83
83
  "@types/react-test-renderer": "^17.0.1",
84
84
  "@typescript-eslint/eslint-plugin": "^5.5.0",
85
85
  "@typescript-eslint/parser": "^5.5.0",
86
- "eslint": "^8.3.0",
86
+ "eslint": "^8.4.0",
87
87
  "eslint-config-airbnb-base": "^15.0.0",
88
88
  "eslint-plugin-import": "^2.25.3",
89
89
  "eslint-plugin-react": "^7.27.1",
90
90
  "jest": "^27.4.3",
91
91
  "react-test-renderer": "^17.0.2",
92
- "ts-jest": "^27.0.7",
92
+ "ts-jest": "^27.1.0",
93
93
  "typescript": "^4.5.2"
94
94
  }
95
95
  }
@@ -2,6 +2,11 @@
2
2
  * Refresh token request data
3
3
  */
4
4
  export type RefreshTokenRQ = {
5
+ /**
6
+ * Login password
7
+ */
8
+ pwd?: string;
9
+
5
10
  /**
6
11
  * Service id
7
12
  */
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  createClient,
3
- IActionResult,
4
3
  IApi,
5
- IApiPayload
4
+ IApiPayload,
5
+ RefreshTokenProps,
6
+ RefreshTokenResult
6
7
  } from '@etsoo/appscript';
7
- import { ApiDataError } from '@etsoo/restclient';
8
8
  import { DomUtils } from '@etsoo/shared';
9
9
  import { CoreConstants } from './CoreConstants';
10
10
  import { IServiceAppSettings } from './IServiceAppSettings';
@@ -72,93 +72,193 @@ export class ServiceApp<
72
72
  }
73
73
 
74
74
  /**
75
- * Try login result processing
76
- * @param result Result
75
+ * Refresh token
76
+ * @param props Props
77
77
  */
78
- protected tryLoginResult(result: string | ApiDataError | IActionResult) {}
78
+ override async refreshToken<D = RefreshTokenRQ>(
79
+ props?: RefreshTokenProps<D>
80
+ ) {
81
+ // Destruct
82
+ const {
83
+ callback,
84
+ data,
85
+ relogin = false,
86
+ showLoading = false
87
+ } = props ?? {};
79
88
 
80
- /**
81
- * Try login
82
- */
83
- override async tryLogin() {
84
89
  // Token
85
90
  const token = this.getCacheToken();
86
91
  if (token == null || token === '') {
87
- this.tryLoginResult('');
92
+ if (callback) callback(false);
88
93
  return false;
89
94
  }
90
95
 
91
96
  // Reqest data
92
- const data: RefreshTokenRQ = {
93
- timezone: this.getTimeZone()
97
+ // Merge additional data passed
98
+ const rq: RefreshTokenRQ = {
99
+ timezone: this.getTimeZone(),
100
+ ...data
94
101
  };
95
102
 
96
103
  // Payload
97
104
  const payload: IApiPayload<SmartERPLoginResult, any> = {
105
+ showLoading,
98
106
  config: { headers: { [CoreConstants.TokenHeaderRefresh]: token } },
99
107
  onError: (error) => {
100
- this.tryLoginResult(error);
108
+ if (callback) callback(error);
101
109
 
102
110
  // Prevent further processing
103
111
  return false;
104
112
  }
105
113
  };
106
114
 
115
+ // Success callback
116
+ const success = async (
117
+ result: SmartERPLoginResult,
118
+ failCallback?: (result: RefreshTokenResult) => void
119
+ ) => {
120
+ // Token
121
+ const refreshToken = this.getResponseToken(payload.response);
122
+ if (refreshToken == null || result.data == null) {
123
+ if (failCallback) failCallback(this.get('noData')!);
124
+ return false;
125
+ }
126
+
127
+ // User data
128
+ const userData = result.data;
129
+
130
+ // Use core system access token to service api to exchange service access token
131
+ const serviceResult = await this.api.put<ServiceLoginResult>(
132
+ 'Auth/ExchangeToken',
133
+ {
134
+ token: userData.token
135
+ },
136
+ {
137
+ showLoading,
138
+ onError: (error) => {
139
+ if (failCallback) failCallback(error);
140
+
141
+ // Prevent further processing
142
+ return false;
143
+ }
144
+ }
145
+ );
146
+
147
+ if (serviceResult == null) return false;
148
+
149
+ if (!serviceResult.ok) {
150
+ if (failCallback) failCallback(serviceResult);
151
+ return false;
152
+ }
153
+
154
+ if (serviceResult.data == null) {
155
+ if (failCallback) failCallback(this.get('noData')!);
156
+ return false;
157
+ }
158
+
159
+ this.userLoginEx(serviceResult.data, refreshToken, userData);
160
+
161
+ return true;
162
+ };
163
+
107
164
  // Call API
108
165
  const result = await this.coreApi.put<SmartERPLoginResult>(
109
166
  'Auth/RefreshToken',
110
- data,
167
+ rq,
111
168
  payload
112
169
  );
113
170
  if (result == null) return false;
114
171
 
115
172
  if (!result.ok) {
116
- this.tryLoginResult(result);
117
- return false;
118
- }
173
+ if (result.type === 'TokenExpired' && relogin) {
174
+ // Try login
175
+ // Dialog to receive password
176
+ var labels = this.getLabels('reloginTip', 'login');
177
+ this.notifier.prompt(
178
+ labels.reloginTip,
179
+ async (pwd) => {
180
+ if (pwd == null) {
181
+ this.toLoginPage();
182
+ return;
183
+ }
119
184
 
120
- // Token
121
- const refreshToken = this.getResponseToken(payload.response);
122
- if (refreshToken == null || result.data == null) {
123
- this.tryLoginResult(this.get('noData')!);
124
- return false;
125
- }
185
+ // Set password for the action
186
+ rq.pwd = this.encrypt(
187
+ pwd,
188
+ this.settings.serviceId.toString()
189
+ );
126
190
 
127
- // User data
128
- const userData = result.data;
191
+ // Submit again
192
+ const result = await this.api.put<SmartERPLoginResult>(
193
+ 'Auth/RefreshToken',
194
+ data,
195
+ payload
196
+ );
129
197
 
130
- // Use core system access token to service api to exchange service access token
131
- const serviceResult = await this.api.put<ServiceLoginResult>(
132
- 'Auth/ExchangeToken',
133
- {
134
- token: userData.token
135
- },
136
- {
137
- onError: (error) => {
138
- // Set message
139
- this.tryLoginResult(error);
198
+ if (result == null) return;
140
199
 
141
- // Prevent further processing
142
- return false;
143
- }
144
- }
145
- );
200
+ if (result.ok) {
201
+ await success(
202
+ result,
203
+ (loginResult: RefreshTokenResult) => {
204
+ const message =
205
+ this.formatRefreshTokenResult(
206
+ loginResult
207
+ );
208
+ if (message) this.notifier.alert(message);
209
+ }
210
+ );
211
+ return;
212
+ }
146
213
 
147
- if (serviceResult == null) return false;
214
+ // Popup message
215
+ this.alertResult(result);
216
+ return false;
217
+ },
218
+ labels.login,
219
+ { type: 'password' }
220
+ );
148
221
 
149
- if (!serviceResult.ok) {
150
- this.tryLoginResult(result);
151
- return false;
152
- }
222
+ // Fake truth to avoid reloading
223
+ return true;
224
+ }
153
225
 
154
- if (serviceResult.data == null) {
155
- this.tryLoginResult(this.get('noData')!);
226
+ if (callback) callback(result);
156
227
  return false;
157
228
  }
158
229
 
159
- this.userLoginEx(serviceResult.data, refreshToken, userData);
230
+ return await success(result, callback);
231
+ }
232
+
233
+ private loginFailed() {
234
+ this.userUnauthorized();
235
+ this.toLoginPage();
236
+ }
160
237
 
161
- return true;
238
+ /**
239
+ * Try login
240
+ */
241
+ override async tryLogin<D extends {} = {}>(data?: D) {
242
+ // Reset user state
243
+ const result = await super.tryLogin(data);
244
+ if (!result) return false;
245
+
246
+ // Refresh token
247
+ return await this.refreshToken({
248
+ callback: (result) => {
249
+ const message = this.formatRefreshTokenResult(result);
250
+ if (message == null) {
251
+ this.loginFailed();
252
+ return;
253
+ }
254
+
255
+ this.notifier.alert(message, () => {
256
+ this.loginFailed();
257
+ });
258
+ },
259
+ data,
260
+ relogin: true
261
+ });
162
262
  }
163
263
 
164
264
  /**