@abgov/nx-adsp 12.10.0 → 12.11.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/README.md +167 -23
- package/package.json +1 -1
- package/src/generators/express-service/express-service.js +58 -22
- package/src/generators/express-service/express-service.js.map +1 -1
- package/src/generators/express-service/express-service.spec.ts +55 -0
- package/src/generators/express-service/files/AGENTS.md__tmpl__ +183 -0
- package/src/generators/express-service/files/src/environment.ts__tmpl__ +11 -0
- package/src/generators/express-service/files/src/events.ts__tmpl__ +30 -0
- package/src/generators/express-service/files/src/main.ts__tmpl__ +33 -4
- package/src/generators/express-service/files-mongo/.env.example__tmpl__ +8 -0
- package/src/generators/express-service/files-mongo/scripts/dev-db.sh__tmpl__ +38 -0
- package/src/generators/express-service/files-mongo/src/database.ts__tmpl__ +11 -0
- package/src/generators/express-service/files-postgres/.env.example__tmpl__ +8 -0
- package/src/generators/express-service/files-postgres/prisma/migrations/.gitkeep +0 -0
- package/src/generators/express-service/files-postgres/prisma/schema.prisma__tmpl__ +14 -0
- package/src/generators/express-service/files-postgres/scripts/dev-db.sh__tmpl__ +43 -0
- package/src/generators/express-service/files-postgres/src/database.ts__tmpl__ +3 -0
- package/src/generators/express-service/schema.d.ts +5 -0
- package/src/generators/express-service/schema.json +15 -0
- package/src/generators/mean/mean.js +9 -7
- package/src/generators/mean/mean.js.map +1 -1
- package/src/generators/mern/mern.js +9 -7
- package/src/generators/mern/mern.js.map +1 -1
package/README.md
CHANGED
|
@@ -1,35 +1,179 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @abgov/nx-adsp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Nx plugin for bootstrapping [ADSP](https://govalta.github.io/adsp-monorepo/) applications for the Government of Alberta.
|
|
4
4
|
|
|
5
|
-
The plugin
|
|
5
|
+
The plugin provides generators for Node/Express services, React and Angular frontends, .NET services, and fullstack solutions. When `@abgov/nx-oc` is also installed in the workspace, OpenShift deployment YAML is automatically included in the generated output.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Prerequisites
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
| Generator | Required peer dependency |
|
|
10
|
+
|-----------|--------------------------|
|
|
11
|
+
| `react-app` | `@nx/react` |
|
|
12
|
+
| `angular-app` | `@nx/angular` |
|
|
13
|
+
| `dotnet-service` | `@nx-dotnet/core` |
|
|
14
|
+
| `react-dotnet` | `@nx/react`, `@nx-dotnet/core` |
|
|
15
|
+
| `express-service` | `@nx/node` |
|
|
16
|
+
| `react-form`, `react-task-list` | existing React project in the workspace |
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
## Installation
|
|
14
19
|
|
|
15
|
-
|
|
20
|
+
```bash
|
|
21
|
+
npm i -D @abgov/nx-adsp
|
|
22
|
+
```
|
|
16
23
|
|
|
17
|
-
##
|
|
24
|
+
## Quick Start
|
|
18
25
|
|
|
19
|
-
|
|
20
|
-
|
|
26
|
+
```bash
|
|
27
|
+
# 1. Create a workspace
|
|
28
|
+
npx create-nx-workspace my-workspace
|
|
21
29
|
|
|
22
|
-
|
|
30
|
+
# 2. Install the plugin and any required peer dependencies
|
|
31
|
+
npm i -D @abgov/nx-adsp @nx/node
|
|
23
32
|
|
|
24
|
-
|
|
25
|
-
|
|
33
|
+
# 3. Generate a quickstart (interactive prompts fill missing options)
|
|
34
|
+
npx nx g @abgov/nx-adsp:express-service my-service --env dev --tenant my-tenant
|
|
35
|
+
```
|
|
26
36
|
|
|
27
|
-
|
|
28
|
-
i) run
|
|
29
|
-
npm i -D [location-of-nx-tools]/dist/packages/nx-adsp //to update package.json
|
|
30
|
-
npm i -D @nx/angular //(this assumes you are building an angular plugin)
|
|
37
|
+
## Generators
|
|
31
38
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
### `express-service`
|
|
40
|
+
|
|
41
|
+
Creates a Node/Express backend service configured for ADSP.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx nx g @abgov/nx-adsp:express-service my-service --env dev --tenant my-tenant
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
| Option | Alias | Required | Description |
|
|
48
|
+
|--------|-------|----------|-------------|
|
|
49
|
+
| `name` | — | Yes | Name of the service |
|
|
50
|
+
| `env` | `-e` | Yes | ADSP environment: `dev`, `test`, or `prod` |
|
|
51
|
+
| `tenant` | `-t` | No | ADSP tenant name; looks up the Keycloak realm automatically via a single browser login |
|
|
52
|
+
| `tenantRealm` | `-tr` | No | Keycloak realm UUID; overrides the realm resolved from `--tenant` |
|
|
53
|
+
| `accessToken` | `-at` | No | Access token for non-interactive retrieval of ADSP configuration |
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
### `react-app`
|
|
58
|
+
|
|
59
|
+
Creates a React/Redux frontend application configured for ADSP. Requires `@nx/react`.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npx nx g @abgov/nx-adsp:react-app my-app --env dev --tenant my-tenant
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
| Option | Alias | Required | Description |
|
|
66
|
+
|--------|-------|----------|-------------|
|
|
67
|
+
| `name` | — | Yes | Name of the application |
|
|
68
|
+
| `env` | `-e` | Yes | ADSP environment: `dev`, `test`, or `prod` |
|
|
69
|
+
| `tenant` | `-t` | No | ADSP tenant name; looks up the Keycloak realm automatically via a single browser login |
|
|
70
|
+
| `tenantRealm` | `-tr` | No | Keycloak realm UUID; overrides the realm resolved from `--tenant` |
|
|
71
|
+
| `accessToken` | `-at` | No | Access token for non-interactive retrieval of ADSP configuration |
|
|
72
|
+
| `proxy` | — | No | Nginx proxy rule(s) as `{ location, proxyPass }` or an array of such objects |
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
### `angular-app`
|
|
77
|
+
|
|
78
|
+
Creates an Angular frontend application configured for ADSP. Requires `@nx/angular`.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npx nx g @abgov/nx-adsp:angular-app my-app --env dev --tenant my-tenant
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Accepts the same options as `react-app`.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
### `dotnet-service`
|
|
89
|
+
|
|
90
|
+
Creates an ASP.NET Core backend service configured for ADSP. Requires `@nx-dotnet/core`.
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npx nx g @abgov/nx-adsp:dotnet-service my-service --env dev --accessToken $TOKEN
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
| Option | Alias | Required | Description |
|
|
97
|
+
|--------|-------|----------|-------------|
|
|
98
|
+
| `name` | — | Yes | Name of the service |
|
|
99
|
+
| `env` | `-e` | Yes | ADSP environment: `dev`, `test`, or `prod` |
|
|
100
|
+
| `accessToken` | `-at` | No | Access token for non-interactive retrieval of ADSP configuration |
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
### `react-dotnet`
|
|
105
|
+
|
|
106
|
+
Composite generator that creates both a React frontend and a .NET backend as a fullstack solution. Requires `@nx/react` and `@nx-dotnet/core`.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npx nx g @abgov/nx-adsp:react-dotnet my-solution --env dev
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Accepts the same options as `dotnet-service`.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
### `react-form`
|
|
117
|
+
|
|
118
|
+
Adds a React component generated from an existing [ADSP Form Definition](https://govalta.github.io/adsp-monorepo/) to an existing project. The generator fetches form definitions from the ADSP Form service for the target environment.
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
npx nx g @abgov/nx-adsp:react-form my-app --env test
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
| Option | Alias | Required | Description |
|
|
125
|
+
|--------|-------|----------|-------------|
|
|
126
|
+
| `project` | — | Yes | Name of the existing Nx project to add the form component to |
|
|
127
|
+
| `env` | `-e` | Yes | ADSP environment to fetch form definitions from (typically `test`) |
|
|
128
|
+
| `accessToken` | `-at` | No | Access token for non-interactive retrieval of ADSP configuration |
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
### `react-task-list`
|
|
133
|
+
|
|
134
|
+
Adds a React task list component driven by an [ADSP Task Queue](https://govalta.github.io/adsp-monorepo/) to an existing project.
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
npx nx g @abgov/nx-adsp:react-task-list my-app --env test
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Accepts the same options as `react-form`.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Authentication
|
|
145
|
+
|
|
146
|
+
Most generators call ADSP APIs to retrieve tenant-specific configuration during generation. Three authentication methods are supported:
|
|
147
|
+
|
|
148
|
+
| Method | When to use |
|
|
149
|
+
|--------|-------------|
|
|
150
|
+
| `--tenant <name>` | Preferred for interactive use; looks up the Keycloak realm by tenant name and opens a single browser login |
|
|
151
|
+
| `--tenantRealm <uuid>` | Use when you already know the realm UUID; can be combined with `--tenant` to override the auto-resolved realm |
|
|
152
|
+
| `--accessToken <token>` | Use in CI/CD or scripts to skip interactive login entirely |
|
|
153
|
+
|
|
154
|
+
If neither `--tenant`, `--tenantRealm`, nor `--accessToken` is provided, the generator will prompt interactively.
|
|
155
|
+
|
|
156
|
+
## nx-oc integration
|
|
157
|
+
|
|
158
|
+
If `@abgov/nx-oc` is installed in the workspace, the quickstart generators (`express-service`, `react-app`, `angular-app`, `dotnet-service`, `react-dotnet`) automatically include OpenShift deployment YAML in their output. See the [@abgov/nx-oc README](../nx-oc/README.md) for details.
|
|
159
|
+
|
|
160
|
+
## Local development
|
|
161
|
+
|
|
162
|
+
To test the plugin locally against a workspace:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# 1. Build the plugin
|
|
166
|
+
nx run nx-adsp:build
|
|
167
|
+
|
|
168
|
+
# 2. In a separate test workspace, install from the build output
|
|
169
|
+
npm i -D /path/to/nx-tools/dist/packages/nx-adsp
|
|
170
|
+
|
|
171
|
+
# 3. Run a generator
|
|
172
|
+
npx nx g @abgov/nx-adsp:express-service my-service --tenant my-tenant-realm-uuid
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
To add a new generator to this plugin:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
nx g @nx/plugin:generator [generatorName] --project nx-adsp
|
|
179
|
+
```
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@ const agent_1 = require("../../utils/agent");
|
|
|
10
10
|
const plugin_version_1 = require("../../utils/plugin-version");
|
|
11
11
|
function normalizeOptions(host, options) {
|
|
12
12
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
13
|
-
var _a, _b, _c;
|
|
13
|
+
var _a, _b, _c, _d;
|
|
14
14
|
const projectName = (0, devkit_1.names)(options.name).fileName;
|
|
15
15
|
const projectRoot = `${(0, devkit_1.getWorkspaceLayout)(host).appsDir}/${projectName}`;
|
|
16
16
|
let adsp;
|
|
@@ -41,35 +41,71 @@ function normalizeOptions(host, options) {
|
|
|
41
41
|
}
|
|
42
42
|
return Object.assign(Object.assign({}, options), { projectName,
|
|
43
43
|
projectRoot,
|
|
44
|
-
adsp });
|
|
44
|
+
adsp, database: (_d = options.database) !== null && _d !== void 0 ? _d : 'none' });
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
function addFiles(host, options) {
|
|
48
48
|
const templateOptions = Object.assign(Object.assign(Object.assign({}, options), options.adsp), { tmpl: '' });
|
|
49
49
|
(0, devkit_1.generateFiles)(host, path.join(__dirname, 'files'), options.projectRoot, templateOptions);
|
|
50
|
+
if (options.database === 'postgres' || options.database === 'mongo') {
|
|
51
|
+
(0, devkit_1.generateFiles)(host, path.join(__dirname, `files-${options.database}`), options.projectRoot, templateOptions);
|
|
52
|
+
}
|
|
50
53
|
}
|
|
51
54
|
function default_1(host, options) {
|
|
52
55
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
53
|
-
var _a, _b, _c, _d, _e;
|
|
56
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
54
57
|
const normalizedOptions = yield normalizeOptions(host, options);
|
|
55
58
|
const { applicationGenerator: initExpress } = yield Promise.resolve().then(() => require('@nx/express'));
|
|
56
59
|
yield initExpress(host, Object.assign(Object.assign({}, options), { skipFormat: true, skipPackageJson: false, linter: eslint_1.Linter.EsLint, unitTestRunner: 'jest', js: false, directory: `apps/${options.name}` }));
|
|
57
|
-
(0, devkit_1.addDependenciesToPackageJson)(host, {
|
|
58
|
-
'@abgov/adsp-service-sdk': '^2.5.0',
|
|
59
|
-
compression: '^1.8.1',
|
|
60
|
-
cors: '^2.8.5',
|
|
61
|
-
dotenv: '^16.4.7',
|
|
62
|
-
envalid: '^8.0.0',
|
|
63
|
-
helmet: '^8.0.0',
|
|
64
|
-
passport: '^0.7.0',
|
|
65
|
-
'passport-anonymous': '^1.0.1',
|
|
66
|
-
}, {
|
|
67
|
-
'@types/compression': '^1.7.5',
|
|
68
|
-
'@types/cors': '^2.8.17',
|
|
69
|
-
'@types/passport': '^1.0.16',
|
|
70
|
-
'@types/passport-anonymous': '^1.0.3',
|
|
71
|
-
});
|
|
60
|
+
(0, devkit_1.addDependenciesToPackageJson)(host, Object.assign(Object.assign({ '@abgov/adsp-service-sdk': '^2.5.0', compression: '^1.8.1', cors: '^2.8.5', dotenv: '^16.4.7', envalid: '^8.0.0', helmet: '^8.0.0', passport: '^0.7.0', 'passport-anonymous': '^1.0.1' }, (normalizedOptions.database === 'postgres' ? { '@prisma/client': '^6.0.0' } : {})), (normalizedOptions.database === 'mongo' ? { mongoose: '^8.0.0' } : {})), Object.assign({ '@types/compression': '^1.7.5', '@types/cors': '^2.8.17', '@types/passport': '^1.0.16', '@types/passport-anonymous': '^1.0.3' }, (normalizedOptions.database === 'postgres' ? { prisma: '^6.0.0' } : {})));
|
|
72
61
|
addFiles(host, normalizedOptions);
|
|
62
|
+
if (normalizedOptions.database !== 'none') {
|
|
63
|
+
const projectConfig = (0, devkit_1.readProjectConfiguration)(host, normalizedOptions.projectName);
|
|
64
|
+
const targets = Object.assign({}, projectConfig.targets);
|
|
65
|
+
targets['dev-db'] = {
|
|
66
|
+
executor: 'nx:run-commands',
|
|
67
|
+
options: {
|
|
68
|
+
command: 'bash scripts/dev-db.sh',
|
|
69
|
+
cwd: '{projectRoot}',
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
if (targets['serve']) {
|
|
73
|
+
targets['serve'] = Object.assign(Object.assign({}, targets['serve']), { dependsOn: [...((_a = targets['serve'].dependsOn) !== null && _a !== void 0 ? _a : []), 'dev-db'] });
|
|
74
|
+
}
|
|
75
|
+
if (normalizedOptions.database === 'postgres') {
|
|
76
|
+
targets['db:generate'] = {
|
|
77
|
+
executor: 'nx:run-commands',
|
|
78
|
+
options: { command: 'npx prisma generate', cwd: '{projectRoot}' },
|
|
79
|
+
};
|
|
80
|
+
targets['db:migrate'] = {
|
|
81
|
+
executor: 'nx:run-commands',
|
|
82
|
+
options: { command: 'npx prisma migrate dev', cwd: '{projectRoot}' },
|
|
83
|
+
};
|
|
84
|
+
targets['db:migrate:deploy'] = {
|
|
85
|
+
executor: 'nx:run-commands',
|
|
86
|
+
options: { command: 'npx prisma migrate deploy', cwd: '{projectRoot}' },
|
|
87
|
+
};
|
|
88
|
+
targets['db:studio'] = {
|
|
89
|
+
executor: 'nx:run-commands',
|
|
90
|
+
options: { command: 'npx prisma studio', cwd: '{projectRoot}' },
|
|
91
|
+
};
|
|
92
|
+
// Prisma client must be generated before TypeScript can compile the project.
|
|
93
|
+
if (targets['build']) {
|
|
94
|
+
targets['build'] = Object.assign(Object.assign({}, targets['build']), { dependsOn: [...((_b = targets['build'].dependsOn) !== null && _b !== void 0 ? _b : []), 'db:generate'] });
|
|
95
|
+
}
|
|
96
|
+
// The generated client is project-scoped to src/generated/prisma — exclude
|
|
97
|
+
// it from source control so it doesn't get committed alongside app code.
|
|
98
|
+
const gitignorePath = '.gitignore';
|
|
99
|
+
const ignoreEntry = `${normalizedOptions.projectRoot}/src/generated/`;
|
|
100
|
+
if (host.exists(gitignorePath)) {
|
|
101
|
+
const content = host.read(gitignorePath).toString();
|
|
102
|
+
if (!content.includes(ignoreEntry)) {
|
|
103
|
+
host.write(gitignorePath, `${content.trimEnd()}\n${ignoreEntry}\n`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
(0, devkit_1.updateProjectConfiguration)(host, normalizedOptions.projectName, Object.assign(Object.assign({}, projectConfig), { targets }));
|
|
108
|
+
}
|
|
73
109
|
yield (0, devkit_1.formatFiles)(host);
|
|
74
110
|
// Consult the nx-adsp-agent to augment the project with ADSP capabilities.
|
|
75
111
|
// The agent has access to template tools and a workspace; it generates new
|
|
@@ -81,13 +117,13 @@ function default_1(host, options) {
|
|
|
81
117
|
// from the single realm login already performed during normalizeOptions.
|
|
82
118
|
// token from the single realm login. Fall back to a new login only when the
|
|
83
119
|
// full interactive flow was used and no token is available.
|
|
84
|
-
const accessToken = (
|
|
120
|
+
const accessToken = (_c = normalizedOptions.accessToken) !== null && _c !== void 0 ? _c : (yield (0, nx_oc_1.realmLogin)(normalizedOptions.adsp.accessServiceUrl, normalizedOptions.adsp.tenantRealm).catch((err) => {
|
|
85
121
|
var _a;
|
|
86
122
|
process.stdout.write(`Agent sign-in failed (${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}) — skipping agent interaction.\n`);
|
|
87
123
|
return undefined;
|
|
88
124
|
}));
|
|
89
|
-
const mainTs = (
|
|
90
|
-
const environmentTs = (
|
|
125
|
+
const mainTs = (_e = (_d = host.read(`${normalizedOptions.projectRoot}/src/main.ts`)) === null || _d === void 0 ? void 0 : _d.toString()) !== null && _e !== void 0 ? _e : '';
|
|
126
|
+
const environmentTs = (_g = (_f = host.read(`${normalizedOptions.projectRoot}/src/environment.ts`)) === null || _f === void 0 ? void 0 : _f.toString()) !== null && _g !== void 0 ? _g : '';
|
|
91
127
|
const agentResult = yield (0, agent_1.consultAgent)(normalizedOptions.adsp.directoryServiceUrl, accessToken, {
|
|
92
128
|
projectName: normalizedOptions.projectName,
|
|
93
129
|
projectType: 'express-service',
|
|
@@ -114,7 +150,7 @@ function default_1(host, options) {
|
|
|
114
150
|
}
|
|
115
151
|
}
|
|
116
152
|
}
|
|
117
|
-
yield (0, nx_oc_1.deploymentGenerator)(host, Object.assign(Object.assign({}, normalizedOptions), { appType: 'node', project: normalizedOptions.projectName }));
|
|
153
|
+
yield (0, nx_oc_1.deploymentGenerator)(host, Object.assign(Object.assign({}, normalizedOptions), { appType: 'node', project: normalizedOptions.projectName, database: normalizedOptions.database }));
|
|
118
154
|
return () => {
|
|
119
155
|
(0, devkit_1.installPackagesTask)(host);
|
|
120
156
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"express-service.js","sourceRoot":"","sources":["../../../../../../packages/nx-adsp/src/generators/express-service/express-service.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"express-service.js","sourceRoot":"","sources":["../../../../../../packages/nx-adsp/src/generators/express-service/express-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -34,4 +34,59 @@ describe('Express Service Generator', () => {
|
|
|
34
34
|
expect(host.exists('apps/test/src/environment.ts')).toBeTruthy();
|
|
35
35
|
expect(host.exists('apps/test/src/environments/environment.ts')).toBeFalsy();
|
|
36
36
|
}, 60000);
|
|
37
|
+
|
|
38
|
+
it('scaffolds postgres database files and targets', async () => {
|
|
39
|
+
const host = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
40
|
+
await generator(host, { ...options, database: 'postgres' });
|
|
41
|
+
|
|
42
|
+
expect(host.exists('apps/test/prisma/schema.prisma')).toBeTruthy();
|
|
43
|
+
expect(host.exists('apps/test/src/database.ts')).toBeTruthy();
|
|
44
|
+
expect(host.exists('apps/test/scripts/dev-db.sh')).toBeTruthy();
|
|
45
|
+
expect(host.exists('apps/test/.env.example')).toBeTruthy();
|
|
46
|
+
|
|
47
|
+
const schema = host.read('apps/test/prisma/schema.prisma').toString();
|
|
48
|
+
expect(schema).toContain('postgresql');
|
|
49
|
+
expect(schema).toContain('src/generated/prisma');
|
|
50
|
+
|
|
51
|
+
const database = host.read('apps/test/src/database.ts').toString();
|
|
52
|
+
expect(database).toContain('./generated/prisma');
|
|
53
|
+
|
|
54
|
+
const config = readProjectConfiguration(host, 'test');
|
|
55
|
+
expect(config.targets['dev-db']).toBeTruthy();
|
|
56
|
+
expect(config.targets['db:generate']).toBeTruthy();
|
|
57
|
+
expect(config.targets['db:migrate']).toBeTruthy();
|
|
58
|
+
expect(config.targets['db:migrate:deploy']).toBeTruthy();
|
|
59
|
+
expect(config.targets['db:studio']).toBeTruthy();
|
|
60
|
+
expect(config.targets['serve'].dependsOn).toContain('dev-db');
|
|
61
|
+
expect(config.targets['build'].dependsOn).toContain('db:generate');
|
|
62
|
+
}, 60000);
|
|
63
|
+
|
|
64
|
+
it('scaffolds mongo database files and targets', async () => {
|
|
65
|
+
const host = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
66
|
+
await generator(host, { ...options, database: 'mongo' });
|
|
67
|
+
|
|
68
|
+
expect(host.exists('apps/test/src/database.ts')).toBeTruthy();
|
|
69
|
+
expect(host.exists('apps/test/scripts/dev-db.sh')).toBeTruthy();
|
|
70
|
+
expect(host.exists('apps/test/.env.example')).toBeTruthy();
|
|
71
|
+
|
|
72
|
+
const database = host.read('apps/test/src/database.ts').toString();
|
|
73
|
+
expect(database).toContain('mongoose');
|
|
74
|
+
|
|
75
|
+
const config = readProjectConfiguration(host, 'test');
|
|
76
|
+
expect(config.targets['dev-db']).toBeTruthy();
|
|
77
|
+
expect(config.targets['serve'].dependsOn).toContain('dev-db');
|
|
78
|
+
expect(config.targets['db:generate']).toBeFalsy();
|
|
79
|
+
}, 60000);
|
|
80
|
+
|
|
81
|
+
it('does not scaffold database files when database is none', async () => {
|
|
82
|
+
const host = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
83
|
+
await generator(host, { ...options, database: 'none' });
|
|
84
|
+
|
|
85
|
+
expect(host.exists('apps/test/prisma/schema.prisma')).toBeFalsy();
|
|
86
|
+
expect(host.exists('apps/test/src/database.ts')).toBeFalsy();
|
|
87
|
+
expect(host.exists('apps/test/scripts/dev-db.sh')).toBeFalsy();
|
|
88
|
+
|
|
89
|
+
const config = readProjectConfiguration(host, 'test');
|
|
90
|
+
expect(config.targets['dev-db']).toBeFalsy();
|
|
91
|
+
}, 60000);
|
|
37
92
|
});
|
|
@@ -10,6 +10,11 @@ Generated by `nx g @abgov/nx-adsp:express-service`.
|
|
|
10
10
|
- **Config**: `envalid` with `.env` file (see `src/environment.ts`)
|
|
11
11
|
- **SDK**: `@abgov/adsp-service-sdk` — provides service registration, auth,
|
|
12
12
|
event publishing, configuration management, and service discovery
|
|
13
|
+
<% if (database === 'postgres') { %>
|
|
14
|
+
- **Database**: PostgreSQL via Prisma ORM
|
|
15
|
+
<% } else if (database === 'mongo') { %>
|
|
16
|
+
- **Database**: MongoDB via Mongoose
|
|
17
|
+
<% } %>
|
|
13
18
|
|
|
14
19
|
## Key files
|
|
15
20
|
|
|
@@ -17,6 +22,13 @@ Generated by `nx g @abgov/nx-adsp:express-service`.
|
|
|
17
22
|
|------|---------|
|
|
18
23
|
| `src/main.ts` | App entry — SDK init, middleware, routes, server start |
|
|
19
24
|
| `src/environment.ts` | Validated env config with defaults pre-set from ADSP tenant |
|
|
25
|
+
| `src/events.ts` | Domain event definitions — register in `initializeService({ events })` |
|
|
26
|
+
<% if (database === 'postgres') { %>
|
|
27
|
+
| `src/database.ts` | PrismaClient singleton — import `{ prisma }` in route handlers |
|
|
28
|
+
| `prisma/schema.prisma` | Prisma schema — edit to add models, then run `nx db:migrate <%= projectName %>` |
|
|
29
|
+
<% } else if (database === 'mongo') { %>
|
|
30
|
+
| `src/database.ts` | Mongoose connection helpers — `connectDatabase()` and `disconnectDatabase()` |
|
|
31
|
+
<% } %>
|
|
20
32
|
|
|
21
33
|
## SDK capabilities
|
|
22
34
|
|
|
@@ -53,6 +65,176 @@ app.get('/<%= projectName %>/v1/my-resource', (req, res) => {
|
|
|
53
65
|
`passport.authenticate(['tenant', 'anonymous'])` is applied to the entire
|
|
54
66
|
`/<%= projectName %>/v1` prefix — check `req.user` inside handlers to
|
|
55
67
|
distinguish authenticated from anonymous access.
|
|
68
|
+
<% if (database === 'postgres') { %>
|
|
69
|
+
|
|
70
|
+
## Local database
|
|
71
|
+
|
|
72
|
+
A local Postgres instance runs in a Podman container managed by `scripts/dev-db.sh`.
|
|
73
|
+
|
|
74
|
+
Start or resume it:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
nx dev-db <%= projectName %>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`nx serve` depends on `dev-db` — the container starts automatically when you
|
|
81
|
+
run the application. Run `nx dev-db` explicitly only when you need the database
|
|
82
|
+
before `nx serve` (e.g. running migrations with `nx db:migrate <%= projectName %>`).
|
|
83
|
+
|
|
84
|
+
`DATABASE_URL` is written to `.env.local` on first run:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
postgresql://<%= projectName %>:<%= projectName %>@localhost:5432/<%= projectName %>_dev
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**If Postgres is not responding:**
|
|
91
|
+
1. Check that Podman is running: `podman ps`
|
|
92
|
+
2. Re-run: `nx dev-db <%= projectName %>`
|
|
93
|
+
3. macOS only — if `podman ps` errors: `podman machine start`
|
|
94
|
+
|
|
95
|
+
**macOS one-time prerequisite** (skip if already set up for another project):
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
podman machine init
|
|
99
|
+
podman machine start
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Adding a Prisma model
|
|
103
|
+
|
|
104
|
+
1. Edit `prisma/schema.prisma` to add your model
|
|
105
|
+
2. Run `nx db:migrate <%= projectName %>` — creates and applies the migration
|
|
106
|
+
3. Run `nx db:generate <%= projectName %>` if the client isn't updated automatically
|
|
107
|
+
4. Import `{ prisma }` from `./database` in your route handler
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// Example route using Prisma
|
|
111
|
+
app.get('/<%= projectName %>/v1/items', async (_req, res) => {
|
|
112
|
+
const items = await prisma.item.findMany();
|
|
113
|
+
res.json(items);
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Prisma targets
|
|
118
|
+
|
|
119
|
+
| Target | Command | Use |
|
|
120
|
+
|--------|---------|-----|
|
|
121
|
+
| `nx db:migrate <%= projectName %>` | `prisma migrate dev` | Create + apply migration during development |
|
|
122
|
+
| `nx db:migrate:deploy <%= projectName %>` | `prisma migrate deploy` | Apply pending migrations in production/CI |
|
|
123
|
+
| `nx db:generate <%= projectName %>` | `prisma generate` | Regenerate the Prisma client after schema changes |
|
|
124
|
+
| `nx db:studio <%= projectName %>` | `prisma studio` | Open Prisma Studio to browse data |
|
|
125
|
+
|
|
126
|
+
## OpenShift deployment — database
|
|
127
|
+
|
|
128
|
+
The deployment manifest references a Secret named `{appName}-database` for the
|
|
129
|
+
`DATABASE_URL` value, and runs `prisma migrate deploy` as an init container
|
|
130
|
+
before the application starts.
|
|
131
|
+
|
|
132
|
+
Create the Secret in each OpenShift namespace before first deploy:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
oc create secret generic <app-name>-database \
|
|
136
|
+
--from-literal=DATABASE_URL=postgresql://user:password@host:5432/dbname \
|
|
137
|
+
-n <namespace>
|
|
138
|
+
```
|
|
139
|
+
<% } else if (database === 'mongo') { %>
|
|
140
|
+
|
|
141
|
+
## Local database
|
|
142
|
+
|
|
143
|
+
A local MongoDB instance runs in a Podman container managed by `scripts/dev-db.sh`.
|
|
144
|
+
|
|
145
|
+
Start or resume it:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
nx dev-db <%= projectName %>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`nx serve` depends on `dev-db` — the container starts automatically when you
|
|
152
|
+
run the application.
|
|
153
|
+
|
|
154
|
+
`MONGODB_URI` is written to `.env.local` on first run:
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
mongodb://localhost:27017/<%= projectName %>_dev
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**If MongoDB is not responding:**
|
|
161
|
+
1. Check that Podman is running: `podman ps`
|
|
162
|
+
2. Re-run: `nx dev-db <%= projectName %>`
|
|
163
|
+
3. macOS only — if `podman ps` errors: `podman machine start`
|
|
164
|
+
|
|
165
|
+
**macOS one-time prerequisite** (skip if already set up for another project):
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
podman machine init
|
|
169
|
+
podman machine start
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Adding a Mongoose model
|
|
173
|
+
|
|
174
|
+
Create a model file under `src/models/`:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
// src/models/item.model.ts
|
|
178
|
+
import mongoose, { Schema, Document } from 'mongoose';
|
|
179
|
+
|
|
180
|
+
export interface IItem extends Document {
|
|
181
|
+
name: string;
|
|
182
|
+
createdAt: Date;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const ItemSchema = new Schema<IItem>({
|
|
186
|
+
name: { type: String, required: true },
|
|
187
|
+
createdAt: { type: Date, default: Date.now },
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
export const Item = mongoose.model<IItem>('Item', ItemSchema);
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Then import and use in a route handler:
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
import { Item } from './models/item.model';
|
|
197
|
+
|
|
198
|
+
app.get('/<%= projectName %>/v1/items', async (_req, res) => {
|
|
199
|
+
const items = await Item.find();
|
|
200
|
+
res.json(items);
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## OpenShift deployment — database
|
|
205
|
+
|
|
206
|
+
The deployment manifest references a Secret named `{appName}-database` for the
|
|
207
|
+
`MONGODB_URI` value.
|
|
208
|
+
|
|
209
|
+
Create the Secret in each OpenShift namespace before first deploy:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
oc create secret generic <app-name>-database \
|
|
213
|
+
--from-literal=MONGODB_URI=mongodb://user:password@host:27017/dbname \
|
|
214
|
+
-n <namespace>
|
|
215
|
+
```
|
|
216
|
+
<% } %>
|
|
217
|
+
|
|
218
|
+
## Domain events
|
|
219
|
+
|
|
220
|
+
Domain events are defined in `src/events.ts` and registered at startup via
|
|
221
|
+
`initializeService({ events: [...] })` in `main.ts`.
|
|
222
|
+
|
|
223
|
+
To publish an event from a route handler (use `eventService` from `capabilities`):
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
import { createExampleEvent } from './events';
|
|
227
|
+
|
|
228
|
+
// Inside a route handler, after eventService is in scope via closure:
|
|
229
|
+
await eventService.send(createExampleEvent(id));
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
To add a new event:
|
|
233
|
+
|
|
234
|
+
1. Add a new `const MY_EVENT_NAME` and `DomainEventDefinition` export to `src/events.ts`
|
|
235
|
+
2. Add the definition to the `events` array in `initializeService()`
|
|
236
|
+
3. Add a factory function that returns a typed `DomainEvent`
|
|
237
|
+
4. Call `eventService.send(myFactory(...))` at the appropriate point in your handler
|
|
56
238
|
|
|
57
239
|
## Frontend proxy integration (mern / mean stacks)
|
|
58
240
|
|
|
@@ -74,3 +256,4 @@ frontend's `/api/v1/public`.
|
|
|
74
256
|
- The passport strategy setup — the tenant strategy handles JWT validation
|
|
75
257
|
- `configurationHandler` on the API path — provides configuration service
|
|
76
258
|
integration; keep it applied before route handlers
|
|
259
|
+
- `createErrorHandler(logger)` — must remain the last `app.use()` call
|
|
@@ -4,8 +4,14 @@ import { resolve } from 'path';
|
|
|
4
4
|
|
|
5
5
|
config({
|
|
6
6
|
path: resolve(process.cwd(), 'apps/<%= projectName %>/.env'),
|
|
7
|
+
});
|
|
8
|
+
<% if (database !== 'none') { %>
|
|
9
|
+
// .env.local is written by 'nx dev-db' with the local database connection string.
|
|
10
|
+
config({
|
|
11
|
+
path: resolve(process.cwd(), 'apps/<%= projectName %>/.env.local'),
|
|
7
12
|
override: true,
|
|
8
13
|
});
|
|
14
|
+
<% } %>
|
|
9
15
|
|
|
10
16
|
export const environment = cleanEnv(process.env, {
|
|
11
17
|
KEYCLOAK_ROOT_URL: str({ default: '<%= accessServiceUrl %>' }),
|
|
@@ -16,4 +22,9 @@ export const environment = cleanEnv(process.env, {
|
|
|
16
22
|
TRUSTED_PROXY: str({ default: 'uniquelocal' }),
|
|
17
23
|
LOG_LEVEL: str({ default: 'debug' }),
|
|
18
24
|
PORT: num({ default: 3000 }),
|
|
25
|
+
<% if (database === 'postgres') { %>
|
|
26
|
+
DATABASE_URL: str(),
|
|
27
|
+
<% } else if (database === 'mongo') { %>
|
|
28
|
+
MONGODB_URI: str(),
|
|
29
|
+
<% } %>
|
|
19
30
|
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { DomainEvent, DomainEventDefinition } from '@abgov/adsp-service-sdk';
|
|
2
|
+
|
|
3
|
+
// Define domain events your service publishes. Each event requires a unique name,
|
|
4
|
+
// a human-readable description, and a JSON Schema for the payload.
|
|
5
|
+
// Register all definitions in initializeService({ events: [...] }) in main.ts.
|
|
6
|
+
|
|
7
|
+
const EXAMPLE_EVENT_NAME = '<%= projectName %>:example';
|
|
8
|
+
|
|
9
|
+
export const exampleEventDefinition: DomainEventDefinition = {
|
|
10
|
+
name: EXAMPLE_EVENT_NAME,
|
|
11
|
+
description: 'Replace with a description of what this event signals.',
|
|
12
|
+
payloadSchema: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
id: { type: 'string' },
|
|
16
|
+
},
|
|
17
|
+
required: ['id'],
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function createExampleEvent(id: string): DomainEvent {
|
|
22
|
+
return {
|
|
23
|
+
name: EXAMPLE_EVENT_NAME,
|
|
24
|
+
timestamp: new Date(),
|
|
25
|
+
tenantId: null,
|
|
26
|
+
correlationId: null,
|
|
27
|
+
context: {},
|
|
28
|
+
payload: { id },
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import { AdspId, initializeService } from '@abgov/adsp-service-sdk';
|
|
1
|
+
import { AdspId, createErrorHandler, initializeService } from '@abgov/adsp-service-sdk';
|
|
2
2
|
import compression from 'compression';
|
|
3
3
|
import cors from 'cors';
|
|
4
4
|
import express from 'express';
|
|
5
5
|
import helmet from 'helmet';
|
|
6
6
|
import passport from 'passport';
|
|
7
7
|
import { Strategy as AnonymousStrategy } from 'passport-anonymous';
|
|
8
|
-
|
|
8
|
+
<% if (database === 'postgres') { %>
|
|
9
|
+
import { prisma } from './database';
|
|
10
|
+
<% } else if (database === 'mongo') { %>
|
|
11
|
+
import { connectDatabase, disconnectDatabase } from './database';
|
|
12
|
+
<% } %>
|
|
9
13
|
import { environment } from './environment';
|
|
14
|
+
import { exampleEventDefinition } from './events';
|
|
10
15
|
|
|
11
16
|
async function initializeApp() {
|
|
12
17
|
const serviceId = AdspId.parse(environment.CLIENT_ID);
|
|
@@ -19,11 +24,15 @@ async function initializeApp() {
|
|
|
19
24
|
clientSecret: environment.CLIENT_SECRET,
|
|
20
25
|
accessServiceUrl: new URL(environment.KEYCLOAK_ROOT_URL),
|
|
21
26
|
directoryUrl: new URL(environment.DIRECTORY_URL),
|
|
27
|
+
events: [exampleEventDefinition],
|
|
22
28
|
},
|
|
23
29
|
{ logLevel: environment.LOG_LEVEL }
|
|
24
30
|
);
|
|
25
31
|
|
|
26
|
-
const { logger, tenantStrategy, traceHandler, configurationHandler, healthCheck } = capabilities;
|
|
32
|
+
const { logger, tenantStrategy, traceHandler, configurationHandler, healthCheck, eventService } = capabilities;
|
|
33
|
+
<% if (database === 'mongo') { %>
|
|
34
|
+
await connectDatabase();
|
|
35
|
+
<% } %>
|
|
27
36
|
|
|
28
37
|
const app = express();
|
|
29
38
|
|
|
@@ -72,7 +81,10 @@ async function initializeApp() {
|
|
|
72
81
|
});
|
|
73
82
|
});
|
|
74
83
|
|
|
75
|
-
|
|
84
|
+
// Mount error handler last — after all routes and middleware.
|
|
85
|
+
app.use(createErrorHandler(logger));
|
|
86
|
+
|
|
87
|
+
return { app, logger, eventService };
|
|
76
88
|
}
|
|
77
89
|
|
|
78
90
|
initializeApp().then(({ app, logger }) => {
|
|
@@ -81,4 +93,21 @@ initializeApp().then(({ app, logger }) => {
|
|
|
81
93
|
logger.info(`Listening at http://localhost:${port}/<%= projectName %>/v1`);
|
|
82
94
|
});
|
|
83
95
|
server.on('error', (err) => logger.error(`Error encountered in server: ${err}`));
|
|
96
|
+
<% if (database === 'postgres') { %>
|
|
97
|
+
|
|
98
|
+
const shutdown = async () => {
|
|
99
|
+
server.close();
|
|
100
|
+
await prisma.$disconnect();
|
|
101
|
+
};
|
|
102
|
+
process.on('SIGTERM', shutdown);
|
|
103
|
+
process.on('SIGINT', shutdown);
|
|
104
|
+
<% } else if (database === 'mongo') { %>
|
|
105
|
+
|
|
106
|
+
const shutdown = async () => {
|
|
107
|
+
server.close();
|
|
108
|
+
await disconnectDatabase();
|
|
109
|
+
};
|
|
110
|
+
process.on('SIGTERM', shutdown);
|
|
111
|
+
process.on('SIGINT', shutdown);
|
|
112
|
+
<% } %>
|
|
84
113
|
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Copy this file to .env and set CLIENT_SECRET.
|
|
2
|
+
# MONGODB_URI is written automatically to .env.local by 'nx dev-db <%= projectName %>'.
|
|
3
|
+
KEYCLOAK_ROOT_URL=<%= accessServiceUrl %>
|
|
4
|
+
DIRECTORY_URL=<%= directoryServiceUrl %>
|
|
5
|
+
TENANT_REALM=<%= tenantRealm %>
|
|
6
|
+
CLIENT_ID=urn:ads:<%= tenant %>:<%= projectName %>
|
|
7
|
+
CLIENT_SECRET=
|
|
8
|
+
MONGODB_URI=mongodb://localhost:27017/<%= projectName %>_dev
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Idempotent: creates the MongoDB container + volume on first run,
|
|
3
|
+
# starts the existing container on subsequent runs.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
APP="<%= projectName %>"
|
|
7
|
+
CONTAINER="${APP}-mongo"
|
|
8
|
+
VOLUME="${APP}-mongo-data"
|
|
9
|
+
DB_NAME="${APP}_dev"
|
|
10
|
+
PORT=27017
|
|
11
|
+
|
|
12
|
+
if ! command -v podman &>/dev/null; then
|
|
13
|
+
echo "podman is required. On macOS: brew install podman" >&2
|
|
14
|
+
exit 1
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
podman volume inspect "${VOLUME}" &>/dev/null || podman volume create "${VOLUME}"
|
|
18
|
+
|
|
19
|
+
if podman container inspect "${CONTAINER}" &>/dev/null; then
|
|
20
|
+
podman start "${CONTAINER}" 2>/dev/null || true
|
|
21
|
+
else
|
|
22
|
+
podman run -d \
|
|
23
|
+
--name "${CONTAINER}" \
|
|
24
|
+
-p "${PORT}:27017" \
|
|
25
|
+
-v "${VOLUME}:/data/db" \
|
|
26
|
+
docker.io/library/mongo:7
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
echo "Waiting for MongoDB to be ready..."
|
|
30
|
+
until podman exec "${CONTAINER}" mongosh --quiet --eval "quit()" &>/dev/null; do
|
|
31
|
+
sleep 1
|
|
32
|
+
done
|
|
33
|
+
echo "MongoDB is ready."
|
|
34
|
+
|
|
35
|
+
# Write MONGODB_URI to .env.local so the app picks it up without manual .env editing.
|
|
36
|
+
# Overridden by the Secret in OpenShift at runtime.
|
|
37
|
+
echo "MONGODB_URI=mongodb://localhost:${PORT}/${DB_NAME}" > .env.local
|
|
38
|
+
echo "MONGODB_URI written to .env.local"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import mongoose from 'mongoose';
|
|
2
|
+
|
|
3
|
+
import { environment } from './environment';
|
|
4
|
+
|
|
5
|
+
export async function connectDatabase(): Promise<void> {
|
|
6
|
+
await mongoose.connect(environment.MONGODB_URI);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function disconnectDatabase(): Promise<void> {
|
|
10
|
+
await mongoose.disconnect();
|
|
11
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Copy this file to .env and set CLIENT_SECRET.
|
|
2
|
+
# DATABASE_URL is written automatically to .env.local by 'nx dev-db <%= projectName %>'.
|
|
3
|
+
KEYCLOAK_ROOT_URL=<%= accessServiceUrl %>
|
|
4
|
+
DIRECTORY_URL=<%= directoryServiceUrl %>
|
|
5
|
+
TENANT_REALM=<%= tenantRealm %>
|
|
6
|
+
CLIENT_ID=urn:ads:<%= tenant %>:<%= projectName %>
|
|
7
|
+
CLIENT_SECRET=
|
|
8
|
+
DATABASE_URL=postgresql://<%= projectName %>:<%= projectName %>@localhost:5432/<%= projectName %>_dev
|
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
generator client {
|
|
2
|
+
provider = "prisma-client-js"
|
|
3
|
+
// Scoped output prevents multiple postgres services in the same workspace from
|
|
4
|
+
// overwriting each other's generated client in the shared node_modules.
|
|
5
|
+
output = "../src/generated/prisma"
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
datasource db {
|
|
9
|
+
provider = "postgresql"
|
|
10
|
+
url = env("DATABASE_URL")
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Add your models here.
|
|
14
|
+
// After editing, run: nx db:migrate <%= projectName %>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Idempotent: creates the Postgres container + volume on first run,
|
|
3
|
+
# starts the existing container on subsequent runs.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
APP="<%= projectName %>"
|
|
7
|
+
CONTAINER="${APP}-postgres"
|
|
8
|
+
VOLUME="${APP}-postgres-data"
|
|
9
|
+
DB_NAME="${APP}_dev"
|
|
10
|
+
DB_USER="${APP}"
|
|
11
|
+
DB_PASS="${APP}"
|
|
12
|
+
PORT=5432
|
|
13
|
+
|
|
14
|
+
if ! command -v podman &>/dev/null; then
|
|
15
|
+
echo "podman is required. On macOS: brew install podman" >&2
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
podman volume inspect "${VOLUME}" &>/dev/null || podman volume create "${VOLUME}"
|
|
20
|
+
|
|
21
|
+
if podman container inspect "${CONTAINER}" &>/dev/null; then
|
|
22
|
+
podman start "${CONTAINER}" 2>/dev/null || true
|
|
23
|
+
else
|
|
24
|
+
podman run -d \
|
|
25
|
+
--name "${CONTAINER}" \
|
|
26
|
+
-e POSTGRES_DB="${DB_NAME}" \
|
|
27
|
+
-e POSTGRES_USER="${DB_USER}" \
|
|
28
|
+
-e POSTGRES_PASSWORD="${DB_PASS}" \
|
|
29
|
+
-p "${PORT}:5432" \
|
|
30
|
+
-v "${VOLUME}:/var/lib/postgresql/data" \
|
|
31
|
+
docker.io/library/postgres:16-alpine
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
echo "Waiting for Postgres to be ready..."
|
|
35
|
+
until podman exec "${CONTAINER}" pg_isready -U "${DB_USER}" -d "${DB_NAME}" &>/dev/null; do
|
|
36
|
+
sleep 1
|
|
37
|
+
done
|
|
38
|
+
echo "Postgres is ready."
|
|
39
|
+
|
|
40
|
+
# Write DATABASE_URL to .env.local so Prisma and the app pick it up without
|
|
41
|
+
# manual .env editing. Overridden by the SECRET in OpenShift at runtime.
|
|
42
|
+
echo "DATABASE_URL=postgresql://${DB_USER}:${DB_PASS}@localhost:${PORT}/${DB_NAME}" > .env.local
|
|
43
|
+
echo "DATABASE_URL written to .env.local"
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { AdspConfiguration, EnvironmentName } from '@abgov/nx-oc';
|
|
2
2
|
|
|
3
|
+
export type DatabaseType = 'none' | 'postgres' | 'mongo';
|
|
4
|
+
|
|
3
5
|
export interface Schema {
|
|
4
6
|
name: string;
|
|
5
7
|
env: EnvironmentName;
|
|
@@ -10,10 +12,13 @@ export interface Schema {
|
|
|
10
12
|
tenant?: string;
|
|
11
13
|
/** When true, skip the agent interaction. Used by composite generators that run the agent themselves. */
|
|
12
14
|
skipAgent?: boolean;
|
|
15
|
+
/** Database to scaffold. Defaults to 'none'. */
|
|
16
|
+
database?: DatabaseType;
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
export interface NormalizedSchema extends Schema {
|
|
16
20
|
projectName: string;
|
|
17
21
|
projectRoot: string;
|
|
18
22
|
adsp: AdspConfiguration;
|
|
23
|
+
database: DatabaseType;
|
|
19
24
|
}
|
|
@@ -45,6 +45,21 @@
|
|
|
45
45
|
"type": "string",
|
|
46
46
|
"description": "ADSP tenant name. When provided, the realm is looked up anonymously and a single browser login is used instead of the full interactive flow.",
|
|
47
47
|
"alias": "t"
|
|
48
|
+
},
|
|
49
|
+
"database": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"description": "Database to include in the service.",
|
|
52
|
+
"default": "none",
|
|
53
|
+
"enum": ["none", "postgres", "mongo"],
|
|
54
|
+
"x-prompt": {
|
|
55
|
+
"message": "Which database would you like to include?",
|
|
56
|
+
"type": "list",
|
|
57
|
+
"items": [
|
|
58
|
+
{ "value": "none", "label": "None — add a database later" },
|
|
59
|
+
{ "value": "postgres", "label": "PostgreSQL (Prisma)" },
|
|
60
|
+
{ "value": "mongo", "label": "MongoDB (Mongoose)" }
|
|
61
|
+
]
|
|
62
|
+
}
|
|
48
63
|
}
|
|
49
64
|
},
|
|
50
65
|
"required": [
|
|
@@ -20,7 +20,7 @@ function normalizeOptions(host, options) {
|
|
|
20
20
|
}
|
|
21
21
|
function default_1(host, options) {
|
|
22
22
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
23
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
23
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
|
|
24
24
|
const normalizedOptions = yield normalizeOptions(host, options);
|
|
25
25
|
const projectName = (0, devkit_1.names)(options.name).fileName;
|
|
26
26
|
const serviceName = `${projectName}-service`;
|
|
@@ -29,7 +29,7 @@ function default_1(host, options) {
|
|
|
29
29
|
const serviceRoot = `${appsDir}/${serviceName}`;
|
|
30
30
|
const appRoot = `${appsDir}/${appName}`;
|
|
31
31
|
// Scaffold both projects before the agent interaction so files exist to upload.
|
|
32
|
-
yield (0, express_service_1.default)(host, Object.assign(Object.assign({}, normalizedOptions), { name: serviceName, skipAgent: true }));
|
|
32
|
+
yield (0, express_service_1.default)(host, Object.assign(Object.assign({}, normalizedOptions), { name: serviceName, skipAgent: true, database: 'mongo' }));
|
|
33
33
|
yield (0, angular_app_1.default)(host, Object.assign(Object.assign({}, normalizedOptions), { name: appName, proxy: {
|
|
34
34
|
location: '/api/',
|
|
35
35
|
proxyPass: `http://${serviceName}:3333/${serviceName}/`,
|
|
@@ -53,11 +53,13 @@ function default_1(host, options) {
|
|
|
53
53
|
existingFiles: {
|
|
54
54
|
'service/src/main.ts': (_c = (_b = host.read(`${serviceRoot}/src/main.ts`)) === null || _b === void 0 ? void 0 : _b.toString()) !== null && _c !== void 0 ? _c : '',
|
|
55
55
|
'service/src/environment.ts': (_e = (_d = host.read(`${serviceRoot}/src/environment.ts`)) === null || _d === void 0 ? void 0 : _d.toString()) !== null && _e !== void 0 ? _e : '',
|
|
56
|
-
'
|
|
57
|
-
'
|
|
58
|
-
'app/src/app/app.
|
|
59
|
-
'app/src/app/app.
|
|
60
|
-
'app/src/
|
|
56
|
+
'service/src/database.ts': (_g = (_f = host.read(`${serviceRoot}/src/database.ts`)) === null || _f === void 0 ? void 0 : _f.toString()) !== null && _g !== void 0 ? _g : '',
|
|
57
|
+
'service/src/events.ts': (_j = (_h = host.read(`${serviceRoot}/src/events.ts`)) === null || _h === void 0 ? void 0 : _h.toString()) !== null && _j !== void 0 ? _j : '',
|
|
58
|
+
'app/src/app/app.component.ts': (_l = (_k = host.read(`${appRoot}/src/app/app.component.ts`)) === null || _k === void 0 ? void 0 : _k.toString()) !== null && _l !== void 0 ? _l : '',
|
|
59
|
+
'app/src/app/app.component.html': (_o = (_m = host.read(`${appRoot}/src/app/app.component.html`)) === null || _m === void 0 ? void 0 : _m.toString()) !== null && _o !== void 0 ? _o : '',
|
|
60
|
+
'app/src/app/app.config.ts': (_q = (_p = host.read(`${appRoot}/src/app/app.config.ts`)) === null || _p === void 0 ? void 0 : _p.toString()) !== null && _q !== void 0 ? _q : '',
|
|
61
|
+
'app/src/app/app.routes.ts': (_s = (_r = host.read(`${appRoot}/src/app/app.routes.ts`)) === null || _r === void 0 ? void 0 : _r.toString()) !== null && _s !== void 0 ? _s : '',
|
|
62
|
+
'app/src/environments/environment.ts': (_u = (_t = host.read(`${appRoot}/src/environments/environment.ts`)) === null || _t === void 0 ? void 0 : _t.toString()) !== null && _u !== void 0 ? _u : '',
|
|
61
63
|
},
|
|
62
64
|
}, host, serviceRoot, { additionalRoots: { 'app': appRoot } }));
|
|
63
65
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mean.js","sourceRoot":"","sources":["../../../../../../packages/nx-adsp/src/generators/mean/mean.ts"],"names":[],"mappings":";;AAmBA,
|
|
1
|
+
{"version":3,"file":"mean.js","sourceRoot":"","sources":["../../../../../../packages/nx-adsp/src/generators/mean/mean.ts"],"names":[],"mappings":";;AAmBA,4BAqEC;;AAxFD,uCAA+F;AAC/F,wCAAgE;AAChE,4DAAwD;AACxD,wEAAoE;AAEpE,6CAA6E;AAC7E,+DAA4D;AAE5D,SAAe,gBAAgB,CAC7B,IAAU,EACV,OAAe;;;QAEf,MAAM,IAAI,GAAG,MAAM,IAAA,4BAAoB,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACvD,wEAAwE;QACxE,+EAA+E;QAC/E,iDAAiD;QACjD,uCAAY,OAAO,KAAE,WAAW,EAAE,MAAA,IAAI,CAAC,WAAW,mCAAI,OAAO,CAAC,WAAW,EAAE,IAAI,IAAG;IACpF,CAAC;CAAA;AAED,mBAA+B,IAAU,EAAE,OAAe;;;QACxD,MAAM,iBAAiB,GAAG,MAAM,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAChE,MAAM,WAAW,GAAG,IAAA,cAAK,EAAC,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC;QACjD,MAAM,WAAW,GAAG,GAAG,WAAW,UAAU,CAAC;QAC7C,MAAM,OAAO,GAAG,GAAG,WAAW,MAAM,CAAC;QACrC,MAAM,OAAO,GAAG,IAAA,2BAAkB,EAAC,IAAI,CAAC,CAAC,OAAO,CAAC;QACjD,MAAM,WAAW,GAAG,GAAG,OAAO,IAAI,WAAW,EAAE,CAAC;QAChD,MAAM,OAAO,GAAG,GAAG,OAAO,IAAI,OAAO,EAAE,CAAC;QAExC,gFAAgF;QAChF,MAAM,IAAA,yBAAkB,EAAC,IAAI,kCAAO,iBAAiB,KAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,IAAG,CAAC;QAChH,MAAM,IAAA,qBAAc,EAAC,IAAI,kCACpB,iBAAiB,KACpB,IAAI,EAAE,OAAO,EACb,KAAK,EAAE;gBACL,QAAQ,EAAE,OAAO;gBACjB,SAAS,EAAE,UAAU,WAAW,SAAS,WAAW,GAAG;aACxD,EACD,SAAS,EAAE,IAAI,IACf,CAAC;QAEH,IAAI,iBAAiB,CAAC,IAAI,EAAE,CAAC;YAC3B,4EAA4E;YAC5E,2EAA2E;YAC3E,2DAA2D;YAC3D,6EAA6E;YAC7E,8EAA8E;YAC9E,MAAM,WAAW,GACf,MAAA,iBAAiB,CAAC,IAAI,CAAC,WAAW,mCAClC,CAAC,MAAM,IAAA,kBAAU,EACf,iBAAiB,CAAC,IAAI,CAAC,gBAAgB,EACvC,iBAAiB,CAAC,IAAI,CAAC,WAAW,CACnC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;;gBACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,qCAAqC,MAAA,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,OAAO,mCAAI,GAAG,mCAAmC,CAC5F,CAAC;gBACF,OAAO,SAAS,CAAC;YACnB,CAAC,CAAC,CAAC,CAAC;YACN,MAAM,IAAA,kCAA0B,EAAC,MAAM,IAAA,oBAAY,EACjD,iBAAiB,CAAC,IAAI,CAAC,mBAAmB,EAC1C,WAAW,EACX;gBACE,WAAW;gBACX,WAAW,EAAE,MAAM;gBACnB,MAAM,EAAE,iBAAiB,CAAC,IAAI,CAAC,MAAM;gBACrC,aAAa,EAAE,+BAAc;gBAC7B,aAAa,EAAE;oBACb,qBAAqB,EAAE,MAAA,MAAA,IAAI,CAAC,IAAI,CAAC,GAAG,WAAW,cAAc,CAAC,0CAAE,QAAQ,EAAE,mCAAI,EAAE;oBAChF,4BAA4B,EAAE,MAAA,MAAA,IAAI,CAAC,IAAI,CAAC,GAAG,WAAW,qBAAqB,CAAC,0CAAE,QAAQ,EAAE,mCAAI,EAAE;oBAC9F,yBAAyB,EAAE,MAAA,MAAA,IAAI,CAAC,IAAI,CAAC,GAAG,WAAW,kBAAkB,CAAC,0CAAE,QAAQ,EAAE,mCAAI,EAAE;oBACxF,uBAAuB,EAAE,MAAA,MAAA,IAAI,CAAC,IAAI,CAAC,GAAG,WAAW,gBAAgB,CAAC,0CAAE,QAAQ,EAAE,mCAAI,EAAE;oBACpF,8BAA8B,EAAE,MAAA,MAAA,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,2BAA2B,CAAC,0CAAE,QAAQ,EAAE,mCAAI,EAAE;oBAClG,gCAAgC,EAAE,MAAA,MAAA,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,6BAA6B,CAAC,0CAAE,QAAQ,EAAE,mCAAI,EAAE;oBACtG,2BAA2B,EAAE,MAAA,MAAA,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,wBAAwB,CAAC,0CAAE,QAAQ,EAAE,mCAAI,EAAE;oBAC5F,2BAA2B,EAAE,MAAA,MAAA,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,wBAAwB,CAAC,0CAAE,QAAQ,EAAE,mCAAI,EAAE;oBAC5F,qCAAqC,EAAE,MAAA,MAAA,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,kCAAkC,CAAC,0CAAE,QAAQ,EAAE,mCAAI,EAAE;iBACjH;aACF,EACD,IAAI,EACJ,WAAW,EACX,EAAE,eAAe,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,CACxC,CAAC,CAAC;QACL,CAAC;QAED,MAAM,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;QAExB,OAAO,GAAG,EAAE;YACV,IAAA,4BAAmB,EAAC,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC;IACJ,CAAC;CAAA"}
|
|
@@ -20,7 +20,7 @@ function normalizeOptions(host, options) {
|
|
|
20
20
|
}
|
|
21
21
|
function default_1(host, options) {
|
|
22
22
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
23
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
23
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
|
|
24
24
|
const normalizedOptions = yield normalizeOptions(host, options);
|
|
25
25
|
const projectName = (0, devkit_1.names)(options.name).fileName;
|
|
26
26
|
const serviceName = `${projectName}-service`;
|
|
@@ -29,7 +29,7 @@ function default_1(host, options) {
|
|
|
29
29
|
const serviceRoot = `${appsDir}/${serviceName}`;
|
|
30
30
|
const appRoot = `${appsDir}/${appName}`;
|
|
31
31
|
// Scaffold both projects before the agent interaction so files exist to upload.
|
|
32
|
-
yield (0, express_service_1.default)(host, Object.assign(Object.assign({}, normalizedOptions), { name: serviceName, skipAgent: true }));
|
|
32
|
+
yield (0, express_service_1.default)(host, Object.assign(Object.assign({}, normalizedOptions), { name: serviceName, skipAgent: true, database: 'mongo' }));
|
|
33
33
|
yield (0, react_app_1.default)(host, Object.assign(Object.assign({}, normalizedOptions), { name: appName, proxy: {
|
|
34
34
|
location: '/api/',
|
|
35
35
|
proxyPass: `http://${serviceName}:3333/${serviceName}/`,
|
|
@@ -53,11 +53,13 @@ function default_1(host, options) {
|
|
|
53
53
|
existingFiles: {
|
|
54
54
|
'service/src/main.ts': (_c = (_b = host.read(`${serviceRoot}/src/main.ts`)) === null || _b === void 0 ? void 0 : _b.toString()) !== null && _c !== void 0 ? _c : '',
|
|
55
55
|
'service/src/environment.ts': (_e = (_d = host.read(`${serviceRoot}/src/environment.ts`)) === null || _d === void 0 ? void 0 : _d.toString()) !== null && _e !== void 0 ? _e : '',
|
|
56
|
-
'
|
|
57
|
-
'
|
|
58
|
-
'app/src/
|
|
59
|
-
'app/src/
|
|
60
|
-
'app/src/
|
|
56
|
+
'service/src/database.ts': (_g = (_f = host.read(`${serviceRoot}/src/database.ts`)) === null || _f === void 0 ? void 0 : _f.toString()) !== null && _g !== void 0 ? _g : '',
|
|
57
|
+
'service/src/events.ts': (_j = (_h = host.read(`${serviceRoot}/src/events.ts`)) === null || _h === void 0 ? void 0 : _h.toString()) !== null && _j !== void 0 ? _j : '',
|
|
58
|
+
'app/src/app/app.tsx': (_l = (_k = host.read(`${appRoot}/src/app/app.tsx`)) === null || _k === void 0 ? void 0 : _k.toString()) !== null && _l !== void 0 ? _l : '',
|
|
59
|
+
'app/src/store.ts': (_o = (_m = host.read(`${appRoot}/src/store.ts`)) === null || _m === void 0 ? void 0 : _m.toString()) !== null && _o !== void 0 ? _o : '',
|
|
60
|
+
'app/src/environments/environment.ts': (_q = (_p = host.read(`${appRoot}/src/environments/environment.ts`)) === null || _p === void 0 ? void 0 : _p.toString()) !== null && _q !== void 0 ? _q : '',
|
|
61
|
+
'app/src/app/config.slice.ts': (_s = (_r = host.read(`${appRoot}/src/app/config.slice.ts`)) === null || _r === void 0 ? void 0 : _r.toString()) !== null && _s !== void 0 ? _s : '',
|
|
62
|
+
'app/src/app/intake.slice.ts': (_u = (_t = host.read(`${appRoot}/src/app/intake.slice.ts`)) === null || _t === void 0 ? void 0 : _t.toString()) !== null && _u !== void 0 ? _u : '',
|
|
61
63
|
},
|
|
62
64
|
}, host, serviceRoot, { additionalRoots: { 'app': appRoot } }));
|
|
63
65
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mern.js","sourceRoot":"","sources":["../../../../../../packages/nx-adsp/src/generators/mern/mern.ts"],"names":[],"mappings":";;AAmBA,
|
|
1
|
+
{"version":3,"file":"mern.js","sourceRoot":"","sources":["../../../../../../packages/nx-adsp/src/generators/mern/mern.ts"],"names":[],"mappings":"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"}
|