@etsoo/appscript 1.5.40 → 1.5.42

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.
@@ -7,7 +7,7 @@ import { InitCallDto } from '../erp/dto/InitCallDto';
7
7
  import { InitCallResult, InitCallResultData } from '../result/InitCallResult';
8
8
  import { IUser } from '../state/User';
9
9
  import { IAppSettings } from './AppSettings';
10
- import { FormatResultCustomCallback, IApp, IAppFields, IDetectIPCallback, NavigateOptions, RefreshTokenProps, RefreshTokenResult } from './IApp';
10
+ import { AppLoginParams, FormatResultCustomCallback, IApp, IAppFields, IDetectIPCallback, NavigateOptions, RefreshTokenProps, RefreshTokenResult } from './IApp';
11
11
  import { UserRole } from './UserRole';
12
12
  import { ExternalEndpoint } from './ExternalSettings';
13
13
  import { ApiRefreshTokenDto } from '../erp/dto/ApiRefreshTokenDto';
@@ -135,6 +135,11 @@ export declare abstract class CoreApp<U extends IUser, S extends IAppSettings, N
135
135
  */
136
136
  get embedded(): boolean;
137
137
  private _isTryingLogin;
138
+ /**
139
+ * Is trying login
140
+ */
141
+ get isTryingLogin(): boolean;
142
+ protected set isTryingLogin(value: boolean);
138
143
  /**
139
144
  * Last called with token refresh
140
145
  */
@@ -425,7 +430,7 @@ export declare abstract class CoreApp<U extends IUser, S extends IAppSettings, N
425
430
  * @param initCallCallback InitCall callback
426
431
  * @param silent Silent without any popups
427
432
  */
428
- doRefreshTokenResult(result: RefreshTokenResult, initCallCallback?: (result: boolean) => void, silent?: boolean): void;
433
+ doRefreshTokenResult(result: RefreshTokenResult<IActionResult<U>>, initCallCallback?: (result: boolean) => void, silent?: boolean): void;
429
434
  /**
430
435
  * Format as full name
431
436
  * @param familyName Family name
@@ -437,7 +442,7 @@ export declare abstract class CoreApp<U extends IUser, S extends IAppSettings, N
437
442
  * @param result Refresh token result
438
443
  * @returns Message
439
444
  */
440
- formatRefreshTokenResult(result: RefreshTokenResult): string | undefined;
445
+ formatRefreshTokenResult(result: RefreshTokenResult<IActionResult<U>>): string | undefined;
441
446
  private getFieldLabel;
442
447
  /**
443
448
  * Format result text
@@ -630,16 +635,15 @@ export declare abstract class CoreApp<U extends IUser, S extends IAppSettings, N
630
635
  signout(): Promise<void>;
631
636
  /**
632
637
  * Go to the login page
633
- * @param tryLogin Try to login again
634
- * @param removeUrl Remove current URL for reuse
638
+ * params Login parameters
635
639
  */
636
- toLoginPage(tryLogin?: boolean, removeUrl?: boolean): void;
640
+ toLoginPage(params?: AppLoginParams): void;
637
641
  /**
638
642
  * Try login, returning false means is loading
639
643
  * UI get involved while refreshToken not intended
640
- * @param showLoading Show loading bar or not during call
644
+ * @param params Login parameters
641
645
  */
642
- tryLogin(_showLoading?: boolean): Promise<boolean>;
646
+ tryLogin(params?: AppLoginParams): Promise<boolean>;
643
647
  /**
644
648
  * Update embedded status
645
649
  * @param embedded New embedded status
@@ -102,6 +102,15 @@ class CoreApp {
102
102
  get embedded() {
103
103
  return this._embedded;
104
104
  }
105
+ /**
106
+ * Is trying login
107
+ */
108
+ get isTryingLogin() {
109
+ return this._isTryingLogin;
110
+ }
111
+ set isTryingLogin(value) {
112
+ this._isTryingLogin = value;
113
+ }
105
114
  /**
106
115
  * Get persisted fields
107
116
  */
@@ -1009,11 +1018,9 @@ class CoreApp {
1009
1018
  * @param silent Silent without any popups
1010
1019
  */
1011
1020
  doRefreshTokenResult(result, initCallCallback, silent = false) {
1012
- if (result === true)
1013
- return;
1014
- if (typeof result === 'object' &&
1021
+ if (Array.isArray(result) &&
1015
1022
  !(result instanceof restclient_1.ApiDataError) &&
1016
- this.checkDeviceResult(result)) {
1023
+ this.checkDeviceResult(result[1])) {
1017
1024
  initCallCallback ?? (initCallCallback = (result) => {
1018
1025
  if (!result)
1019
1026
  return;
@@ -1057,14 +1064,21 @@ class CoreApp {
1057
1064
  * @returns Message
1058
1065
  */
1059
1066
  formatRefreshTokenResult(result) {
1060
- // Undefined for boolean
1061
- if (typeof result === 'boolean')
1067
+ // Error message
1068
+ if (typeof result === 'string')
1069
+ return result;
1070
+ // API error
1071
+ if (result instanceof restclient_1.ApiDataError)
1072
+ return this.formatError(result);
1073
+ // Action result
1074
+ const [token, r] = result;
1075
+ // Success
1076
+ if (r.ok)
1062
1077
  return undefined;
1063
- return result instanceof restclient_1.ApiDataError
1064
- ? this.formatError(result)
1065
- : typeof result !== 'string'
1066
- ? ActionResultError_1.ActionResultError.format(result)
1067
- : result;
1078
+ // No token data
1079
+ if (token == null)
1080
+ return `${this.get('noData')} (token)`;
1081
+ return ActionResultError_1.ActionResultError.format(r);
1068
1082
  }
1069
1083
  getFieldLabel(field) {
1070
1084
  return this.get(field.formatInitial(false)) ?? field;
@@ -1280,6 +1294,8 @@ class CoreApp {
1280
1294
  */
1281
1295
  getResponseToken(rawResponse, tokenKey) {
1282
1296
  const response = this.api.transformResponse(rawResponse);
1297
+ if (!response.ok)
1298
+ return null;
1283
1299
  return this.api.getHeaderValue(response.headers, tokenKey ?? 'Smarterp-Refresh-Token');
1284
1300
  }
1285
1301
  /**
@@ -1400,8 +1416,6 @@ class CoreApp {
1400
1416
  * @param props Props
1401
1417
  */
1402
1418
  async refreshToken(props) {
1403
- if (props && props.callback)
1404
- props.callback(true, undefined);
1405
1419
  return true;
1406
1420
  }
1407
1421
  /**
@@ -1574,28 +1588,32 @@ class CoreApp {
1574
1588
  // Clear, noTrigger = true, avoid state update
1575
1589
  this.userLogout(true, true);
1576
1590
  // Go to login page
1577
- this.toLoginPage(false, true);
1591
+ this.toLoginPage({ tryLogin: false, removeUrl: true });
1578
1592
  }
1579
1593
  /**
1580
1594
  * Go to the login page
1581
- * @param tryLogin Try to login again
1582
- * @param removeUrl Remove current URL for reuse
1595
+ * params Login parameters
1583
1596
  */
1584
- toLoginPage(tryLogin, removeUrl) {
1597
+ toLoginPage(params) {
1598
+ // Destruct
1599
+ const { removeUrl, showLoading, ...rest } = params ?? {};
1585
1600
  // Save the current URL
1586
1601
  this.cachedUrl = removeUrl ? undefined : globalThis.location.href;
1587
- const url = `/?tryLogin=${tryLogin ?? false}`;
1602
+ // URL with parameters
1603
+ const url = '/'.addUrlParams(rest);
1588
1604
  this.navigate(url);
1589
1605
  }
1590
1606
  /**
1591
1607
  * Try login, returning false means is loading
1592
1608
  * UI get involved while refreshToken not intended
1593
- * @param showLoading Show loading bar or not during call
1609
+ * @param params Login parameters
1594
1610
  */
1595
- async tryLogin(_showLoading) {
1611
+ async tryLogin(params) {
1612
+ // Check status
1596
1613
  if (this._isTryingLogin)
1597
1614
  return false;
1598
1615
  this._isTryingLogin = true;
1616
+ this.toLoginPage(params);
1599
1617
  return true;
1600
1618
  }
1601
1619
  /**
@@ -24,10 +24,10 @@ export interface NavigateOptions {
24
24
  }
25
25
  /**
26
26
  * Refresh token result type
27
- * true means success, false means failed but no any message
27
+ * array means success, false means failed but no any message
28
28
  * other cases means failed with differnet message
29
29
  */
30
- export type RefreshTokenResult = boolean | string | ApiDataError | IActionResult;
30
+ export type RefreshTokenResult<R> = string | ApiDataError | [string | null, R];
31
31
  /**
32
32
  * Format result custom type
33
33
  */
@@ -40,18 +40,39 @@ export type FormatResultCustom = {
40
40
  * Format result custom callback type
41
41
  */
42
42
  export type FormatResultCustomCallback = ((data: FormatResultCustom) => string | null | undefined) | boolean;
43
+ /**
44
+ * Login parameters
45
+ */
46
+ export type AppLoginParams = DataTypes.SimpleObject & {
47
+ /**
48
+ * Try login with cached refresh token
49
+ */
50
+ tryLogin?: boolean;
51
+ /**
52
+ * Don't cache current URL instead of the default page
53
+ */
54
+ removeUrl?: boolean;
55
+ /**
56
+ * Show loading bar or not
57
+ */
58
+ showLoading?: boolean;
59
+ };
43
60
  /**
44
61
  * Refresh token props
45
62
  */
46
63
  export interface RefreshTokenProps {
47
64
  /**
48
- * Callback
65
+ * API name
49
66
  */
50
- callback?: (result: RefreshTokenResult, successData?: string) => void;
67
+ api?: string;
51
68
  /**
52
69
  * Show loading bar or not
53
70
  */
54
71
  showLoading?: boolean;
72
+ /**
73
+ * Header token field name
74
+ */
75
+ tokenField?: string;
55
76
  }
56
77
  /**
57
78
  * App fields
@@ -119,6 +140,10 @@ export interface IApp {
119
140
  * Is the app ready
120
141
  */
121
142
  readonly isReady: boolean;
143
+ /**
144
+ * Is trying to login
145
+ */
146
+ readonly isTryingLogin: boolean;
122
147
  /**
123
148
  * Application name
124
149
  */
@@ -331,7 +356,7 @@ export interface IApp {
331
356
  * @param initCallCallback InitCall callback
332
357
  * @param silent Silent without any popups
333
358
  */
334
- doRefreshTokenResult(result: RefreshTokenResult, initCallCallback?: (result: boolean) => void, silent?: boolean): void;
359
+ doRefreshTokenResult(result: RefreshTokenResult<IActionResult<IUser>>, initCallCallback?: (result: boolean) => void, silent?: boolean): void;
335
360
  /**
336
361
  * Format as full name
337
362
  * @param familyName Family name
@@ -343,7 +368,7 @@ export interface IApp {
343
368
  * @param result Refresh token result
344
369
  * @returns Message
345
370
  */
346
- formatRefreshTokenResult(result: RefreshTokenResult): string | undefined;
371
+ formatRefreshTokenResult(result: RefreshTokenResult<IActionResult<IUser>>): string | undefined;
347
372
  /**
348
373
  * Format result text
349
374
  * @param result Action result
@@ -534,16 +559,15 @@ export interface IApp {
534
559
  persist(): void;
535
560
  /**
536
561
  * Go to the login page
537
- * @param tryLogin Try to login again
538
- * @param removeUrl Remove current URL for reuse
562
+ * @param params Login parameters
539
563
  */
540
- toLoginPage(tryLogin?: boolean, removeUrl?: boolean): void;
564
+ toLoginPage(params?: AppLoginParams): void;
541
565
  /**
542
566
  * Try login, returning false means is loading
543
567
  * UI get involved while refreshToken not intended
544
- * @param showLoading Show loading bar or not
568
+ * @param params Login parameters
545
569
  */
546
- tryLogin(showLoading?: boolean): Promise<boolean>;
570
+ tryLogin(params?: AppLoginParams): Promise<boolean>;
547
571
  /**
548
572
  * Update API token and expires
549
573
  * @param name Api name
@@ -9,6 +9,7 @@ import { SignoutRQ } from './rq/SignoutRQ';
9
9
  import { GetLogInUrlRQ } from './rq/GetLogInUrlRQ';
10
10
  import { TokenRQ } from './rq/TokenRQ';
11
11
  import { ApiRefreshTokenDto } from './dto/ApiRefreshTokenDto';
12
+ import { RefreshTokenProps, RefreshTokenResult } from '../app/IApp';
12
13
  /**
13
14
  * Authentication API
14
15
  */
@@ -49,6 +50,13 @@ export declare class AuthApi extends BaseApi {
49
50
  * @returns Result
50
51
  */
51
52
  loginId(id: string, payload?: ResultPayload): Promise<IActionResult<{}> | undefined>;
53
+ /**
54
+ * Refresh token
55
+ * @param token Refresh token
56
+ * @param props Props
57
+ * @returns Result
58
+ */
59
+ refreshToken<R>(token: string, props?: RefreshTokenProps): Promise<RefreshTokenResult<R>>;
52
60
  /**
53
61
  * Reset password
54
62
  * @param rq Request data
@@ -64,6 +64,39 @@ class AuthApi extends BaseApi_1.BaseApi {
64
64
  };
65
65
  return this.api.post('Auth/LoginId', rq, payload);
66
66
  }
67
+ /**
68
+ * Refresh token
69
+ * @param token Refresh token
70
+ * @param props Props
71
+ * @returns Result
72
+ */
73
+ async refreshToken(token, props) {
74
+ // Destruct
75
+ const { api = 'Auth/RefreshToken', showLoading = false, tokenField = 'Etsoo-Refresh-Token' } = props ?? {};
76
+ // Reqest data
77
+ const rq = {
78
+ deviceId: this.app.deviceId
79
+ };
80
+ // Payload
81
+ const payload = {
82
+ // No loading bar needed to avoid screen flicks
83
+ showLoading,
84
+ config: { headers: { [tokenField]: token } },
85
+ onError: () => {
86
+ // Prevent further processing
87
+ return false;
88
+ }
89
+ };
90
+ // Call API
91
+ const result = await this.api.put(api, rq, payload);
92
+ if (result == null) {
93
+ return this.api.lastError ?? this.app.get('unknownError');
94
+ }
95
+ // Token
96
+ const refreshToken = this.app.getResponseToken(payload.response, tokenField);
97
+ // Success
98
+ return [refreshToken, result];
99
+ }
67
100
  /**
68
101
  * Reset password
69
102
  * @param rq Request data
@@ -7,7 +7,7 @@ import { InitCallDto } from '../erp/dto/InitCallDto';
7
7
  import { InitCallResult, InitCallResultData } from '../result/InitCallResult';
8
8
  import { IUser } from '../state/User';
9
9
  import { IAppSettings } from './AppSettings';
10
- import { FormatResultCustomCallback, IApp, IAppFields, IDetectIPCallback, NavigateOptions, RefreshTokenProps, RefreshTokenResult } from './IApp';
10
+ import { AppLoginParams, FormatResultCustomCallback, IApp, IAppFields, IDetectIPCallback, NavigateOptions, RefreshTokenProps, RefreshTokenResult } from './IApp';
11
11
  import { UserRole } from './UserRole';
12
12
  import { ExternalEndpoint } from './ExternalSettings';
13
13
  import { ApiRefreshTokenDto } from '../erp/dto/ApiRefreshTokenDto';
@@ -135,6 +135,11 @@ export declare abstract class CoreApp<U extends IUser, S extends IAppSettings, N
135
135
  */
136
136
  get embedded(): boolean;
137
137
  private _isTryingLogin;
138
+ /**
139
+ * Is trying login
140
+ */
141
+ get isTryingLogin(): boolean;
142
+ protected set isTryingLogin(value: boolean);
138
143
  /**
139
144
  * Last called with token refresh
140
145
  */
@@ -425,7 +430,7 @@ export declare abstract class CoreApp<U extends IUser, S extends IAppSettings, N
425
430
  * @param initCallCallback InitCall callback
426
431
  * @param silent Silent without any popups
427
432
  */
428
- doRefreshTokenResult(result: RefreshTokenResult, initCallCallback?: (result: boolean) => void, silent?: boolean): void;
433
+ doRefreshTokenResult(result: RefreshTokenResult<IActionResult<U>>, initCallCallback?: (result: boolean) => void, silent?: boolean): void;
429
434
  /**
430
435
  * Format as full name
431
436
  * @param familyName Family name
@@ -437,7 +442,7 @@ export declare abstract class CoreApp<U extends IUser, S extends IAppSettings, N
437
442
  * @param result Refresh token result
438
443
  * @returns Message
439
444
  */
440
- formatRefreshTokenResult(result: RefreshTokenResult): string | undefined;
445
+ formatRefreshTokenResult(result: RefreshTokenResult<IActionResult<U>>): string | undefined;
441
446
  private getFieldLabel;
442
447
  /**
443
448
  * Format result text
@@ -630,16 +635,15 @@ export declare abstract class CoreApp<U extends IUser, S extends IAppSettings, N
630
635
  signout(): Promise<void>;
631
636
  /**
632
637
  * Go to the login page
633
- * @param tryLogin Try to login again
634
- * @param removeUrl Remove current URL for reuse
638
+ * params Login parameters
635
639
  */
636
- toLoginPage(tryLogin?: boolean, removeUrl?: boolean): void;
640
+ toLoginPage(params?: AppLoginParams): void;
637
641
  /**
638
642
  * Try login, returning false means is loading
639
643
  * UI get involved while refreshToken not intended
640
- * @param showLoading Show loading bar or not during call
644
+ * @param params Login parameters
641
645
  */
642
- tryLogin(_showLoading?: boolean): Promise<boolean>;
646
+ tryLogin(params?: AppLoginParams): Promise<boolean>;
643
647
  /**
644
648
  * Update embedded status
645
649
  * @param embedded New embedded status
@@ -99,6 +99,15 @@ export class CoreApp {
99
99
  get embedded() {
100
100
  return this._embedded;
101
101
  }
102
+ /**
103
+ * Is trying login
104
+ */
105
+ get isTryingLogin() {
106
+ return this._isTryingLogin;
107
+ }
108
+ set isTryingLogin(value) {
109
+ this._isTryingLogin = value;
110
+ }
102
111
  /**
103
112
  * Get persisted fields
104
113
  */
@@ -1006,11 +1015,9 @@ export class CoreApp {
1006
1015
  * @param silent Silent without any popups
1007
1016
  */
1008
1017
  doRefreshTokenResult(result, initCallCallback, silent = false) {
1009
- if (result === true)
1010
- return;
1011
- if (typeof result === 'object' &&
1018
+ if (Array.isArray(result) &&
1012
1019
  !(result instanceof ApiDataError) &&
1013
- this.checkDeviceResult(result)) {
1020
+ this.checkDeviceResult(result[1])) {
1014
1021
  initCallCallback ?? (initCallCallback = (result) => {
1015
1022
  if (!result)
1016
1023
  return;
@@ -1054,14 +1061,21 @@ export class CoreApp {
1054
1061
  * @returns Message
1055
1062
  */
1056
1063
  formatRefreshTokenResult(result) {
1057
- // Undefined for boolean
1058
- if (typeof result === 'boolean')
1064
+ // Error message
1065
+ if (typeof result === 'string')
1066
+ return result;
1067
+ // API error
1068
+ if (result instanceof ApiDataError)
1069
+ return this.formatError(result);
1070
+ // Action result
1071
+ const [token, r] = result;
1072
+ // Success
1073
+ if (r.ok)
1059
1074
  return undefined;
1060
- return result instanceof ApiDataError
1061
- ? this.formatError(result)
1062
- : typeof result !== 'string'
1063
- ? ActionResultError.format(result)
1064
- : result;
1075
+ // No token data
1076
+ if (token == null)
1077
+ return `${this.get('noData')} (token)`;
1078
+ return ActionResultError.format(r);
1065
1079
  }
1066
1080
  getFieldLabel(field) {
1067
1081
  return this.get(field.formatInitial(false)) ?? field;
@@ -1277,6 +1291,8 @@ export class CoreApp {
1277
1291
  */
1278
1292
  getResponseToken(rawResponse, tokenKey) {
1279
1293
  const response = this.api.transformResponse(rawResponse);
1294
+ if (!response.ok)
1295
+ return null;
1280
1296
  return this.api.getHeaderValue(response.headers, tokenKey ?? 'Smarterp-Refresh-Token');
1281
1297
  }
1282
1298
  /**
@@ -1397,8 +1413,6 @@ export class CoreApp {
1397
1413
  * @param props Props
1398
1414
  */
1399
1415
  async refreshToken(props) {
1400
- if (props && props.callback)
1401
- props.callback(true, undefined);
1402
1416
  return true;
1403
1417
  }
1404
1418
  /**
@@ -1571,28 +1585,32 @@ export class CoreApp {
1571
1585
  // Clear, noTrigger = true, avoid state update
1572
1586
  this.userLogout(true, true);
1573
1587
  // Go to login page
1574
- this.toLoginPage(false, true);
1588
+ this.toLoginPage({ tryLogin: false, removeUrl: true });
1575
1589
  }
1576
1590
  /**
1577
1591
  * Go to the login page
1578
- * @param tryLogin Try to login again
1579
- * @param removeUrl Remove current URL for reuse
1592
+ * params Login parameters
1580
1593
  */
1581
- toLoginPage(tryLogin, removeUrl) {
1594
+ toLoginPage(params) {
1595
+ // Destruct
1596
+ const { removeUrl, showLoading, ...rest } = params ?? {};
1582
1597
  // Save the current URL
1583
1598
  this.cachedUrl = removeUrl ? undefined : globalThis.location.href;
1584
- const url = `/?tryLogin=${tryLogin ?? false}`;
1599
+ // URL with parameters
1600
+ const url = '/'.addUrlParams(rest);
1585
1601
  this.navigate(url);
1586
1602
  }
1587
1603
  /**
1588
1604
  * Try login, returning false means is loading
1589
1605
  * UI get involved while refreshToken not intended
1590
- * @param showLoading Show loading bar or not during call
1606
+ * @param params Login parameters
1591
1607
  */
1592
- async tryLogin(_showLoading) {
1608
+ async tryLogin(params) {
1609
+ // Check status
1593
1610
  if (this._isTryingLogin)
1594
1611
  return false;
1595
1612
  this._isTryingLogin = true;
1613
+ this.toLoginPage(params);
1596
1614
  return true;
1597
1615
  }
1598
1616
  /**
@@ -24,10 +24,10 @@ export interface NavigateOptions {
24
24
  }
25
25
  /**
26
26
  * Refresh token result type
27
- * true means success, false means failed but no any message
27
+ * array means success, false means failed but no any message
28
28
  * other cases means failed with differnet message
29
29
  */
30
- export type RefreshTokenResult = boolean | string | ApiDataError | IActionResult;
30
+ export type RefreshTokenResult<R> = string | ApiDataError | [string | null, R];
31
31
  /**
32
32
  * Format result custom type
33
33
  */
@@ -40,18 +40,39 @@ export type FormatResultCustom = {
40
40
  * Format result custom callback type
41
41
  */
42
42
  export type FormatResultCustomCallback = ((data: FormatResultCustom) => string | null | undefined) | boolean;
43
+ /**
44
+ * Login parameters
45
+ */
46
+ export type AppLoginParams = DataTypes.SimpleObject & {
47
+ /**
48
+ * Try login with cached refresh token
49
+ */
50
+ tryLogin?: boolean;
51
+ /**
52
+ * Don't cache current URL instead of the default page
53
+ */
54
+ removeUrl?: boolean;
55
+ /**
56
+ * Show loading bar or not
57
+ */
58
+ showLoading?: boolean;
59
+ };
43
60
  /**
44
61
  * Refresh token props
45
62
  */
46
63
  export interface RefreshTokenProps {
47
64
  /**
48
- * Callback
65
+ * API name
49
66
  */
50
- callback?: (result: RefreshTokenResult, successData?: string) => void;
67
+ api?: string;
51
68
  /**
52
69
  * Show loading bar or not
53
70
  */
54
71
  showLoading?: boolean;
72
+ /**
73
+ * Header token field name
74
+ */
75
+ tokenField?: string;
55
76
  }
56
77
  /**
57
78
  * App fields
@@ -119,6 +140,10 @@ export interface IApp {
119
140
  * Is the app ready
120
141
  */
121
142
  readonly isReady: boolean;
143
+ /**
144
+ * Is trying to login
145
+ */
146
+ readonly isTryingLogin: boolean;
122
147
  /**
123
148
  * Application name
124
149
  */
@@ -331,7 +356,7 @@ export interface IApp {
331
356
  * @param initCallCallback InitCall callback
332
357
  * @param silent Silent without any popups
333
358
  */
334
- doRefreshTokenResult(result: RefreshTokenResult, initCallCallback?: (result: boolean) => void, silent?: boolean): void;
359
+ doRefreshTokenResult(result: RefreshTokenResult<IActionResult<IUser>>, initCallCallback?: (result: boolean) => void, silent?: boolean): void;
335
360
  /**
336
361
  * Format as full name
337
362
  * @param familyName Family name
@@ -343,7 +368,7 @@ export interface IApp {
343
368
  * @param result Refresh token result
344
369
  * @returns Message
345
370
  */
346
- formatRefreshTokenResult(result: RefreshTokenResult): string | undefined;
371
+ formatRefreshTokenResult(result: RefreshTokenResult<IActionResult<IUser>>): string | undefined;
347
372
  /**
348
373
  * Format result text
349
374
  * @param result Action result
@@ -534,16 +559,15 @@ export interface IApp {
534
559
  persist(): void;
535
560
  /**
536
561
  * Go to the login page
537
- * @param tryLogin Try to login again
538
- * @param removeUrl Remove current URL for reuse
562
+ * @param params Login parameters
539
563
  */
540
- toLoginPage(tryLogin?: boolean, removeUrl?: boolean): void;
564
+ toLoginPage(params?: AppLoginParams): void;
541
565
  /**
542
566
  * Try login, returning false means is loading
543
567
  * UI get involved while refreshToken not intended
544
- * @param showLoading Show loading bar or not
568
+ * @param params Login parameters
545
569
  */
546
- tryLogin(showLoading?: boolean): Promise<boolean>;
570
+ tryLogin(params?: AppLoginParams): Promise<boolean>;
547
571
  /**
548
572
  * Update API token and expires
549
573
  * @param name Api name
@@ -9,6 +9,7 @@ import { SignoutRQ } from './rq/SignoutRQ';
9
9
  import { GetLogInUrlRQ } from './rq/GetLogInUrlRQ';
10
10
  import { TokenRQ } from './rq/TokenRQ';
11
11
  import { ApiRefreshTokenDto } from './dto/ApiRefreshTokenDto';
12
+ import { RefreshTokenProps, RefreshTokenResult } from '../app/IApp';
12
13
  /**
13
14
  * Authentication API
14
15
  */
@@ -49,6 +50,13 @@ export declare class AuthApi extends BaseApi {
49
50
  * @returns Result
50
51
  */
51
52
  loginId(id: string, payload?: ResultPayload): Promise<IActionResult<{}> | undefined>;
53
+ /**
54
+ * Refresh token
55
+ * @param token Refresh token
56
+ * @param props Props
57
+ * @returns Result
58
+ */
59
+ refreshToken<R>(token: string, props?: RefreshTokenProps): Promise<RefreshTokenResult<R>>;
52
60
  /**
53
61
  * Reset password
54
62
  * @param rq Request data
@@ -61,6 +61,39 @@ export class AuthApi extends BaseApi {
61
61
  };
62
62
  return this.api.post('Auth/LoginId', rq, payload);
63
63
  }
64
+ /**
65
+ * Refresh token
66
+ * @param token Refresh token
67
+ * @param props Props
68
+ * @returns Result
69
+ */
70
+ async refreshToken(token, props) {
71
+ // Destruct
72
+ const { api = 'Auth/RefreshToken', showLoading = false, tokenField = 'Etsoo-Refresh-Token' } = props ?? {};
73
+ // Reqest data
74
+ const rq = {
75
+ deviceId: this.app.deviceId
76
+ };
77
+ // Payload
78
+ const payload = {
79
+ // No loading bar needed to avoid screen flicks
80
+ showLoading,
81
+ config: { headers: { [tokenField]: token } },
82
+ onError: () => {
83
+ // Prevent further processing
84
+ return false;
85
+ }
86
+ };
87
+ // Call API
88
+ const result = await this.api.put(api, rq, payload);
89
+ if (result == null) {
90
+ return this.api.lastError ?? this.app.get('unknownError');
91
+ }
92
+ // Token
93
+ const refreshToken = this.app.getResponseToken(payload.response, tokenField);
94
+ // Success
95
+ return [refreshToken, result];
96
+ }
64
97
  /**
65
98
  * Reset password
66
99
  * @param rq Request data
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/appscript",
3
- "version": "1.5.40",
3
+ "version": "1.5.42",
4
4
  "description": "Applications shared TypeScript framework",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/mjs/index.js",
@@ -33,6 +33,7 @@ import { IUser } from '../state/User';
33
33
  import { IAppSettings } from './AppSettings';
34
34
  import {
35
35
  appFields,
36
+ AppLoginParams,
36
37
  FormatResultCustomCallback,
37
38
  IApp,
38
39
  IAppFields,
@@ -257,6 +258,15 @@ export abstract class CoreApp<
257
258
  }
258
259
 
259
260
  private _isTryingLogin = false;
261
+ /**
262
+ * Is trying login
263
+ */
264
+ get isTryingLogin() {
265
+ return this._isTryingLogin;
266
+ }
267
+ protected set isTryingLogin(value: boolean) {
268
+ this._isTryingLogin = value;
269
+ }
260
270
 
261
271
  /**
262
272
  * Last called with token refresh
@@ -1481,16 +1491,14 @@ export abstract class CoreApp<
1481
1491
  * @param silent Silent without any popups
1482
1492
  */
1483
1493
  doRefreshTokenResult(
1484
- result: RefreshTokenResult,
1494
+ result: RefreshTokenResult<IActionResult<U>>,
1485
1495
  initCallCallback?: (result: boolean) => void,
1486
1496
  silent: boolean = false
1487
1497
  ) {
1488
- if (result === true) return;
1489
-
1490
1498
  if (
1491
- typeof result === 'object' &&
1499
+ Array.isArray(result) &&
1492
1500
  !(result instanceof ApiDataError) &&
1493
- this.checkDeviceResult(result)
1501
+ this.checkDeviceResult(result[1])
1494
1502
  ) {
1495
1503
  initCallCallback ??= (result) => {
1496
1504
  if (!result) return;
@@ -1542,15 +1550,23 @@ export abstract class CoreApp<
1542
1550
  * @param result Refresh token result
1543
1551
  * @returns Message
1544
1552
  */
1545
- formatRefreshTokenResult(result: RefreshTokenResult) {
1546
- // Undefined for boolean
1547
- if (typeof result === 'boolean') return undefined;
1553
+ formatRefreshTokenResult(result: RefreshTokenResult<IActionResult<U>>) {
1554
+ // Error message
1555
+ if (typeof result === 'string') return result;
1556
+
1557
+ // API error
1558
+ if (result instanceof ApiDataError) return this.formatError(result);
1548
1559
 
1549
- return result instanceof ApiDataError
1550
- ? this.formatError(result)
1551
- : typeof result !== 'string'
1552
- ? ActionResultError.format(result)
1553
- : result;
1560
+ // Action result
1561
+ const [token, r] = result;
1562
+
1563
+ // Success
1564
+ if (r.ok) return undefined;
1565
+
1566
+ // No token data
1567
+ if (token == null) return `${this.get('noData')} (token)`;
1568
+
1569
+ return ActionResultError.format(r);
1554
1570
  }
1555
1571
 
1556
1572
  private getFieldLabel(field: string) {
@@ -1805,6 +1821,7 @@ export abstract class CoreApp<
1805
1821
  */
1806
1822
  getResponseToken(rawResponse: any, tokenKey?: string): string | null {
1807
1823
  const response = this.api.transformResponse(rawResponse);
1824
+ if (!response.ok) return null;
1808
1825
  return this.api.getHeaderValue(
1809
1826
  response.headers,
1810
1827
  tokenKey ?? 'Smarterp-Refresh-Token'
@@ -1942,7 +1959,6 @@ export abstract class CoreApp<
1942
1959
  * @param props Props
1943
1960
  */
1944
1961
  async refreshToken(props?: RefreshTokenProps) {
1945
- if (props && props.callback) props.callback(true, undefined);
1946
1962
  return true;
1947
1963
  }
1948
1964
 
@@ -2160,19 +2176,22 @@ export abstract class CoreApp<
2160
2176
  this.userLogout(true, true);
2161
2177
 
2162
2178
  // Go to login page
2163
- this.toLoginPage(false, true);
2179
+ this.toLoginPage({ tryLogin: false, removeUrl: true });
2164
2180
  }
2165
2181
 
2166
2182
  /**
2167
2183
  * Go to the login page
2168
- * @param tryLogin Try to login again
2169
- * @param removeUrl Remove current URL for reuse
2184
+ * params Login parameters
2170
2185
  */
2171
- toLoginPage(tryLogin?: boolean, removeUrl?: boolean) {
2186
+ toLoginPage(params?: AppLoginParams) {
2187
+ // Destruct
2188
+ const { removeUrl, showLoading, ...rest } = params ?? {};
2189
+
2172
2190
  // Save the current URL
2173
2191
  this.cachedUrl = removeUrl ? undefined : globalThis.location.href;
2174
2192
 
2175
- const url = `/?tryLogin=${tryLogin ?? false}`;
2193
+ // URL with parameters
2194
+ const url = '/'.addUrlParams(rest);
2176
2195
 
2177
2196
  this.navigate(url);
2178
2197
  }
@@ -2180,11 +2199,15 @@ export abstract class CoreApp<
2180
2199
  /**
2181
2200
  * Try login, returning false means is loading
2182
2201
  * UI get involved while refreshToken not intended
2183
- * @param showLoading Show loading bar or not during call
2202
+ * @param params Login parameters
2184
2203
  */
2185
- async tryLogin(_showLoading?: boolean) {
2204
+ async tryLogin(params?: AppLoginParams) {
2205
+ // Check status
2186
2206
  if (this._isTryingLogin) return false;
2187
2207
  this._isTryingLogin = true;
2208
+
2209
+ this.toLoginPage(params);
2210
+
2188
2211
  return true;
2189
2212
  }
2190
2213
 
package/src/app/IApp.ts CHANGED
@@ -42,14 +42,10 @@ export interface NavigateOptions {
42
42
 
43
43
  /**
44
44
  * Refresh token result type
45
- * true means success, false means failed but no any message
45
+ * array means success, false means failed but no any message
46
46
  * other cases means failed with differnet message
47
47
  */
48
- export type RefreshTokenResult =
49
- | boolean
50
- | string
51
- | ApiDataError
52
- | IActionResult;
48
+ export type RefreshTokenResult<R> = string | ApiDataError | [string | null, R];
53
49
 
54
50
  /**
55
51
  * Format result custom type
@@ -67,19 +63,44 @@ export type FormatResultCustomCallback =
67
63
  | ((data: FormatResultCustom) => string | null | undefined)
68
64
  | boolean;
69
65
 
66
+ /**
67
+ * Login parameters
68
+ */
69
+ export type AppLoginParams = DataTypes.SimpleObject & {
70
+ /**
71
+ * Try login with cached refresh token
72
+ */
73
+ tryLogin?: boolean;
74
+
75
+ /**
76
+ * Don't cache current URL instead of the default page
77
+ */
78
+ removeUrl?: boolean;
79
+
80
+ /**
81
+ * Show loading bar or not
82
+ */
83
+ showLoading?: boolean;
84
+ };
85
+
70
86
  /**
71
87
  * Refresh token props
72
88
  */
73
89
  export interface RefreshTokenProps {
74
90
  /**
75
- * Callback
91
+ * API name
76
92
  */
77
- callback?: (result: RefreshTokenResult, successData?: string) => void;
93
+ api?: string;
78
94
 
79
95
  /**
80
96
  * Show loading bar or not
81
97
  */
82
98
  showLoading?: boolean;
99
+
100
+ /**
101
+ * Header token field name
102
+ */
103
+ tokenField?: string;
83
104
  }
84
105
 
85
106
  /**
@@ -169,6 +190,11 @@ export interface IApp {
169
190
  */
170
191
  readonly isReady: boolean;
171
192
 
193
+ /**
194
+ * Is trying to login
195
+ */
196
+ readonly isTryingLogin: boolean;
197
+
172
198
  /**
173
199
  * Application name
174
200
  */
@@ -454,7 +480,7 @@ export interface IApp {
454
480
  * @param silent Silent without any popups
455
481
  */
456
482
  doRefreshTokenResult(
457
- result: RefreshTokenResult,
483
+ result: RefreshTokenResult<IActionResult<IUser>>,
458
484
  initCallCallback?: (result: boolean) => void,
459
485
  silent?: boolean
460
486
  ): void;
@@ -474,7 +500,9 @@ export interface IApp {
474
500
  * @param result Refresh token result
475
501
  * @returns Message
476
502
  */
477
- formatRefreshTokenResult(result: RefreshTokenResult): string | undefined;
503
+ formatRefreshTokenResult(
504
+ result: RefreshTokenResult<IActionResult<IUser>>
505
+ ): string | undefined;
478
506
 
479
507
  /**
480
508
  * Format result text
@@ -731,17 +759,16 @@ export interface IApp {
731
759
 
732
760
  /**
733
761
  * Go to the login page
734
- * @param tryLogin Try to login again
735
- * @param removeUrl Remove current URL for reuse
762
+ * @param params Login parameters
736
763
  */
737
- toLoginPage(tryLogin?: boolean, removeUrl?: boolean): void;
764
+ toLoginPage(params?: AppLoginParams): void;
738
765
 
739
766
  /**
740
767
  * Try login, returning false means is loading
741
768
  * UI get involved while refreshToken not intended
742
- * @param showLoading Show loading bar or not
769
+ * @param params Login parameters
743
770
  */
744
- tryLogin(showLoading?: boolean): Promise<boolean>;
771
+ tryLogin(params?: AppLoginParams): Promise<boolean>;
745
772
 
746
773
  /**
747
774
  * Update API token and expires
@@ -10,6 +10,8 @@ import { SignoutRQ } from './rq/SignoutRQ';
10
10
  import { GetLogInUrlRQ } from './rq/GetLogInUrlRQ';
11
11
  import { TokenRQ } from './rq/TokenRQ';
12
12
  import { ApiRefreshTokenDto } from './dto/ApiRefreshTokenDto';
13
+ import { RefreshTokenProps, RefreshTokenResult } from '../app/IApp';
14
+ import { RefreshTokenRQ } from './rq/RefreshTokenRQ';
13
15
 
14
16
  /**
15
17
  * Authentication API
@@ -82,6 +84,55 @@ export class AuthApi extends BaseApi {
82
84
  return this.api.post('Auth/LoginId', rq, payload);
83
85
  }
84
86
 
87
+ /**
88
+ * Refresh token
89
+ * @param token Refresh token
90
+ * @param props Props
91
+ * @returns Result
92
+ */
93
+ async refreshToken<R>(
94
+ token: string,
95
+ props?: RefreshTokenProps
96
+ ): Promise<RefreshTokenResult<R>> {
97
+ // Destruct
98
+ const {
99
+ api = 'Auth/RefreshToken',
100
+ showLoading = false,
101
+ tokenField = 'Etsoo-Refresh-Token'
102
+ } = props ?? {};
103
+
104
+ // Reqest data
105
+ const rq: RefreshTokenRQ = {
106
+ deviceId: this.app.deviceId
107
+ };
108
+
109
+ // Payload
110
+ const payload: IApiPayload<R, any> = {
111
+ // No loading bar needed to avoid screen flicks
112
+ showLoading,
113
+ config: { headers: { [tokenField]: token } },
114
+ onError: () => {
115
+ // Prevent further processing
116
+ return false;
117
+ }
118
+ };
119
+
120
+ // Call API
121
+ const result = await this.api.put(api, rq, payload);
122
+ if (result == null) {
123
+ return this.api.lastError ?? this.app.get('unknownError')!;
124
+ }
125
+
126
+ // Token
127
+ const refreshToken = this.app.getResponseToken(
128
+ payload.response,
129
+ tokenField
130
+ );
131
+
132
+ // Success
133
+ return [refreshToken, result];
134
+ }
135
+
85
136
  /**
86
137
  * Reset password
87
138
  * @param rq Request data