Skip to content

Commit dad7293

Browse files
authored
Merge pull request #44 from constructive-io/feat/rename-send-verification-link
refactor: rename email functions to send-verification-link and send-email
2 parents 5f0ee96 + 065f48d commit dad7293

53 files changed

Lines changed: 354 additions & 450 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AGENTS.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export default handler;
7979

8080
```json
8181
{
82-
"name": "send-email-link",
82+
"name": "send-verification-link",
8383
"version": "1.1.0",
8484
"description": "Sends invite, password reset, and verification emails",
8585
"type": "node-graphql",
@@ -111,7 +111,7 @@ export default handler;
111111
**Build Docker images:**
112112
```bash
113113
make docker-build # build all function images
114-
make docker-build-send-email-link # build single function image
114+
make docker-build-send-verification-link # build single function image
115115
```
116116

117117
**Local development with Docker:**
@@ -129,9 +129,9 @@ pnpm build # Recompile
129129

130130
## Key Details
131131

132-
- Each function declares its port in `handler.json` (`simple-email` 8081, `send-email-link` 8082, `knative-job-example` 8083, `python-example` 8084); the job service uses 8080
133-
- Email functions support dry-run via `SIMPLE_EMAIL_DRY_RUN` / `SEND_EMAIL_LINK_DRY_RUN`
134-
- `loadFunctionApp()` in job/service resolves modules by name (e.g. `@constructive-io/simple-email-fn`)
132+
- Each function declares its port in `handler.json` (`send-email` 8081, `send-verification-link` 8082, `knative-job-example` 8083, `python-example` 8084); the job service uses 8080
133+
- Email functions support dry-run via `SEND_EMAIL_DRY_RUN` / `SEND_VERIFICATION_LINK_DRY_RUN` (legacy `SIMPLE_EMAIL_DRY_RUN` / `SEND_EMAIL_LINK_DRY_RUN` still honored as fallback)
134+
- `loadFunctionApp()` in job/service resolves modules by name (e.g. `@constructive-io/send-email-fn`)
135135
- GraphQL clients require `GRAPHQL_URL` env var and `X-Database-Id` header
136136
- The `generated/` directory is entirely gitignored
137137
- Templates use `{{name}}`, `{{version}}`, `{{description}}` placeholders

CLAUDE.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Constructive Functions
22

3-
Serverless function workloads (simple-email, send-email-link) with a job queue system deployed via Kubernetes.
3+
Serverless function workloads (send-email, send-verification-link) with a job queue system deployed via Kubernetes.
44

55
## Project Structure
66

@@ -91,8 +91,8 @@ Edit `functions/<name>/handler.ts` → Skaffold syncs the file into the containe
9191
|---------|------------|
9292
| PostgreSQL | 5432 |
9393
| Job Service | 8080 |
94-
| simple-email | 8081 |
95-
| send-email-link | 8082 |
94+
| send-email | 8081 |
95+
| send-verification-link | 8082 |
9696

9797
## Debugging K8s Pods
9898

@@ -111,8 +111,8 @@ All pods should show `Running` (except `constructive-db` which should be `Comple
111111
kubectl logs -n constructive-functions -l app=knative-job-service -f
112112

113113
# Function logs
114-
kubectl logs -n constructive-functions -l app=simple-email -f
115-
kubectl logs -n constructive-functions -l app=send-email-link -f
114+
kubectl logs -n constructive-functions -l app=send-email -f
115+
kubectl logs -n constructive-functions -l app=send-verification-link -f
116116

117117
# Constructive server logs
118118
kubectl logs -n constructive-functions -l app=constructive-server -f
@@ -133,15 +133,15 @@ kubectl describe pod <pod-name> -n constructive-functions
133133
```bash
134134
kubectl port-forward -n constructive-functions svc/postgres 5432:5432
135135
kubectl port-forward -n constructive-functions svc/knative-job-service 8080:8080
136-
kubectl port-forward -n constructive-functions svc/simple-email 8081:80
137-
kubectl port-forward -n constructive-functions svc/send-email-link 8082:80
136+
kubectl port-forward -n constructive-functions svc/send-email 8081:80
137+
kubectl port-forward -n constructive-functions svc/send-verification-link 8082:80
138138
kubectl port-forward -n constructive-functions svc/constructive-server 3002:3000
139139
```
140140

141141
### Exec into a pod
142142

143143
```bash
144-
kubectl exec -it -n constructive-functions deploy/simple-email -- sh
144+
kubectl exec -it -n constructive-functions deploy/send-email -- sh
145145
kubectl exec -it -n constructive-functions deploy/knative-job-service -- sh
146146
```
147147

@@ -162,7 +162,7 @@ SELECT set_config('jwt.claims.database_id', (SELECT id::text FROM metaschema_pub
162162

163163
-- Manually insert a test job
164164
SELECT * FROM app_jobs.add_job(
165-
'simple-email'::text,
165+
'send-email'::text,
166166
'{"to":"test@example.com","subject":"test","html":"<p>hello</p>"}'::json
167167
);
168168
```
@@ -171,7 +171,7 @@ SELECT * FROM app_jobs.add_job(
171171

172172
```bash
173173
kubectl rollout restart -n constructive-functions deploy/knative-job-service
174-
kubectl rollout restart -n constructive-functions deploy/simple-email
174+
kubectl rollout restart -n constructive-functions deploy/send-email
175175
```
176176

177177
### GHCR pull secret

DEVELOPMENT.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ After this you should have built artifacts in:
5858

5959
| Package | Output |
6060
|---------|--------|
61-
| `generated/send-email-link/dist/` | Send-email-link function server |
62-
| `generated/simple-email/dist/` | Simple-email function server |
61+
| `generated/send-verification-link/dist/` | Send-verification-link function server |
62+
| `generated/send-email/dist/` | Send-email function server |
6363
| `generated/example/dist/` | knative-job-example function server |
6464
| `generated/python-example/dist/` | Python example function server |
6565
| `job/service/dist/` | Knative job service (worker + scheduler) |
@@ -112,20 +112,20 @@ This runs `scripts/dev.ts` which spawns local Node processes with env vars point
112112
| Process | Port | Script |
113113
|---------|------|--------|
114114
| **job-service** | 8080 | `job/service/dist/run.js` |
115-
| **simple-email** | 8081 | `generated/simple-email/dist/index.js` |
116-
| **send-email-link** | 8082 | `generated/send-email-link/dist/index.js` |
115+
| **send-email** | 8081 | `generated/send-email/dist/index.js` |
116+
| **send-verification-link** | 8082 | `generated/send-verification-link/dist/index.js` |
117117
| **knative-job-example** | 8083 | `generated/example/dist/index.js` |
118118
| **python-example** | 8084 | `generated/python-example/...` (python entrypoint) |
119119

120120
To start a single function:
121121

122122
```bash
123-
pnpm dev:fn -- --only=send-email-link
123+
pnpm dev:fn -- --only=send-verification-link
124124
```
125125

126126
### 4. Test a Function
127127

128-
Send a request to `send-email-link`:
128+
Send a request to `send-verification-link`:
129129

130130
```bash
131131
curl -X POST http://localhost:8082 \
@@ -176,8 +176,8 @@ make dev-down # Stop Docker infrastructure
176176
| Mailpit SMTP | 1025 |
177177
| Mailpit UI | 8025 |
178178
| Job Service | 8080 |
179-
| simple-email | 8081 |
180-
| send-email-link | 8082 |
179+
| send-email | 8081 |
180+
| send-verification-link | 8082 |
181181
| knative-job-example | 8083 |
182182
| python-example | 8084 |
183183

@@ -190,8 +190,8 @@ Docker Compose (infrastructure):
190190
191191
Local Node processes (functions):
192192
job/service/dist/run.js (port 8080)
193-
generated/simple-email/dist/index.js (port 8081)
194-
generated/send-email-link/dist/index.js (port 8082)
193+
generated/send-email/dist/index.js (port 8081)
194+
generated/send-verification-link/dist/index.js (port 8082)
195195
```
196196

197197
Infrastructure runs in Docker. Functions run as local Node processes from `generated/` — no Docker rebuild needed when function code changes. Edit `functions/*/handler.ts`, rebuild (`pnpm build`), restart `make dev-fn`.
@@ -252,7 +252,7 @@ make skaffold-dev
252252
This runs `skaffold dev -p local-simple` which:
253253
1. Builds the `constructive-functions` Docker image from `Dockerfile.dev`
254254
2. Deploys infrastructure (postgres, minio, constructive-server, constructive-server-admin, db-setup, job-service) via kustomize
255-
3. Deploys functions (simple-email, send-email-link) via generated rawYaml manifests
255+
3. Deploys functions (send-email, send-verification-link) via generated rawYaml manifests
256256
4. Sets up port-forwarding automatically
257257
5. Watches `functions/**/*.ts` — edits are synced into running containers
258258
6. `tsx --watch` inside each function container detects changes and restarts
@@ -283,8 +283,8 @@ Changes to runtime packages (`packages/fn-runtime`, `packages/fn-app`) or `packa
283283

284284
| Service | Local Port |
285285
|---------|------------|
286-
| simple-email | 8081 |
287-
| send-email-link | 8082 |
286+
| send-email | 8081 |
287+
| send-verification-link | 8082 |
288288
| knative-job-example | 8083 |
289289
| python-example | 8084 |
290290
| Job Service | 8080 |

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ setup-check:
4444
skaffold-dev:
4545
skaffold dev -p local-simple
4646

47-
# Single function: make skaffold-dev-simple-email
47+
# Single function: make skaffold-dev-send-email
4848
skaffold-dev-%:
4949
skaffold dev -p $*
5050

@@ -57,6 +57,6 @@ skaffold-dev-knative:
5757
docker-build:
5858
pnpm run docker:build
5959

60-
# Build a single function image: make docker-build-send-email-link
60+
# Build a single function image: make docker-build-send-verification-link
6161
docker-build-%:
6262
node --experimental-strip-types scripts/docker-build.ts --only=$*

README.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,38 +27,38 @@ pnpm install # install dependencies (including generated packages)
2727
pnpm build # build all packages and functions
2828

2929
make docker-build # build all function Docker images
30-
make docker-build-simple-email # build a single function image
31-
make docker-build-send-email-link
30+
make docker-build-send-email # build a single function image
31+
make docker-build-send-verification-link
3232
```
3333

3434
## Functions
3535

3636
| Function | Port | Type | Image |
3737
|----------|------|------|-------|
38-
| `simple-email` | 8081 | node-graphql | `ghcr.io/constructive-io/constructive-functions/simple-email:latest` |
39-
| `send-email-link` | 8082 | node-graphql | `ghcr.io/constructive-io/constructive-functions/send-email-link:latest` |
40-
| `knative-job-example` | 8083 | node-graphql | `ghcr.io/constructive-io/constructive-functions/knative-job-example:latest` |
41-
| `python-example` | 8084 | python | `ghcr.io/constructive-io/constructive-functions/python-example:latest` |
38+
| `send-email` | 8081 | node-graphql | `ghcr.io/constructive-io/send-email-fn:latest` |
39+
| `send-verification-link` | 8082 | node-graphql | `ghcr.io/constructive-io/send-verification-link-fn:latest` |
40+
| `knative-job-example` | 8083 | node-graphql | `ghcr.io/constructive-io/knative-job-example-fn:latest` |
41+
| `python-example` | 8084 | python | `ghcr.io/constructive-io/python-example-fn:latest` |
4242

4343
Port `8080` is reserved for the job service.
4444

45-
### `simple-email`
45+
### `send-email`
4646

4747
Sends emails directly from a job payload.
4848

49-
- `SIMPLE_EMAIL_DRY_RUN` — if `true`, logs the payload instead of sending
49+
- `SEND_EMAIL_DRY_RUN` — if `true`, logs the payload instead of sending
5050
- `MAILGUN_API_KEY`, `MAILGUN_KEY`, `MAILGUN_DOMAIN`, `MAILGUN_FROM`, `MAILGUN_REPLY` — Mailgun config
5151

52-
### `send-email-link`
52+
### `send-verification-link`
5353

5454
Sends invite, password reset, and verification emails (rendered via MJML).
5555

56-
- `SEND_EMAIL_LINK_DRY_RUN` — if `true`, logs the payload instead of sending
56+
- `SEND_VERIFICATION_LINK_DRY_RUN` — if `true`, logs the payload instead of sending
5757
- `DEFAULT_DATABASE_ID` — default database UUID
5858
- `GRAPHQL_URL`, `META_GRAPHQL_URL` — GraphQL API endpoints
5959
- `GRAPHQL_AUTH_TOKEN` — optional Bearer token for GraphQL requests
6060
- `LOCAL_APP_PORT` — local port for dashboard links (e.g. `3000`)
61-
- `MAILGUN_*` — same Mailgun config as `simple-email`
61+
- `MAILGUN_*` — same Mailgun config as `send-email`
6262

6363
### `knative-job-example` / `python-example`
6464

@@ -114,10 +114,10 @@ The `CI Test K8s` workflow (`.github/workflows/test-k8s-deployment.yaml`) runs o
114114
Images are tagged with the GHCR prefix automatically:
115115

116116
```bash
117-
docker push ghcr.io/constructive-io/constructive-functions/simple-email:latest
118-
docker push ghcr.io/constructive-io/constructive-functions/send-email-link:latest
119-
docker push ghcr.io/constructive-io/constructive-functions/knative-job-example:latest
120-
docker push ghcr.io/constructive-io/constructive-functions/python-example:latest
117+
docker push ghcr.io/constructive-io/send-email-fn:latest
118+
docker push ghcr.io/constructive-io/send-verification-link-fn:latest
119+
docker push ghcr.io/constructive-io/knative-job-example-fn:latest
120+
docker push ghcr.io/constructive-io/python-example-fn:latest
121121
```
122122

123123
- `make docker-build` — builds all function images

docs/plan/00-overview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ Generated packages symlink `handler.ts` and `*.d.ts` back to `functions/<name>/`
8686
- [x] fn-runtime package (server, context, GraphQL clients, types)
8787
- [x] fn-app package (Express factory with error middleware)
8888
- [x] job/service (KnativeJobsSvc with function loading and job orchestration)
89-
- [x] 3 functions: example, simple-email, send-email-link
89+
- [x] 3 functions: example, send-email, send-verification-link
9090
- [x] Docker compose dev setup
9191
- [x] K8s base manifests and overlays
9292

docs/plan/01-docker-ci.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ jobs:
143143
144144
**Dynamic matrix via discovery job**: The `discover` job reads `functions/*/handler.json` and outputs a JSON matrix. This means adding a new function only requires creating `functions/<name>/handler.json` — no workflow changes needed.
145145

146-
**`matrix.dir` vs `matrix.name`**: The directory name (e.g., `simple-email`) may differ from the handler.json `name` field (e.g., `knative-job-example` vs dir `example`). We pass both:
146+
**`matrix.dir` vs `matrix.name`**: The directory name (e.g., `send-email`) may differ from the handler.json `name` field (e.g., `knative-job-example` vs dir `example`). We pass both:
147147
- `matrix.dir` — used for `--only=` flag and Dockerfile path (generate.ts filters by directory name)
148148
- `matrix.name` — used for image naming (from handler.json `name` field)
149149

@@ -161,8 +161,8 @@ jobs:
161161
- Each builds successfully but does NOT push (PR event)
162162

163163
2. **Push test**: Merge to main. Verify:
164-
- Images appear at `ghcr.io/<owner>/simple-email-fn:latest`
165-
- Images appear at `ghcr.io/<owner>/send-email-link-fn:latest`
164+
- Images appear at `ghcr.io/<owner>/send-email-fn:latest`
165+
- Images appear at `ghcr.io/<owner>/send-verification-link-fn:latest`
166166
- Short SHA tags are applied
167167

168168
3. **New function test**: Add a new `functions/test-fn/handler.json` and verify it appears as a 4th matrix job automatically.

docs/plan/02-testing-strategy.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ Each function has:
1818

1919
### Handler testing challenges
2020

21-
**simple-email** (`functions/simple-email/handler.ts`):
21+
**send-email** (`functions/send-email/handler.ts`):
2222
- Lines 30-31: `isDryRun` and `useSmtp` read from `process.env` at **module load time** (top-level `const`)
2323
- Tests must set env vars BEFORE importing the handler, or use `vi.stubEnv()` + dynamic `import()`
2424
- Mocks needed: `sendPostmaster`, `sendSmtp` from imported modules
2525

26-
**send-email-link** (`functions/send-email-link/handler.ts`):
26+
**send-verification-link** (`functions/send-verification-link/handler.ts`):
2727
- Lines 73-74: `isDryRun` and `useSmtp` read from `context.env` (not process.env) — per-request, easier to test
2828
- Makes GraphQL calls: `meta.request(GetDatabaseInfo)` and `client.request(GetUser)`
2929
- Tests mock GraphQL client responses to control site/user data
@@ -157,7 +157,7 @@ describe('example handler', () => {
157157
});
158158
```
159159

160-
#### `functions/simple-email/__tests__/handler.test.ts`
160+
#### `functions/send-email/__tests__/handler.test.ts`
161161

162162
Key challenge: `isDryRun` and `useSmtp` are module-level constants (lines 30-31). Use `vi.stubEnv()` and dynamic import with `vi.resetModules()`.
163163

@@ -173,12 +173,12 @@ vi.mock('@constructive-io/postmaster', () => ({
173173
send: vi.fn().mockResolvedValue(undefined)
174174
}));
175175

176-
describe('simple-email handler', () => {
176+
describe('send-email handler', () => {
177177
let handler: any;
178178

179179
beforeEach(async () => {
180180
vi.resetModules();
181-
vi.stubEnv('SIMPLE_EMAIL_DRY_RUN', 'false');
181+
vi.stubEnv('SEND_EMAIL_DRY_RUN', 'false');
182182
vi.stubEnv('EMAIL_SEND_USE_SMTP', 'false');
183183
const mod = await import('../handler');
184184
handler = mod.default;
@@ -230,7 +230,7 @@ describe('simple-email handler', () => {
230230
describe('dry-run mode', () => {
231231
beforeEach(async () => {
232232
vi.resetModules();
233-
vi.stubEnv('SIMPLE_EMAIL_DRY_RUN', 'true');
233+
vi.stubEnv('SEND_EMAIL_DRY_RUN', 'true');
234234
const mod = await import('../handler');
235235
handler = mod.default;
236236
});
@@ -248,7 +248,7 @@ describe('simple-email handler', () => {
248248
});
249249
```
250250

251-
#### `functions/send-email-link/__tests__/handler.test.ts`
251+
#### `functions/send-verification-link/__tests__/handler.test.ts`
252252

253253
```typescript
254254
import { describe, it, expect, vi, beforeEach } from 'vitest';
@@ -285,7 +285,7 @@ const mockSiteData = {
285285
}
286286
};
287287

288-
describe('send-email-link handler', () => {
288+
describe('send-verification-link handler', () => {
289289
let handler: any;
290290

291291
beforeEach(async () => {
@@ -377,7 +377,7 @@ describe('send-email-link handler', () => {
377377
it('logs but does not send when DRY_RUN is true', async () => {
378378
const ctx = createMockContext({
379379
metaResponse: mockSiteData,
380-
env: { SEND_EMAIL_LINK_DRY_RUN: 'true' }
380+
env: { SEND_VERIFICATION_LINK_DRY_RUN: 'true' }
381381
});
382382
const result = await handler({
383383
email_type: 'forgot_password',
@@ -574,8 +574,8 @@ This layer requires both Docker images (WS1) and Knative services to be working.
574574
| Create | `.github/workflows/test.yaml` |
575575
| Create | `tests/unit/helpers/mock-context.ts` |
576576
| Create | `functions/example/__tests__/handler.test.ts` |
577-
| Create | `functions/simple-email/__tests__/handler.test.ts` |
578-
| Create | `functions/send-email-link/__tests__/handler.test.ts` |
577+
| Create | `functions/send-email/__tests__/handler.test.ts` |
578+
| Create | `functions/send-verification-link/__tests__/handler.test.ts` |
579579
| Create | `tests/integration/helpers/start-function.ts` |
580580
| Create | `tests/integration/runtime.test.ts` |
581581
| Modify | `package.json` — add vitest devDep + test scripts |

0 commit comments

Comments
 (0)