@hmcts/opal-frontend-common 0.0.36 → 0.0.37
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.
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { inject } from '@angular/core';
|
|
2
|
+
import { Router } from '@angular/router';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creates a route guard that checks whether the current URL state matches specific conditions.
|
|
6
|
+
*
|
|
7
|
+
* This function returns a CanActivate guard function that performs the following steps:
|
|
8
|
+
* 1. Retrieves the current state using the provided getState function.
|
|
9
|
+
* 2. Checks the route using the checkRoute function.
|
|
10
|
+
* 3. Evaluates the state and route using the checkCondition function.
|
|
11
|
+
* 4. If the route or condition check fails, it redirects to a URL created by the getNavigationPath function
|
|
12
|
+
* while preserving query parameters and the URL fragment.
|
|
13
|
+
*
|
|
14
|
+
* @typeParam T The type of the state returned by getState.
|
|
15
|
+
*
|
|
16
|
+
* @param getState - A function that returns the current state.
|
|
17
|
+
* @param hasRouteParams - A function that receives an ActivatedRouteSnapshot and returns a boolean indicating whether the route meets certain criteria.
|
|
18
|
+
* @param checkCondition - A function that evaluates whether the current state satisfies the necessary condition based on the ActivatedRouteSnapshot.
|
|
19
|
+
* @param getNavigationPath - A function that receives an ActivatedRouteSnapshot and returns the navigation path as a string for redirection.
|
|
20
|
+
*
|
|
21
|
+
* @returns A function implementing the CanActivate guard that returns `true` if the route and state meet the required conditions,
|
|
22
|
+
* or a UrlTree for redirection if they do not.
|
|
23
|
+
*/
|
|
24
|
+
function hasUrlStateMatchGuard(getState, hasRouteParams, checkCondition, getNavigationPath) {
|
|
25
|
+
return (route) => {
|
|
26
|
+
const router = inject(Router);
|
|
27
|
+
const state = getState();
|
|
28
|
+
const { queryParams, fragment } = route;
|
|
29
|
+
const createRedirectUrlTree = () => {
|
|
30
|
+
const hasQueryParams = Object.keys(queryParams || {}).length > 0;
|
|
31
|
+
return router.createUrlTree([getNavigationPath(route)], {
|
|
32
|
+
queryParams: hasQueryParams ? queryParams : undefined,
|
|
33
|
+
fragment: fragment ?? undefined,
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
if (!hasRouteParams(route)) {
|
|
37
|
+
return createRedirectUrlTree();
|
|
38
|
+
}
|
|
39
|
+
if (!checkCondition(state, route)) {
|
|
40
|
+
return createRedirectUrlTree();
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Generated bundle index. Do not edit.
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
export { hasUrlStateMatchGuard };
|
|
51
|
+
//# sourceMappingURL=hmcts-opal-frontend-common-guards-has-url-state-match.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hmcts-opal-frontend-common-guards-has-url-state-match.mjs","sources":["../../../projects/opal-frontend-common/guards/has-url-state-match/has-url-state-match.guard.ts","../../../projects/opal-frontend-common/guards/has-url-state-match/hmcts-opal-frontend-common-guards-has-url-state-match.ts"],"sourcesContent":["import { inject } from '@angular/core';\nimport { ActivatedRouteSnapshot, CanActivateFn, Router } from '@angular/router';\n\n/**\n * Creates a route guard that checks whether the current URL state matches specific conditions.\n *\n * This function returns a CanActivate guard function that performs the following steps:\n * 1. Retrieves the current state using the provided getState function.\n * 2. Checks the route using the checkRoute function.\n * 3. Evaluates the state and route using the checkCondition function.\n * 4. If the route or condition check fails, it redirects to a URL created by the getNavigationPath function\n * while preserving query parameters and the URL fragment.\n *\n * @typeParam T The type of the state returned by getState.\n *\n * @param getState - A function that returns the current state.\n * @param hasRouteParams - A function that receives an ActivatedRouteSnapshot and returns a boolean indicating whether the route meets certain criteria.\n * @param checkCondition - A function that evaluates whether the current state satisfies the necessary condition based on the ActivatedRouteSnapshot.\n * @param getNavigationPath - A function that receives an ActivatedRouteSnapshot and returns the navigation path as a string for redirection.\n *\n * @returns A function implementing the CanActivate guard that returns `true` if the route and state meet the required conditions,\n * or a UrlTree for redirection if they do not.\n */\nexport function hasUrlStateMatchGuard<T>(\n getState: () => T,\n hasRouteParams: (route: ActivatedRouteSnapshot) => boolean,\n checkCondition: (state: T, route: ActivatedRouteSnapshot) => boolean,\n getNavigationPath: (route: ActivatedRouteSnapshot) => string,\n): CanActivateFn {\n return (route: ActivatedRouteSnapshot) => {\n const router = inject(Router);\n const state = getState();\n const { queryParams, fragment } = route;\n\n const createRedirectUrlTree = () => {\n const hasQueryParams = Object.keys(queryParams || {}).length > 0;\n return router.createUrlTree([getNavigationPath(route)], {\n queryParams: hasQueryParams ? queryParams : undefined,\n fragment: fragment ?? undefined,\n });\n };\n\n if (!hasRouteParams(route)) {\n return createRedirectUrlTree();\n }\n\n if (!checkCondition(state, route)) {\n return createRedirectUrlTree();\n }\n\n return true;\n };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;AAGA;;;;;;;;;;;;;;;;;;;AAmBG;AACG,SAAU,qBAAqB,CACnC,QAAiB,EACjB,cAA0D,EAC1D,cAAoE,EACpE,iBAA4D,EAAA;IAE5D,OAAO,CAAC,KAA6B,KAAI;AACvC,QAAA,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;AAC7B,QAAA,MAAM,KAAK,GAAG,QAAQ,EAAE;AACxB,QAAA,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,KAAK;QAEvC,MAAM,qBAAqB,GAAG,MAAK;AACjC,YAAA,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC;YAChE,OAAO,MAAM,CAAC,aAAa,CAAC,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,EAAE;gBACtD,WAAW,EAAE,cAAc,GAAG,WAAW,GAAG,SAAS;gBACrD,QAAQ,EAAE,QAAQ,IAAI,SAAS;AAChC,aAAA,CAAC;AACJ,QAAA,CAAC;AAED,QAAA,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE;YAC1B,OAAO,qBAAqB,EAAE;QAChC;QAEA,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE;YACjC,OAAO,qBAAqB,EAAE;QAChC;AAEA,QAAA,OAAO,IAAI;AACb,IAAA,CAAC;AACH;;ACpDA;;AAEG;;;;"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Has URL State Match Guard
|
|
2
|
+
|
|
3
|
+
This Angular guard validates URLs and application state, redirecting when either the URL format is invalid or when the state doesn't match the URL parameters. The guard performs two main checks:
|
|
4
|
+
|
|
5
|
+
1. **URL Validation**: Ensures the URL structure is canonical/valid
|
|
6
|
+
2. **State Validation**: Verifies that the application state matches the URL parameters
|
|
7
|
+
|
|
8
|
+
If either check fails, the guard redirects to a specified path while preserving query parameters and fragments.
|
|
9
|
+
|
|
10
|
+
## Table of Contents
|
|
11
|
+
|
|
12
|
+
- [Installation](#installation)
|
|
13
|
+
- [Selector](#selector)
|
|
14
|
+
- [Usage](#usage)
|
|
15
|
+
- [Inputs](#inputs)
|
|
16
|
+
- [Outputs](#outputs)
|
|
17
|
+
- [Methods](#methods)
|
|
18
|
+
- [Testing](#testing)
|
|
19
|
+
- [Contributing](#contributing)
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { hasUrlStateMatchGuard } from '@hmcts/opal-frontend-common/guards/has-url-state-match';
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Ensure the guard is imported in your route configuration:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { hasUrlStateMatchGuard } from '@hmcts/opal-frontend-common/guards/has-url-state-match';
|
|
31
|
+
|
|
32
|
+
const routes: Routes = [
|
|
33
|
+
{
|
|
34
|
+
path: 'account/:accountNumber/summary',
|
|
35
|
+
component: AccountSummaryComponent,
|
|
36
|
+
canActivate: [yourGuardInstance],
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Selector
|
|
42
|
+
|
|
43
|
+
Use this guard factory function to create route guards.
|
|
44
|
+
|
|
45
|
+
hasUrlStateMatchGuard
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
The guard performs validation in the following order:
|
|
50
|
+
|
|
51
|
+
1. **URL Validation**: Calls `hasRouteParams(route)` to check if the URL format is valid
|
|
52
|
+
- If `false`, redirects immediately
|
|
53
|
+
- If `true`, proceeds to state validation
|
|
54
|
+
|
|
55
|
+
2. **State Validation**: Calls `checkCondition(state, route)` to validate state against URL
|
|
56
|
+
- If `false`, redirects
|
|
57
|
+
- If `true`, allows access
|
|
58
|
+
|
|
59
|
+
3. **Query Parameters & Fragments**: When redirecting, preserves existing query parameters and fragments
|
|
60
|
+
|
|
61
|
+
You can create a guard instance by calling the factory function:
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
export const accountValidationGuard = hasUrlStateMatchGuard(
|
|
65
|
+
() => accountService.getState(),
|
|
66
|
+
(route) => !!route.params['accountNumber'], // Returns true if URL is canonical/valid
|
|
67
|
+
(state, route) => state.selectedAccount?.accountNumber === route.params['accountNumber'],
|
|
68
|
+
(route) => `/account/${route.params['accountNumber']}/details`,
|
|
69
|
+
);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Advanced Example
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
export const userAccountGuard = hasUrlStateMatchGuard(
|
|
76
|
+
// Get current state
|
|
77
|
+
() => authService.getCurrentUserState(),
|
|
78
|
+
|
|
79
|
+
// Check if URL format is valid (has required parameters)
|
|
80
|
+
(route) => {
|
|
81
|
+
const accountId = route.params['accountId'];
|
|
82
|
+
const userId = route.queryParams['userId'];
|
|
83
|
+
return !!(accountId && userId && accountId.match(/^\d+$/));
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
// Validate state matches URL parameters
|
|
87
|
+
(state, route) => {
|
|
88
|
+
if (!state.user || !state.selectedAccount) return false;
|
|
89
|
+
return state.user.id === route.queryParams['userId'] && state.selectedAccount.id === route.params['accountId'];
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
// Redirect path when validation fails
|
|
93
|
+
(route) => '/dashboard',
|
|
94
|
+
);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Inputs
|
|
98
|
+
|
|
99
|
+
| Input | Type | Description |
|
|
100
|
+
| ------------------- | ------------------------------------------------------ | ----------------------------------------------------------------------- |
|
|
101
|
+
| `getState` | `() => T` | Function that returns the current application state |
|
|
102
|
+
| `hasRouteParams` | `(route: ActivatedRouteSnapshot) => boolean` | Function that checks if the route params valid (return `true` if valid) |
|
|
103
|
+
| `checkCondition` | `(state: T, route: ActivatedRouteSnapshot) => boolean` | Function that validates state against route (return `true` if valid) |
|
|
104
|
+
| `getNavigationPath` | `(route: ActivatedRouteSnapshot) => string` | Function that returns the redirect path when validation fails |
|
|
105
|
+
|
|
106
|
+
Example usage of inputs:
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
export const userContextGuard = hasUrlStateMatchGuard(
|
|
110
|
+
() => userService.getCurrentUser(),
|
|
111
|
+
(route) => !!route.queryParams['userId'], // Returns true if URL contains required userId
|
|
112
|
+
(state, route) => state?.id === route.queryParams['userId'],
|
|
113
|
+
() => '/login',
|
|
114
|
+
);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Outputs
|
|
118
|
+
|
|
119
|
+
| Output | Type | Description |
|
|
120
|
+
| --------------- | -------------------- | ----------------------------------------------------------------- |
|
|
121
|
+
| `CanActivateFn` | `boolean \| UrlTree` | Returns `true` if access is allowed, or `UrlTree` for redirection |
|
|
122
|
+
|
|
123
|
+
Example of using the output in routes:
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
const routes: Routes = [
|
|
127
|
+
{
|
|
128
|
+
path: 'account/:accountNumber/summary',
|
|
129
|
+
component: AccountSummaryComponent,
|
|
130
|
+
canActivate: [accountValidationGuard],
|
|
131
|
+
},
|
|
132
|
+
];
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Methods
|
|
136
|
+
|
|
137
|
+
There are no custom methods for this guard. It returns a `CanActivateFn` that follows Angular's route guard pattern.
|
|
138
|
+
|
|
139
|
+
### Error Handling
|
|
140
|
+
|
|
141
|
+
The guard does not catch errors thrown by the provided functions. If any of the callback functions (`getState`, `isCanonicalUrl`, `checkCondition`, or `getNavigationPath`) throw an error, it will bubble up and should be handled by your application's error handling mechanisms.
|
|
142
|
+
|
|
143
|
+
### Query Parameters and Fragments
|
|
144
|
+
|
|
145
|
+
When redirecting, the guard:
|
|
146
|
+
|
|
147
|
+
- Preserves existing query parameters (only if they exist and are non-empty)
|
|
148
|
+
- Preserves URL fragments
|
|
149
|
+
- Passes both to the redirect URL
|
|
150
|
+
|
|
151
|
+
## Testing
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
yarn test
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Contributing
|
|
158
|
+
|
|
159
|
+
Feel free to submit issues or pull requests to improve this guard.
|
|
160
|
+
If you encounter any bugs or missing functionality, please raise an issue.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ActivatedRouteSnapshot, CanActivateFn } from '@angular/router';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a route guard that checks whether the current URL state matches specific conditions.
|
|
5
|
+
*
|
|
6
|
+
* This function returns a CanActivate guard function that performs the following steps:
|
|
7
|
+
* 1. Retrieves the current state using the provided getState function.
|
|
8
|
+
* 2. Checks the route using the checkRoute function.
|
|
9
|
+
* 3. Evaluates the state and route using the checkCondition function.
|
|
10
|
+
* 4. If the route or condition check fails, it redirects to a URL created by the getNavigationPath function
|
|
11
|
+
* while preserving query parameters and the URL fragment.
|
|
12
|
+
*
|
|
13
|
+
* @typeParam T The type of the state returned by getState.
|
|
14
|
+
*
|
|
15
|
+
* @param getState - A function that returns the current state.
|
|
16
|
+
* @param hasRouteParams - A function that receives an ActivatedRouteSnapshot and returns a boolean indicating whether the route meets certain criteria.
|
|
17
|
+
* @param checkCondition - A function that evaluates whether the current state satisfies the necessary condition based on the ActivatedRouteSnapshot.
|
|
18
|
+
* @param getNavigationPath - A function that receives an ActivatedRouteSnapshot and returns the navigation path as a string for redirection.
|
|
19
|
+
*
|
|
20
|
+
* @returns A function implementing the CanActivate guard that returns `true` if the route and state meet the required conditions,
|
|
21
|
+
* or a UrlTree for redirection if they do not.
|
|
22
|
+
*/
|
|
23
|
+
declare function hasUrlStateMatchGuard<T>(getState: () => T, hasRouteParams: (route: ActivatedRouteSnapshot) => boolean, checkCondition: (state: T, route: ActivatedRouteSnapshot) => boolean, getNavigationPath: (route: ActivatedRouteSnapshot) => string): CanActivateFn;
|
|
24
|
+
|
|
25
|
+
export { hasUrlStateMatchGuard };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hmcts/opal-frontend-common",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.37",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"peerDependencies": {
|
|
6
6
|
"@angular/common": "^18.2.0 || ^19.0.0 || ^20.0.0",
|
|
@@ -547,6 +547,11 @@
|
|
|
547
547
|
"types": "./guards/has-flow-state/index.d.ts",
|
|
548
548
|
"default": "./fesm2022/hmcts-opal-frontend-common-guards-has-flow-state.mjs"
|
|
549
549
|
},
|
|
550
|
+
"./guards/has-url-state-match": {
|
|
551
|
+
"import": "./fesm2022/hmcts-opal-frontend-common-guards-has-url-state-match.mjs",
|
|
552
|
+
"types": "./guards/has-url-state-match/index.d.ts",
|
|
553
|
+
"default": "./fesm2022/hmcts-opal-frontend-common-guards-has-url-state-match.mjs"
|
|
554
|
+
},
|
|
550
555
|
"./guards/helpers": {
|
|
551
556
|
"import": "./fesm2022/hmcts-opal-frontend-common-guards-helpers.mjs",
|
|
552
557
|
"types": "./guards/helpers/index.d.ts",
|