@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.36",
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",