diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2d2f53c..220e9a5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,6 +17,6 @@ jobs: build_target: build tag_override: ci compose_file: compose.yml - compose_service: odkhook + compose_service: webhook cache_extra_imgs: | "docker.io/postgis/postgis:17-3.5-alpine" diff --git a/.gitignore b/.gitignore index 8267296..1ab37e4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,8 @@ *.dll *.so *.dylib -**/**/odk-webhook -**/**/odkhook +**/**/central-webhook +**/**/centralwebhook # Test binary, built with `go test -c` *.test diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 9256ac4..9384509 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,7 +1,7 @@ version: 2 builds: - - binary: odkhook + - binary: centralwebhook main: main.go env: - CGO_ENABLED=0 @@ -18,7 +18,7 @@ builds: - -s -w -X main.version={{.Tag}} -X main.buildTime={{.Date}} archives: - - name_template: "odkhook_{{ .Version }}_{{ .Os }}_{{ .Arch }}" + - name_template: "centralwebhook_{{ .Version }}_{{ .Os }}_{{ .Arch }}" wrap_in_directory: false format: tar.gz # use zip for windows archives diff --git a/Dockerfile b/Dockerfile index eb01e4f..bde8d80 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . -RUN CGO_ENABLED=0 GOOS=linux go build -o /app/odkhook +RUN CGO_ENABLED=0 GOOS=linux go build -o /app/centralwebhook # Run the tests in the container @@ -24,8 +24,8 @@ RUN useradd -u 1000 nonroot -g 1000 # Deploy the application binary into sratch image FROM scratch AS release WORKDIR /app -COPY --from=build /app/odkhook /app/odkhook +COPY --from=build /app/centralwebhook /app/centralwebhook COPY --from=useradd /etc/group /etc/group COPY --from=useradd /etc/passwd /etc/passwd USER nonroot:nonroot -ENTRYPOINT ["/app/odkhook"] +ENTRYPOINT ["/app/centralwebhook"] diff --git a/README.md b/README.md index d52ebf7..947c541 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ODK Webhook +# Central Webhook Call a remote API on ODK Central database events: @@ -7,18 +7,18 @@ Call a remote API on ODK Central database events: ## Usage -The `odkhook` tool is a service that runs continually, monitoring the +The `centralwebhook` tool is a service that runs continually, monitoring the ODK Central database for updates and triggering the webhook as appropriate. ### Binary Download the binary for your platform from the -[releases](https://github.com/hotosm/odk-webhook/releases) page. +[releases](https://github.com/hotosm/central-webhook/releases) page. Then run with: ```bash -./odkhook \ +./centralwebhook \ -db 'postgresql://{user}:{password}@{hostname}/{db}?sslmode=disable' \ -entityUrl 'https://your.domain.com/some/webhook' \ -submissionUrl 'https://your.domain.com/some/webhook' @@ -30,12 +30,22 @@ Then run with: ### Docker ```bash -docker run -d ghcr.io/hotosm/odk-webhook:latest \ +docker run -d ghcr.io/hotosm/central-webhook:latest \ -db 'postgresql://{user}:{password}@{hostname}/{db}?sslmode=disable' \ -entityUrl 'https://your.domain.com/some/webhook' \ -submissionUrl 'https://your.domain.com/some/webhook' ``` +Environment variables are also supported: + +```dotenv +CENTRAL_WEBHOOK_DB_URI=postgresql://user:pass@localhost:5432/db_name?sslmode=disable +CENTRAL_WEBHOOK_ENTITY_URL=https://your.domain.com/some/webhook +CENTRAL_WEBHOOK_SUBMISSION_URL=https://your.domain.com/some/webhook +CENTRAL_WEBHOOK_API_KEY=ksdhfiushfiosehf98e3hrih39r8hy439rh389r3hy983y +CENTRAL_WEBHOOK_LOG_LEVEL=DEBUG +``` + > [!NOTE] > Alternatively, add the service to your docker compose stack. @@ -51,8 +61,8 @@ import ( "context" "log/slog" - "github.com/hotosm/odk-webhook/db" - "github.com/hotosm/odk-webhook/webhook" + "github.com/hotosm/central-webhook/db" + "github.com/hotosm/central-webhook/webhook" ) ctx := context.Background() @@ -78,3 +88,25 @@ if err != nil { > [!NOTE] > To not provide a webhook for either entities or submissions, > pass `nil` instead. + +## APIs With Authentication + +Many APIs will not be public and require some sort of authentication. + +There is an optional `-apiKey` flag that can be used to pass +an API key / token provided by the application. + +This will be inserted in the `X-API-Key` request header. + +No other authentication methods are supported for now, but feel +free to open an issue (or PR!) for a proposal to support other +auth methods. + +Example: + +```bash +./centralwebhook \ + -db 'postgresql://{user}:{password}@{hostname}/{db}?sslmode=disable' \ + -entityUrl 'https://your.domain.com/some/webhook' \ + -apiKey 'ksdhfiushfiosehf98e3hrih39r8hy439rh389r3hy983y' +``` diff --git a/compose.yml b/compose.yml index 6e357d7..58458b1 100644 --- a/compose.yml +++ b/compose.yml @@ -5,11 +5,11 @@ networks: name: fmtm-splitter services: - odkhook: - image: "ghcr.io/hotosm/odk-webhook:${TAG_OVERRIDE:-ci}" + webhook: + image: "ghcr.io/hotosm/central-webhook:${TAG_OVERRIDE:-ci}" build: target: build - container_name: odkhook + container_name: centralwebhook volumes: # Mount local files - ./go.mod:/app/go.mod:ro @@ -21,8 +21,7 @@ services: - ./parser:/app/parser:ro # environment: # # Override to use database on host - # ODK_WEBHOOK_DB_URI: postgresql://odk:odk@host.docker.internal:5434/odk?sslmode=disable - # ODK_WEBHOOK_WEBHOOK_URL: + # CENTRAL_WEBHOOK_DB_URI: postgresql://odk:odk@host.docker.internal:5434/odk?sslmode=disable depends_on: db: condition: service_healthy @@ -36,18 +35,18 @@ services: db: image: "postgis/postgis:17-3.5-alpine" - container_name: odkhook-db + container_name: centralwebhook-db environment: - POSTGRES_USER=odk - POSTGRES_PASSWORD=odk - - POSTGRES_DB=odkhook + - POSTGRES_DB=odk ports: - "5439:5432" networks: - net restart: "unless-stopped" healthcheck: - test: pg_isready -U odk -d odkhook + test: pg_isready -U odk -d odk start_period: 5s interval: 10s timeout: 5s diff --git a/db/notifier_test.go b/db/notifier_test.go index 6390a11..6558807 100644 --- a/db/notifier_test.go +++ b/db/notifier_test.go @@ -14,13 +14,13 @@ import ( // with username odk and password odk. // // The easiest way to ensure this is to run the tests with docker compose: -// docker compose run --rm odkhook +// docker compose run --rm webhook func TestNotifier(t *testing.T) { - dbUri := os.Getenv("ODK_WEBHOOK_DB_URI") + dbUri := os.Getenv("CENTRAL_WEBHOOK_DB_URI") if len(dbUri) == 0 { // Default - dbUri = "postgresql://odk:odk@db:5432/odkhook?sslmode=disable" + dbUri = "postgresql://odk:odk@db:5432/odk?sslmode=disable" } is := is.New(t) diff --git a/db/trigger_test.go b/db/trigger_test.go index f010d4f..f71de1f 100644 --- a/db/trigger_test.go +++ b/db/trigger_test.go @@ -16,13 +16,13 @@ import ( // with username odk and password odk. // // The easiest way to ensure this is to run the tests with docker compose: -// docker compose run --rm odkhook +// docker compose run --rm webhook func TestEntityTrigger(t *testing.T) { - dbUri := os.Getenv("ODK_WEBHOOK_DB_URI") + dbUri := os.Getenv("CENTRAL_WEBHOOK_DB_URI") if len(dbUri) == 0 { // Default - dbUri = "postgresql://odk:odk@db:5432/odkhook?sslmode=disable" + dbUri = "postgresql://odk:odk@db:5432/odk?sslmode=disable" } is := is.New(t) @@ -160,10 +160,10 @@ func TestEntityTrigger(t *testing.T) { // Test a new submission event type func TestSubmissionTrigger(t *testing.T) { - dbUri := os.Getenv("ODK_WEBHOOK_DB_URI") + dbUri := os.Getenv("CENTRAL_WEBHOOK_DB_URI") if len(dbUri) == 0 { // Default - dbUri = "postgresql://odk:odk@db:5432/odkhook?sslmode=disable" + dbUri = "postgresql://odk:odk@db:5432/odk?sslmode=disable" } is := is.New(t) @@ -304,10 +304,10 @@ func TestSubmissionTrigger(t *testing.T) { // Test an unsupported event type and ensure nothing is triggered func TestNoTrigger(t *testing.T) { - dbUri := os.Getenv("ODK_WEBHOOK_DB_URI") + dbUri := os.Getenv("CENTRAL_WEBHOOK_DB_URI") if len(dbUri) == 0 { // Default - dbUri = "postgresql://odk:odk@db:5432/odkhook?sslmode=disable" + dbUri = "postgresql://odk:odk@db:5432/odk?sslmode=disable" } is := is.New(t) diff --git a/go.mod b/go.mod index 03c50ba..01bfc5e 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/hotosm/odk-webhook +module github.com/hotosm/central-webhook go 1.23.2 diff --git a/main.go b/main.go index aa92b46..70486b2 100644 --- a/main.go +++ b/main.go @@ -15,9 +15,9 @@ import ( "github.com/jackc/pgx/v5/pgxpool" - "github.com/hotosm/odk-webhook/db" - "github.com/hotosm/odk-webhook/parser" - "github.com/hotosm/odk-webhook/webhook" + "github.com/hotosm/central-webhook/db" + "github.com/hotosm/central-webhook/parser" + "github.com/hotosm/central-webhook/webhook" ) func getDefaultLogger(lvl slog.Level) *slog.Logger { @@ -130,10 +130,10 @@ func main() { ctx := context.Background() // Read environment variables - defaultDbUri := os.Getenv("ODK_WEBHOOK_DB_URI") - defaultEntityUrl := os.Getenv("ODK_WEBHOOK_ENTITY_URL") - defaultSubmissionUrl := os.Getenv("ODK_WEBHOOK_SUBMISSION_URL") - defaultLogLevel := os.Getenv("ODK_WEBHOOK_LOG_LEVEL") + defaultDbUri := os.Getenv("CENTRAL_WEBHOOK_DB_URI") + defaultEntityUrl := os.Getenv("CENTRAL_WEBHOOK_ENTITY_URL") + defaultSubmissionUrl := os.Getenv("CENTRAL_WEBHOOK_SUBMISSION_URL") + defaultLogLevel := os.Getenv("CENTRAL_WEBHOOK_LOG_LEVEL") var dbUri string flag.StringVar(&dbUri, "db", defaultDbUri, "DB host (postgresql://{user}:{password}@{hostname}/{db}?sslmode=disable)") @@ -165,7 +165,7 @@ func main() { flag.PrintDefaults() os.Exit(1) } - + if entityUrl == "" && submissionUrl == "" { fmt.Fprintf(os.Stderr, "At least one of entityUrl or submissionUrl is required\n") flag.PrintDefaults() diff --git a/main_test.go b/main_test.go index 021babf..fb46a9e 100644 --- a/main_test.go +++ b/main_test.go @@ -14,15 +14,15 @@ package main // "github.com/matryer/is" -// "github.com/hotosm/odk-webhook/parser" -// "github.com/hotosm/odk-webhook/db" +// "github.com/hotosm/central-webhook/parser" +// "github.com/hotosm/central-webhook/db" // ) // func TestSetupWebhook(t *testing.T) { -// dbUri := os.Getenv("ODK_WEBHOOK_DB_URI") +// dbUri := os.Getenv("CENTRAL_WEBHOOK_DB_URI") // if len(dbUri) == 0 { // // Default -// dbUri = "postgresql://odk:odk@db:5432/odkhook?sslmode=disable" +// dbUri = "postgresql://odk:odk@db:5432/odk?sslmode=disable" // } // is := is.New(t) diff --git a/webhook/request.go b/webhook/request.go index bbe0604..a90e933 100644 --- a/webhook/request.go +++ b/webhook/request.go @@ -8,7 +8,7 @@ import ( "net/http" "time" - "github.com/hotosm/odk-webhook/parser" + "github.com/hotosm/central-webhook/parser" ) // SendRequest parses the request content JSON from the PostgreSQL notification diff --git a/webhook/request_test.go b/webhook/request_test.go index 083c666..48721ec 100644 --- a/webhook/request_test.go +++ b/webhook/request_test.go @@ -13,7 +13,7 @@ import ( "github.com/matryer/is" - "github.com/hotosm/odk-webhook/parser" + "github.com/hotosm/central-webhook/parser" ) func TestSendRequest(t *testing.T) {