Skip to content

Commit 9bb2576

Browse files
authored
Merge pull request #14 from hotosm/feat/pgsql-http
Simplify code to use pgsql-http requests directly, instead of pg_notify
2 parents f58a092 + 5a7975f commit 9bb2576

21 files changed

+1453
-1725
lines changed

.github/workflows/http-postgres.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on:
44
push:
55
branches: [main]
66
paths:
7-
- 'http.dockerfile'
7+
- 'pgsql-http.dockerfile'
88
- '.github/workflows/http-postgres.yml'
99
workflow_dispatch:
1010

@@ -22,4 +22,4 @@ jobs:
2222
build_target: pg-${{ matrix.pg_version }}
2323
image_tags: |
2424
"ghcr.io/hotosm/postgres:${{ matrix.pg_version }}-http"
25-
dockerfile: http.dockerfile
25+
dockerfile: pgsql-http.dockerfile

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
runs-on: ubuntu-latest
2020

2121
container:
22-
image: docker.io/goreleaser/goreleaser-cross:v1.24
22+
image: docker.io/goreleaser/goreleaser-cross:v1.25
2323

2424
steps:
2525
- name: Checkout

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.24 AS base
1+
FROM golang:1.25 AS base
22

33

44
# Build statically compiled binary

README.md

Lines changed: 56 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<!-- markdownlint-disable -->
44
<p align="center">
5-
<em>A lightweight webhook for ODK Central submissions and entity property updates.</em>
5+
<em>A lightweight CLI tool for installing webhook triggers in ODK Central database.</em>
66
</p>
77
<p align="center">
88
<a href="https://github.com/hotosm/central-webhook/actions/workflows/release.yml" target="_blank">
@@ -17,75 +17,63 @@
1717

1818
<!-- markdownlint-enable -->
1919

20-
Call a remote API on ODK Central database events:
20+
Install PostgreSQL triggers that automatically call remote APIs on ODK Central database events:
2121

2222
- New submission (XML).
2323
- Update entity (entity properties).
2424
- Submission review (approved, hasIssues, rejected).
2525

26-
The `centralwebhook` binary is small ~15MB and only consumes
27-
~5MB of memory when running.
28-
29-
> [!NOTE]
30-
> There is a 8000 byte Postgres limit for the submission XML that can be sent
31-
> until [this issue](https://github.com/hotosm/central-webhook/issues/9)
32-
> is addressed.
33-
>
34-
> The submission XML will simply be truncated in this case.
26+
The `centralwebhook` tool is a simple CLI that installs or uninstalls database triggers. Once installed, the triggers use the `pgsql-http` extension to send HTTP requests directly from the database.
3527

3628
## Prerequisites
3729

38-
- ODK Central running, connecting to an accessible Postgresql database.
39-
- A POST webhook endpoint on your service API, to call when the selected
40-
event occurs.
30+
- ODK Central running, connecting to an accessible PostgreSQL database.
31+
- The `pgsql-http` extension installed and enabled in your PostgreSQL database:
32+
```sql
33+
CREATE EXTENSION http;
34+
```
35+
> [!NOTE]
36+
> **Using our helper images**: We provide PostgreSQL images with the `pgsql-http` extension pre-installed:
37+
> - `ghcr.io/hotosm/postgres:18-http` (based on vanilla PostgreSQL 18 images)
38+
>
39+
> These images are drop-in replacements for standard PostgreSQL images and simply add the extension.
40+
>
41+
> **Installing manually**: If you don't wish to use these images, you must install the `pgsql-http` extension yourself. The extension may require superuser privileges to install. If you cannot install it yourself, ask your database administrator.
42+
- A POST webhook endpoint on your service API, to call when the selected event occurs.
4143

4244
## Usage
4345

44-
The `centralwebhook` tool is a service that runs continually, monitoring the
45-
ODK Central database for updates and triggering the webhook as appropriate.
46+
The `centralwebhook` tool is a CLI that installs or uninstalls database triggers. After installation, the triggers run automatically whenever audit events occur in the database.
4647

47-
### Integrate Into [ODK Central](https://github.com/getodk/central) Stack
48+
### Install Triggers
4849

49-
- It's possible to include this as part of the standard ODK Central docker
50-
compose stack.
51-
- First add the environment variables to your `.env` file:
50+
Install webhook triggers in your database:
5251

53-
```dotenv
54-
CENTRAL_WEBHOOK_UPDATE_ENTITY_URL=https://your.domain.com/some/webhook
55-
CENTRAL_WEBHOOK_REVIEW_SUBMISSION_URL=https://your.domain.com/some/webhook
56-
CENTRAL_WEBHOOK_NEW_SUBMISSION_URL=https://your.domain.com/some/webhook
57-
CENTRAL_WEBHOOK_API_KEY=your_api_key_key
58-
```
52+
```bash
53+
./centralwebhook install \
54+
-db 'postgresql://{user}:{password}@{hostname}/{db}?sslmode=disable' \
55+
-updateEntityUrl 'https://your.domain.com/some/webhook' \
56+
-newSubmissionUrl 'https://your.domain.com/some/webhook' \
57+
-reviewSubmissionUrl 'https://your.domain.com/some/webhook'
58+
```
5959

6060
> [!TIP]
61-
> Omit a xxx_URL variable if you do not wish to use that particular webhook.
61+
> Omit a webhook URL flag if you do not wish to use that particular webhook.
6262
>
63-
> The CENTRAL_WEBHOOK_API_KEY variable is also optional, see the
64-
> [APIs With Authentication](#apis-with-authentication) section.
65-
66-
- Then extend the docker compose configuration at startup:
67-
68-
```bash
69-
# Starting from the getodk/central code repo
70-
docker compose -f docker-compose.yml -f /path/to/this/repo/compose.webhook.yml up -d
71-
```
72-
73-
### Other Ways To Run
63+
> The `-apiKey` flag is optional, see the [APIs With Authentication](#apis-with-authentication) section.
7464
75-
<details>
76-
<summary>Via Docker (Standalone)</summary>
65+
### Uninstall Triggers
7766

78-
#### Via Docker (Standalone)
67+
Remove webhook triggers from your database:
7968

8069
```bash
81-
docker run -d ghcr.io/hotosm/central-webhook:latest \
82-
-db 'postgresql://{user}:{password}@{hostname}/{db}?sslmode=disable' \
83-
-updateEntityUrl 'https://your.domain.com/some/webhook' \
84-
-newSubmissionUrl 'https://your.domain.com/some/webhook' \
85-
-reviewSubmissionUrl 'https://your.domain.com/some/webhook'
70+
./centralwebhook uninstall \
71+
-db 'postgresql://{user}:{password}@{hostname}/{db}?sslmode=disable'
8672
```
8773

88-
Environment variables are also supported:
74+
### Environment Variables
75+
76+
All flags can also be provided via environment variables:
8977

9078
```dotenv
9179
CENTRAL_WEBHOOK_DB_URI=postgresql://user:pass@localhost:5432/db_name?sslmode=disable
@@ -96,74 +84,22 @@ CENTRAL_WEBHOOK_API_KEY=ksdhfiushfiosehf98e3hrih39r8hy439rh389r3hy983y
9684
CENTRAL_WEBHOOK_LOG_LEVEL=DEBUG
9785
```
9886

99-
</details>
100-
101-
<details>
102-
<summary>Via Binary (Standalone)</summary>
87+
### Via Docker
10388

104-
#### Via Binary (Standalone)
105-
106-
Download the binary for your platform from the
107-
[releases](https://github.com/hotosm/central-webhook/releases) page.
108-
109-
Then run with:
89+
You can run the CLI tool via Docker:
11090

11191
```bash
112-
./centralwebhook \
92+
docker run --rm ghcr.io/hotosm/central-webhook:latest install \
11393
-db 'postgresql://{user}:{password}@{hostname}/{db}?sslmode=disable' \
11494
-updateEntityUrl 'https://your.domain.com/some/webhook' \
11595
-newSubmissionUrl 'https://your.domain.com/some/webhook' \
11696
-reviewSubmissionUrl 'https://your.domain.com/some/webhook'
11797
```
11898

119-
> It's possible to specify a single webhook event, or multiple.
120-
121-
</details>
122-
123-
<details>
124-
<summary>Via Code</summary>
125-
126-
#### Via Code
127-
128-
Usage via the code / API:
129-
130-
```go
131-
package main
132-
133-
import (
134-
"fmt"
135-
"context"
136-
"log/slog"
137-
138-
"github.com/hotosm/central-webhook/db"
139-
"github.com/hotosm/central-webhook/webhook"
140-
)
141-
142-
ctx := context.Background()
143-
log := slog.New()
144-
145-
dbPool, err := db.InitPool(ctx, log, "postgresql://{user}:{password}@{hostname}/{db}?sslmode=disable")
146-
if err != nil {
147-
fmt.Fprintf(os.Stderr, "could not connect to database: %v", err)
148-
}
149-
150-
err = SetupWebhook(
151-
log,
152-
ctx,
153-
dbPool,
154-
nil,
155-
"https://your.domain.com/some/entity/webhook",
156-
"https://your.domain.com/some/submission/webhook",
157-
"https://your.domain.com/some/review/webhook",
158-
)
159-
if err != nil {
160-
fmt.Fprintf(os.Stderr, "error setting up webhook: %v", err)
161-
}
162-
```
163-
164-
> To not provide a webhook for an event, pass `nil` as the url.
99+
### Download Binary
165100

166-
</details>
101+
Download the binary for your platform from the
102+
[releases](https://github.com/hotosm/central-webhook/releases) page.
167103

168104
## Webhook Request Payload Examples
169105

@@ -208,7 +144,7 @@ Many APIs will not be public and require some sort of authentication.
208144
There is an optional `-apiKey` flag that can be used to pass
209145
an API key / token provided by the application.
210146

211-
This will be inserted in the `X-API-Key` request header.
147+
This will be inserted in the `X-API-Key` request header when the trigger sends HTTP requests.
212148

213149
No other authentication methods are supported for now, but feel
214150
free to open an issue (or PR!) for a proposal to support other
@@ -217,7 +153,7 @@ auth methods.
217153
Example:
218154

219155
```bash
220-
./centralwebhook \
156+
./centralwebhook install \
221157
-db 'postgresql://{user}:{password}@{hostname}/{db}?sslmode=disable' \
222158
-updateEntityUrl 'https://your.domain.com/some/webhook' \
223159
-apiKey 'ksdhfiushfiosehf98e3hrih39r8hy439rh389r3hy983y'
@@ -284,18 +220,25 @@ async def update_entity_status_in_fmtm(
284220
raise HTTPException(status_code=400, detail=msg)
285221
```
286222

223+
## How It Works
224+
225+
The tool installs PostgreSQL triggers on the `audits` table that:
226+
227+
1. Detect when audit events occur (entity updates, submission creates/updates)
228+
2. Format the event data into a JSON payload with `type`, `id`, and `data` fields
229+
3. Use the `pgsql-http` extension to send an HTTP POST request directly from the database
230+
4. Include the `X-API-Key` header if provided during installation
231+
232+
The triggers run automatically after installation - no long-running service is needed.
233+
287234
## Development
288235

289-
- This package mostly uses the standard library, plus a Postgres driver
290-
and testing framework.
236+
- This package uses the standard library and a Postgres driver.
291237
- Binary and container image distribution is automated on new **release**.
292238

293239
### Run The Tests
294240

295-
The test suite depends on a database, so the most convenient way is to run
296-
via docker.
297-
298-
There is a pre-configured `compose.yml` for testing:
241+
The test suite depends on a database with the `pgsql-http` extension installed. The most convenient way is to run via docker:
299242

300243
```bash
301244
docker compose run --rm webhook

compose.webhook.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@ services:
1616
postgres14:
1717
condition: service_started
1818
restart: always
19+
20+
postgres14:
21+
image: "ghcr.io/hotosm/postgres:14-http"

compose.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ services:
1717
- ./main.go:/app/main.go:ro
1818
- ./main_test.go:/app/main_test.go:ro
1919
- ./db:/app/db:ro
20-
- ./webhook:/app/webhook:ro
21-
- ./parser:/app/parser:ro
2220
# environment:
2321
# # Override to use database on host
2422
# CENTRAL_WEBHOOK_DB_URI: postgresql://odk:odk@host.docker.internal:5434/odk?sslmode=disable
@@ -36,7 +34,10 @@ services:
3634
entrypoint: go test -timeout=2m -v ./...
3735

3836
db:
39-
image: "postgis/postgis:17-3.5-alpine"
37+
image: "ghcr.io/hotosm/postgres:18-http"
38+
build:
39+
dockerfile: pgsql-http.dockerfile
40+
target: pg-18
4041
container_name: centralwebhook-db
4142
environment:
4243
- POSTGRES_USER=odk

0 commit comments

Comments
 (0)