diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index a8b4350..6366df1 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -5,8 +5,8 @@ repos:
rev: v4.6.0
hooks:
- id: trailing-whitespace
+ exclude: configgen/api-docs.md
- id: detect-private-key
- exclude: providers/gmail/gmail_test.go
- repo: https://github.com/google/yamlfmt
rev: v0.13.0
hooks:
diff --git a/config/.env.example b/config/.env.example
index 716200d..a75c67a 100644
--- a/config/.env.example
+++ b/config/.env.example
@@ -1,3 +1,7 @@
RIVERBOAT_REFRESHINTERVAL="10m"
RIVERBOAT_JOBQUEUE_DATABASEHOST="postgres://postgres:password@0.0.0.0:5432/jobs?sslmode=disable"
RIVERBOAT_JOBQUEUE_QUEUES=""
+RIVERBOAT_JOBQUEUE_WORKERS_DATABASEWORKER_CONFIG_ENABLED="true"
+RIVERBOAT_JOBQUEUE_WORKERS_DATABASEWORKER_CONFIG_BASEURL="http://localhost:1337"
+RIVERBOAT_JOBQUEUE_WORKERS_DATABASEWORKER_CONFIG_ENDPOINT="query"
+RIVERBOAT_JOBQUEUE_WORKERS_DATABASEWORKER_CONFIG_DEBUG="false"
diff --git a/config/config.example.yaml b/config/config.example.yaml
index 65cb312..e19d6de 100644
--- a/config/config.example.yaml
+++ b/config/config.example.yaml
@@ -2,6 +2,12 @@ jobQueue:
databaseHost: postgres://postgres:password@0.0.0.0:5432/jobs?sslmode=disable
queues: null
workers:
+ databaseWorker:
+ config:
+ baseUrl: http://localhost:1337
+ debug: false
+ enabled: true
+ endpoint: query
emailWorker:
devMode: true
fromEmail: no-reply@example.com
diff --git a/config/configmap.yaml b/config/configmap.yaml
index 068d1b7..d994e44 100644
--- a/config/configmap.yaml
+++ b/config/configmap.yaml
@@ -13,3 +13,7 @@ data:
RIVERBOAT_REFRESHINTERVAL: {{ .Values.riverboat.refreshInterval | default "10m" }}
RIVERBOAT_JOBQUEUE_DATABASEHOST: {{ .Values.riverboat.jobQueue.databaseHost | default "postgres://postgres:password@0.0.0.0:5432/jobs?sslmode=disable" }}
RIVERBOAT_JOBQUEUE_QUEUES: {{ .Values.riverboat.jobQueue.queues }}
+ RIVERBOAT_JOBQUEUE_WORKERS_DATABASEWORKER_CONFIG_ENABLED: {{ .Values.riverboat.jobQueue.workers.databaseWorker.config.enabled | default true }}
+ RIVERBOAT_JOBQUEUE_WORKERS_DATABASEWORKER_CONFIG_BASEURL: {{ .Values.riverboat.jobQueue.workers.databaseWorker.config.baseUrl | default "http://localhost:1337" }}
+ RIVERBOAT_JOBQUEUE_WORKERS_DATABASEWORKER_CONFIG_ENDPOINT: {{ .Values.riverboat.jobQueue.workers.databaseWorker.config.endpoint | default "query" }}
+ RIVERBOAT_JOBQUEUE_WORKERS_DATABASEWORKER_CONFIG_DEBUG: {{ .Values.riverboat.jobQueue.workers.databaseWorker.config.debug | default false }}
diff --git a/configgen/api-docs.md b/configgen/api-docs.md
index 5234dfd..30f2413 100644
--- a/configgen/api-docs.md
+++ b/configgen/api-docs.md
@@ -20,7 +20,7 @@ Config is the configuration for the river server
|----|----|-----------|--------|
|**databaseHost**|`string`|DatabaseHost for connecting to the postgres database
||
|[**queues**](#jobqueuequeues)|`array`|||
-|[**workers**](#jobqueueworkers)|`object`|||
+|[**workers**](#jobqueueworkers)|`object`|Workers that will be enabled on the server
||
**Additional Properties:** not allowed
@@ -31,11 +31,15 @@ Config is the configuration for the river server
### jobQueue\.workers: object
+Workers that will be enabled on the server
+
+
**Properties**
|Name|Type|Description|Required|
|----|----|-----------|--------|
|[**emailWorker**](#jobqueueworkersemailworker)|`object`|||
+|[**databaseWorker**](#jobqueueworkersdatabaseworker)|`object`|||
**Additional Properties:** not allowed
@@ -51,4 +55,27 @@ Config is the configuration for the river server
|**fromEmail**|`string`|||
**Additional Properties:** not allowed
+
+#### jobQueue\.workers\.databaseWorker: object
+
+**Properties**
+
+|Name|Type|Description|Required|
+|----|----|-----------|--------|
+|[**config**](#jobqueueworkersdatabaseworkerconfig)|`object`|||
+
+**Additional Properties:** not allowed
+
+##### jobQueue\.workers\.databaseWorker\.config: object
+
+**Properties**
+
+|Name|Type|Description|Required|
+|----|----|-----------|--------|
+|**enabled**|`boolean`|Enable the dbx client
||
+|**baseUrl**|`string`|Base URL for the dbx service
||
+|**endpoint**|`string`|Endpoint for the graphql api
||
+|**debug**|`boolean`|Enable debug mode
||
+
+**Additional Properties:** not allowed
diff --git a/configgen/riverboat.config.json b/configgen/riverboat.config.json
index e153f1f..850da5e 100644
--- a/configgen/riverboat.config.json
+++ b/configgen/riverboat.config.json
@@ -8,6 +8,38 @@
},
"type": "array"
},
+ "dbxclient.Config": {
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Enable the dbx client"
+ },
+ "baseUrl": {
+ "type": "string",
+ "description": "Base URL for the dbx service"
+ },
+ "endpoint": {
+ "type": "string",
+ "description": "Endpoint for the graphql api"
+ },
+ "debug": {
+ "type": "boolean",
+ "description": "Enable debug mode"
+ }
+ },
+ "additionalProperties": false,
+ "type": "object"
+ },
+ "jobs.DatabaseWorker": {
+ "properties": {
+ "config": {
+ "$ref": "#/$defs/dbxclient.Config",
+ "description": "the database configuration"
+ }
+ },
+ "additionalProperties": false,
+ "type": "object"
+ },
"jobs.EmailWorker": {
"properties": {
"devMode": {
@@ -66,11 +98,17 @@
"river.Workers": {
"properties": {
"emailWorker": {
- "$ref": "#/$defs/jobs.EmailWorker"
+ "$ref": "#/$defs/jobs.EmailWorker",
+ "description": "EmailWorker configuration for sending emails"
+ },
+ "databaseWorker": {
+ "$ref": "#/$defs/jobs.DatabaseWorker",
+ "description": "DatabaseWorker configuration for creating databases using openlane/dbx"
}
},
"additionalProperties": false,
- "type": "object"
+ "type": "object",
+ "description": "Workers that will be enabled on the server"
}
},
"properties": {
diff --git a/go.mod b/go.mod
index 4bba6cf..c31fb87 100644
--- a/go.mod
+++ b/go.mod
@@ -17,18 +17,25 @@ require (
github.com/rs/zerolog v1.33.0
github.com/spf13/cobra v1.8.1
github.com/stretchr/testify v1.9.0
+ github.com/theopenlane/dbx v0.1.2
github.com/theopenlane/newman v0.1.1
github.com/theopenlane/utils v0.2.1
)
require (
+ github.com/99designs/gqlgen v0.17.49 // indirect
+ github.com/Yamashou/gqlgenc v0.24.0 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
+ github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
+ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-viper/mapstructure/v2 v2.1.0 // indirect
+ github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/css v1.0.1 // indirect
+ github.com/gorilla/securecookie v1.1.2 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
@@ -40,19 +47,32 @@ require (
github.com/microcosm-cc/bluemonday v1.0.27 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
+ github.com/oklog/ulid/v2 v2.1.0 // indirect
+ github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+ github.com/redis/go-redis/v9 v9.6.1 // indirect
github.com/resend/resend-go/v2 v2.11.0 // indirect
github.com/riverqueue/river/riverdriver v0.11.4 // indirect
github.com/riverqueue/river/rivershared v0.11.4 // indirect
github.com/riverqueue/river/rivertype v0.11.4 // indirect
+ github.com/sosodev/duration v1.3.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
+ github.com/theopenlane/echox v0.2.0 // indirect
+ github.com/theopenlane/iam v0.1.6 // indirect
+ github.com/valyala/bytebufferpool v1.0.0 // indirect
+ github.com/valyala/fasttemplate v1.2.2 // indirect
+ github.com/vektah/gqlparser/v2 v2.5.16 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
go.uber.org/goleak v1.3.0 // indirect
+ go.uber.org/multierr v1.11.0 // indirect
+ go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/net v0.29.0 // indirect
+ golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.18.0 // indirect
+ golang.org/x/time v0.6.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index 6258503..2dee70d 100644
--- a/go.sum
+++ b/go.sum
@@ -1,22 +1,48 @@
+github.com/99designs/gqlgen v0.17.49 h1:b3hNGexHd33fBSAd4NDT/c3NCcQzcAVkknhN9ym36YQ=
+github.com/99designs/gqlgen v0.17.49/go.mod h1:tC8YFVZMed81x7UJ7ORUwXF4Kn6SXuucFqQBhN8+BU0=
+github.com/Yamashou/gqlgenc v0.24.0 h1:Aeufjb2zF0XxkeSTAVQ+DfiHL+ney/M2ovShZozBmHw=
+github.com/Yamashou/gqlgenc v0.24.0/go.mod h1:3QQD8ZoeEyVXuzqcMDsl8OfCCCTk+ulaxkvFFQDupIA=
+github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
+github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
+github.com/alicebob/miniredis/v2 v2.33.0 h1:uvTF0EDeu9RLnUEG27Db5I68ESoIxTiXbNUiji6lZrA=
+github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQevyi/DJpoj6mi0=
+github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
+github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
+github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
+github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
+github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
+github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-viper/mapstructure/v2 v2.1.0 h1:gHnMa2Y/pIxElCH2GlZZ1lZSsn6XMtufpGyP1XxdC/w=
github.com/go-viper/mapstructure/v2 v2.1.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
+github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
+github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI=
@@ -71,10 +97,16 @@ github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HK
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
+github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
+github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4=
+github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
github.com/resend/resend-go/v2 v2.11.0 h1:Ja5eXizUCbvyLgbiP8sFsJW/UN1b7d6IEUqi80IlgiU=
github.com/resend/resend-go/v2 v2.11.0/go.mod h1:ihnxc7wPpSgans8RV8d8dIF4hYWVsqMK5KxXAr9LIos=
github.com/riverqueue/river v0.11.4 h1:NMRsODhRgFztf080RMCjI377jldLXsx41E2r7+c0lPE=
@@ -97,6 +129,10 @@ github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
+github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
+github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4=
+github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -113,18 +149,38 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/theopenlane/dbx v0.1.2 h1:h2+okfFg3oh4ou8z99QTqCxBTDeaQ2Ek2AEsh3tw/KQ=
+github.com/theopenlane/dbx v0.1.2/go.mod h1:SRBOZyQaGQLM4OFxOEtJW5JbR0aTXFUjY8GYm2WOr64=
+github.com/theopenlane/echox v0.2.0 h1:s9DJJrsLOSPsXVfgmQxgXmSVtxzztBnSmcVX4ax7tIM=
+github.com/theopenlane/echox v0.2.0/go.mod h1:nfxwQpwvqYYI/pFHJKDs3/HLvjYKEGCih4XDgLSma64=
+github.com/theopenlane/iam v0.1.6 h1:ps6xLXHpnGy687uLPRZiD7034DRVqaWEfJLCJVMx95o=
+github.com/theopenlane/iam v0.1.6/go.mod h1:mOtYjuqUD7SX4EkwXFAYwf8+mwPDsRvTsLhAngqVIxM=
github.com/theopenlane/newman v0.1.1 h1:pxGPRcy8kXQplfv4Sp1N3XUkWmx/scZvp7oj+y2l8wI=
github.com/theopenlane/newman v0.1.1/go.mod h1:A37pInKEYsdvUmjQzTDv7x5T4KhMxoFW105DA3XvH4Y=
github.com/theopenlane/utils v0.2.1 h1:T6VfvOQDcAXBa1NFVL4QCsCbHvVQkp6Tl4hGJVd7TwQ=
github.com/theopenlane/utils v0.2.1/go.mod h1:ydEtwhmEvkVt3KKmNqiQiSY5b3rKH7U4umZ3QbFDsxU=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
+github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
+github.com/vektah/gqlparser/v2 v2.5.16 h1:1gcmLTvs3JLKXckwCwlUagVn/IlV2bwqle0vJ0vy5p8=
+github.com/vektah/gqlparser/v2 v2.5.16/go.mod h1:1lz1OeCqgQbQepsGxPVywrjdBHW2T08PUS3pJqepRww=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
+github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
+github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
+go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
+golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
+golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -134,6 +190,8 @@ golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
+golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
+golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
diff --git a/internal/river/config.go b/internal/river/config.go
index 5cd5251..6844fce 100644
--- a/internal/river/config.go
+++ b/internal/river/config.go
@@ -31,8 +31,13 @@ type Logger struct {
Pretty bool `koanf:"-" json:"-"`
}
+// Workers that will be enabled on the server
type Workers struct {
+ // EmailWorker configuration for sending emails
EmailWorker jobs.EmailWorker `koanf:"emailWorker" json:"emailWorker"`
+ // DatabaseWorker configuration for creating databases using openlane/dbx
+ DatabaseWorker jobs.DatabaseWorker `koanf:"databaseWorker" json:"databaseWorker"`
+
// add more workers here
}
diff --git a/internal/river/workers.go b/internal/river/workers.go
index 974a185..93ea50c 100644
--- a/internal/river/workers.go
+++ b/internal/river/workers.go
@@ -2,6 +2,7 @@ package river
import (
"github.com/riverqueue/river"
+
"github.com/theopenlane/riverboat/pkg/jobs"
)
@@ -17,6 +18,13 @@ func createWorkers(c Workers) (*river.Workers, error) {
return nil, err
}
+ if err := river.AddWorkerSafely(workers, &jobs.DatabaseWorker{
+ Config: c.DatabaseWorker.Config,
+ },
+ ); err != nil {
+ return nil, err
+ }
+
// add more workers here
return workers, nil
diff --git a/pkg/jobs/database.go b/pkg/jobs/database.go
new file mode 100644
index 0000000..041f0d9
--- /dev/null
+++ b/pkg/jobs/database.go
@@ -0,0 +1,74 @@
+package jobs
+
+import (
+ "context"
+
+ dbx "github.com/theopenlane/dbx/pkg/dbxclient"
+
+ "github.com/riverqueue/river"
+ "github.com/rs/zerolog/log"
+)
+
+// DatabaseArgs are the arguments for the database worker
+type DatabaseArgs struct {
+ // OrganizationID is the organization id to create the database for
+ OrganizationID string `json:"organization_id"`
+ // Location is the location to create the database in, e.g. AMER
+ Location string `json:"location"`
+}
+
+// Kind satisfies the river.Args interface for the database worker
+func (DatabaseArgs) Kind() string { return "dedicated_database" }
+
+// DatabaseWorker is a worker to create a dedicated database for an organization
+type DatabaseWorker struct {
+ river.WorkerDefaults[DatabaseArgs]
+
+ Config dbx.Config `koanf:"config" json:"config" jsonschema:"description=the database configuration"`
+}
+
+// validateDatabaseInput validates the input for the database worker
+func validateDatabaseInput(job *river.Job[DatabaseArgs]) error {
+ if job.Args.OrganizationID == "" {
+ return newMissingRequiredArg("organization_id", DatabaseArgs{}.Kind())
+ }
+
+ if job.Args.Location == "" {
+ return newMissingRequiredArg("location", DatabaseArgs{}.Kind())
+ }
+
+ return nil
+}
+
+// Work satisfies the river.Worker interface for the database worker
+// it creates a dedicated database using the dbx client for the organization
+func (w *DatabaseWorker) Work(ctx context.Context, job *river.Job[DatabaseArgs]) error {
+ // if its not enabled, return early
+ if !w.Config.Enabled {
+ return nil
+ }
+
+ if err := validateDatabaseInput(job); err != nil {
+ return err
+ }
+
+ input := dbx.CreateDatabaseInput{
+ OrganizationID: job.Args.OrganizationID,
+ Geo: &job.Args.Location,
+ }
+
+ log.Debug().
+ Str("org", input.OrganizationID).
+ Str("geo", *input.Geo).
+ Msg("creating database")
+
+ client := w.Config.NewDefaultClient()
+
+ if _, err := client.CreateDatabase(ctx, input); err != nil {
+ log.Error().Err(err).Msg("failed to create database")
+
+ return err
+ }
+
+ return nil
+}
diff --git a/pkg/jobs/database_test.go b/pkg/jobs/database_test.go
new file mode 100644
index 0000000..e703a81
--- /dev/null
+++ b/pkg/jobs/database_test.go
@@ -0,0 +1,56 @@
+package jobs_test
+
+import (
+ "context"
+ "testing"
+
+ "github.com/riverqueue/river"
+ "github.com/stretchr/testify/require"
+ "github.com/theopenlane/dbx/pkg/dbxclient"
+ "github.com/theopenlane/utils/ulids"
+
+ "github.com/theopenlane/riverboat/pkg/jobs"
+)
+
+// TODO :this currently does not test the actual database creation because the dbx client is not mocked
+// so the only thing we can test is that the worker returns early when disabled
+func (suite *TestSuite) TestDatabaseWorker() {
+ t := suite.T()
+
+ testCases := []struct {
+ name string
+ worker *jobs.DatabaseWorker
+ args jobs.DatabaseArgs
+ expectedError string
+ }{
+ {
+ name: "happy path, skip while disabled",
+ worker: &jobs.DatabaseWorker{
+ Config: dbxclient.Config{
+ Enabled: false,
+ },
+ },
+ args: jobs.DatabaseArgs{
+ Location: "AMER",
+ OrganizationID: ulids.New().String(),
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ ctx := context.Background()
+
+ err := (tc.worker).Work(ctx, &river.Job[jobs.DatabaseArgs]{Args: tc.args})
+
+ if tc.expectedError != "" {
+ require.Error(t, err)
+ require.Contains(t, err.Error(), tc.expectedError)
+
+ return
+ }
+
+ require.NoError(t, err)
+ })
+ }
+}
diff --git a/pkg/jobs/email.go b/pkg/jobs/email.go
index 22d85b1..887a027 100644
--- a/pkg/jobs/email.go
+++ b/pkg/jobs/email.go
@@ -42,11 +42,11 @@ type EmailConfig struct {
// validateEmailConfig validates the email configuration settings
func (w *EmailWorker) validateEmailConfig() error {
if w.DevMode && w.TestDir == "" {
- return ErrMissingTestDir
+ return newMissingRequiredArg("test directory", EmailArgs{}.Kind())
}
if !w.DevMode && w.Token == "" {
- return ErrMissingToken
+ return newMissingRequiredArg("token", EmailArgs{}.Kind())
}
return nil
diff --git a/pkg/jobs/email_test.go b/pkg/jobs/email_test.go
index 6c1cf1e..f7ee014 100644
--- a/pkg/jobs/email_test.go
+++ b/pkg/jobs/email_test.go
@@ -49,7 +49,7 @@ func (suite *TestSuite) TestEmailWorker() {
},
},
msg: emailWithoutFrom,
- expectedError: jobs.ErrMissingTestDir.Error(),
+ expectedError: "test directory is required for the email job",
},
{
name: "missing from email",
@@ -81,7 +81,7 @@ func (suite *TestSuite) TestEmailWorker() {
},
},
msg: emailWithoutFrom,
- expectedError: jobs.ErrMissingToken.Error(),
+ expectedError: "token is required for the email job",
},
}
diff --git a/pkg/jobs/errors.go b/pkg/jobs/errors.go
index fd5b2f0..3c7593d 100644
--- a/pkg/jobs/errors.go
+++ b/pkg/jobs/errors.go
@@ -1,6 +1,9 @@
package jobs
-import "errors"
+import (
+ "errors"
+ "fmt"
+)
var (
// ErrMissingTestDir is the error for missing test directory
@@ -8,3 +11,24 @@ var (
// ErrMissingToken is the error for missing token
ErrMissingToken = errors.New("missing resend api token, set token or use dev mode")
)
+
+// MissingRequiredArg is returned when a required argument was not provided to a job
+type MissingRequiredArgError struct {
+ // Arg is the required argument that was not provided
+ Arg string `json:"arg"`
+ // Job is the job that requires the argument
+ Job string `json:"job"`
+}
+
+// Error returns the MissingRequiredArgError in string format
+func (e *MissingRequiredArgError) Error() string {
+ return fmt.Sprintf("%s is required for the %s job", e.Arg, e.Job)
+}
+
+// newMissingRequiredArg returns an error for a missing required argument
+func newMissingRequiredArg(field, job string) *MissingRequiredArgError {
+ return &MissingRequiredArgError{
+ Arg: field,
+ Job: job,
+ }
+}
diff --git a/test/database/main.go b/test/database/main.go
new file mode 100644
index 0000000..603a320
--- /dev/null
+++ b/test/database/main.go
@@ -0,0 +1,27 @@
+package main
+
+import (
+ "context"
+
+ "github.com/rs/zerolog/log"
+
+ "github.com/theopenlane/riverboat/test/common"
+
+ "github.com/theopenlane/riverboat/pkg/jobs"
+)
+
+// the main function here will insert an database job into the river
+// this will be picked up by the river server and processed
+func main() {
+ client := common.NewInsertOnlyRiverClient()
+
+ _, err := client.Insert(context.Background(), jobs.DatabaseArgs{
+ OrganizationID: "100100100010001000",
+ Location: "AMER",
+ }, nil)
+ if err != nil {
+ log.Fatal().Err(err).Msg("error inserting database job")
+ }
+
+ log.Info().Msg("database job successfully inserted")
+}