@abgov/nx-adsp 12.5.0 → 12.6.0
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/package.json
CHANGED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# AGENTS.md — <%= projectName %>
|
|
2
|
+
|
|
3
|
+
Angular 19 frontend for the Alberta Digital Service Platform (ADSP).
|
|
4
|
+
Generated by `nx g @abgov/nx-adsp:angular-app`.
|
|
5
|
+
|
|
6
|
+
## Stack
|
|
7
|
+
|
|
8
|
+
- **UI**: Angular 19 standalone + GoA design system (`@abgov/angular-components` — `Goab*` components)
|
|
9
|
+
- **Auth**: `keycloak-angular` with `keycloak-js`
|
|
10
|
+
- **Router**: Angular Router with `createAuthGuard` from `keycloak-angular`
|
|
11
|
+
|
|
12
|
+
## Key files
|
|
13
|
+
|
|
14
|
+
| File | Purpose |
|
|
15
|
+
|------|---------|
|
|
16
|
+
| `src/main.ts` | Entry — bootstraps `AppComponent` with `appConfig`, imports `zone.js` |
|
|
17
|
+
| `src/app/app.config.ts` | `provideKeycloak` (PKCE, silent SSO), `provideRouter`, `provideHttpClient` |
|
|
18
|
+
| `src/app/app.component.ts` | Shell — auth state, hero banner background workaround, public API call |
|
|
19
|
+
| `src/app/app.routes.ts` | Routes — `/protected` with `createAuthGuard` |
|
|
20
|
+
| `src/app/protected/protected.component.ts` | Protected route — shows authenticated user info |
|
|
21
|
+
| `src/environments/environment.ts` | Access URL, realm, client ID — pre-set from ADSP tenant |
|
|
22
|
+
|
|
23
|
+
## Auth pattern
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import Keycloak from 'keycloak-js';
|
|
27
|
+
import { inject } from '@angular/core';
|
|
28
|
+
|
|
29
|
+
private keycloak = inject(Keycloak); // provided by provideKeycloak in app.config.ts
|
|
30
|
+
|
|
31
|
+
this.keycloak.authenticated // boolean
|
|
32
|
+
this.keycloak.tokenParsed?.['name'] // user display name
|
|
33
|
+
this.keycloak.login()
|
|
34
|
+
this.keycloak.logout({ redirectUri: window.location.origin })
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Route guard using `createAuthGuard` from `keycloak-angular`:
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
const authGuard = createAuthGuard(async (_route, _state, { authenticated, keycloak }) => {
|
|
41
|
+
if (authenticated) return true;
|
|
42
|
+
await keycloak.login({ redirectUri: window.location.href });
|
|
43
|
+
return false;
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## GoA design system
|
|
48
|
+
|
|
49
|
+
Components are imported from `@abgov/angular-components` with the `Goab` prefix
|
|
50
|
+
and added to each standalone component's `imports` array:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { GoabButton, GoabAppHeader } from '@abgov/angular-components';
|
|
54
|
+
|
|
55
|
+
@Component({ imports: [GoabButton, GoabAppHeader], ... })
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Known limitation:** `GoabHeroBanner.backgroundUrl` does not bind reliably via
|
|
59
|
+
Angular's template binding due to an `@if(isReady)` timing issue inside the
|
|
60
|
+
component wrapper. Use `MutationObserver` to set the property directly after
|
|
61
|
+
the inner `<goa-hero-banner>` element appears — see `app.component.ts` for the
|
|
62
|
+
established pattern.
|
|
63
|
+
|
|
64
|
+
## Backend API calls (mean / proxy setup)
|
|
65
|
+
|
|
66
|
+
If `proxy.conf.json` exists at the project root, API calls are proxied to the
|
|
67
|
+
backend service in development. Use relative `/api/` paths — do not hardcode
|
|
68
|
+
the service URL:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
// ✓ correct — works through proxy in dev, nginx in production
|
|
72
|
+
this.http.get('/api/v1/my-resource')
|
|
73
|
+
|
|
74
|
+
// ✗ wrong — bypasses proxy, won't work in production
|
|
75
|
+
this.http.get('http://localhost:3333/my-service/v1/my-resource')
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`includeBearerTokenInterceptor` (configured in `app.config.ts`) automatically
|
|
79
|
+
attaches the Keycloak access token to all outgoing HTTP requests.
|
|
80
|
+
In production, `nginx.conf` contains the same proxy rule routing `/api/` to
|
|
81
|
+
the backend service hostname.
|
|
82
|
+
|
|
83
|
+
## Adding a new route
|
|
84
|
+
|
|
85
|
+
1. Create `src/app/my-feature/my-feature.component.ts` as a standalone component
|
|
86
|
+
2. Add the route to `src/app/app.routes.ts`
|
|
87
|
+
3. Add required `Goab*` components to the new component's `imports` array
|
|
88
|
+
|
|
89
|
+
## What NOT to change
|
|
90
|
+
|
|
91
|
+
- `app.config.ts` — `provideKeycloak` initialises keycloak-js once; do not
|
|
92
|
+
create a second Keycloak instance elsewhere
|
|
93
|
+
- `silent-check-sso.html` — served from `public/`; required for keycloak-js
|
|
94
|
+
silent SSO check on page load
|
|
95
|
+
- `environments/environment.ts` — access URL and realm are pre-configured for
|
|
96
|
+
the ADSP tenant; override at runtime via environment variables
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# AGENTS.md — <%= projectName %>
|
|
2
|
+
|
|
3
|
+
Node/Express backend service for the Alberta Digital Service Platform (ADSP).
|
|
4
|
+
Generated by `nx g @abgov/nx-adsp:express-service`.
|
|
5
|
+
|
|
6
|
+
## Stack
|
|
7
|
+
|
|
8
|
+
- **Runtime**: Node.js + Express
|
|
9
|
+
- **Auth**: passport.js with tenant strategy from `@abgov/adsp-service-sdk`
|
|
10
|
+
- **Config**: `envalid` with `.env` file (see `src/environment.ts`)
|
|
11
|
+
- **SDK**: `@abgov/adsp-service-sdk` — provides service registration, auth,
|
|
12
|
+
event publishing, configuration management, and service discovery
|
|
13
|
+
|
|
14
|
+
## Key files
|
|
15
|
+
|
|
16
|
+
| File | Purpose |
|
|
17
|
+
|------|---------|
|
|
18
|
+
| `src/main.ts` | App entry — SDK init, middleware, routes, server start |
|
|
19
|
+
| `src/environment.ts` | Validated env config with defaults pre-set from ADSP tenant |
|
|
20
|
+
|
|
21
|
+
## SDK capabilities
|
|
22
|
+
|
|
23
|
+
`initializeService()` returns `capabilities`:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
const { logger, tenantStrategy, traceHandler, configurationHandler,
|
|
27
|
+
healthCheck, directory, tokenProvider, eventService } = capabilities;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
To resolve another ADSP service URL:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
const url = await directory.getServiceUrl(AdspId.parse('urn:ads:platform:file-service'));
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
To call another service with an access token:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
const token = await tokenProvider.getAccessToken();
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Adding routes
|
|
43
|
+
|
|
44
|
+
Add route handlers in `main.ts` under the `/<%= projectName %>/v1` path prefix:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
app.get('/<%= projectName %>/v1/my-resource', (req, res) => {
|
|
48
|
+
const user = req.user; // null for anonymous, populated for authenticated
|
|
49
|
+
res.json({ ... });
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`passport.authenticate(['tenant', 'anonymous'])` is applied to the entire
|
|
54
|
+
`/<%= projectName %>/v1` prefix — check `req.user` inside handlers to
|
|
55
|
+
distinguish authenticated from anonymous access.
|
|
56
|
+
|
|
57
|
+
## Frontend proxy integration (mern / mean stacks)
|
|
58
|
+
|
|
59
|
+
When this service is paired with a React or Angular frontend via the `mern` or
|
|
60
|
+
`mean` generators, the frontend proxies `/api/` to this service:
|
|
61
|
+
|
|
62
|
+
- **Dev** (webpack / Vite): `/api/v1/my-resource` → `/<%= projectName %>/v1/my-resource` on `localhost:3333`
|
|
63
|
+
- **Production** (nginx): same rewrite via the nginx proxy block in the frontend app
|
|
64
|
+
|
|
65
|
+
All API routes in this service live under `/<%= projectName %>/v1/`. The proxy
|
|
66
|
+
rewrites the frontend's `/api/` prefix to `/<%= projectName %>/` before the
|
|
67
|
+
request reaches Express, so `/<%= projectName %>/v1/public` maps to the
|
|
68
|
+
frontend's `/api/v1/public`.
|
|
69
|
+
|
|
70
|
+
## What NOT to change
|
|
71
|
+
|
|
72
|
+
- `initializeService(...)` config — `CLIENT_ID` and `CLIENT_SECRET` are read
|
|
73
|
+
from environment; do not hardcode credentials
|
|
74
|
+
- The passport strategy setup — the tenant strategy handles JWT validation
|
|
75
|
+
- `configurationHandler` on the API path — provides configuration service
|
|
76
|
+
integration; keep it applied before route handlers
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# AGENTS.md — <%= projectName %>
|
|
2
|
+
|
|
3
|
+
React frontend for the Alberta Digital Service Platform (ADSP).
|
|
4
|
+
Generated by `nx g @abgov/nx-adsp:react-app`.
|
|
5
|
+
|
|
6
|
+
## Stack
|
|
7
|
+
|
|
8
|
+
- **UI**: React 18 + GoA design system (`@abgov/react-components` — `Goab*` components)
|
|
9
|
+
- **Auth**: `keycloak-js` via Redux Toolkit slice (`src/app/user.slice.ts`)
|
|
10
|
+
- **State**: Redux Toolkit — store in `src/store.ts`
|
|
11
|
+
- **Router**: React Router v6
|
|
12
|
+
|
|
13
|
+
## Key files
|
|
14
|
+
|
|
15
|
+
| File | Purpose |
|
|
16
|
+
|------|---------|
|
|
17
|
+
| `src/main.tsx` | Entry — Redux Provider, BrowserRouter, dispatches `initializeUser()` after render |
|
|
18
|
+
| `src/app/user.slice.ts` | Keycloak auth — login, logout, token refresh, `getAccessToken()` |
|
|
19
|
+
| `src/app/start.slice.ts` | Example public/private API calls |
|
|
20
|
+
| `src/app/config.slice.ts` | Loads ADSP directory service endpoints on startup |
|
|
21
|
+
| `src/app/intake.slice.ts` | Application domain state — extend this for your feature |
|
|
22
|
+
| `src/store.ts` | Redux store — add new slices here |
|
|
23
|
+
| `src/environments/environment.ts` | Access URL, realm, client ID — pre-set from ADSP tenant |
|
|
24
|
+
|
|
25
|
+
## Auth pattern
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { getAccessToken, loginUser, logoutUser, userSelector } from './user.slice';
|
|
29
|
+
|
|
30
|
+
// In a component:
|
|
31
|
+
const { authenticated, name } = useSelector(userSelector);
|
|
32
|
+
dispatch(loginUser()); // redirects to Keycloak
|
|
33
|
+
dispatch(logoutUser()); // redirects to Keycloak logout
|
|
34
|
+
|
|
35
|
+
// In a thunk (for authenticated API calls):
|
|
36
|
+
const token = await getAccessToken(); // refreshes token if needed
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## GoA design system
|
|
40
|
+
|
|
41
|
+
Components are imported from `@abgov/react-components` with the `Goab` prefix:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { GoabButton, GoabAppHeader, GoabButtonGroup } from '@abgov/react-components';
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Adding a new Redux slice
|
|
48
|
+
|
|
49
|
+
1. Create `src/app/my-feature.slice.ts` using `createSlice` / `createAsyncThunk`
|
|
50
|
+
2. Export the reducer and add it to `src/store.ts`
|
|
51
|
+
|
|
52
|
+
## Backend API calls (mern / proxy setup)
|
|
53
|
+
|
|
54
|
+
If `proxy.conf.json` exists at the project root, API calls are proxied to the
|
|
55
|
+
backend service in development. Use relative `/api/` paths — do not hardcode
|
|
56
|
+
the service URL:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// ✓ correct — works through proxy in dev, nginx in production
|
|
60
|
+
const response = await fetch('/api/v1/my-resource');
|
|
61
|
+
|
|
62
|
+
// ✗ wrong — bypasses proxy, won't work in production
|
|
63
|
+
const response = await fetch('http://localhost:3333/my-service/v1/my-resource');
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
In production, `nginx.conf` contains the same proxy rule routing `/api/` to
|
|
67
|
+
the backend service hostname.
|
|
68
|
+
|
|
69
|
+
## What NOT to change
|
|
70
|
+
|
|
71
|
+
- `user.slice.ts` — keycloak-js is initialised once per app lifecycle; do not
|
|
72
|
+
create a second Keycloak instance
|
|
73
|
+
- `environments/environment.ts` — access URL and realm are pre-configured for
|
|
74
|
+
the ADSP tenant; override at runtime via deployment config
|
|
75
|
+
- `silent-check-sso.html` — required for keycloak-js silent SSO; must be served
|
|
76
|
+
from the app root
|