diff --git a/db/dev/v1_0.sql b/db/dev/v1_0.sql
index 9bcee9f..51163d5 100644
--- a/db/dev/v1_0.sql
+++ b/db/dev/v1_0.sql
@@ -22,29 +22,36 @@ CREATE TABLE IF NOT EXISTS "holiday"
references country(iso_name) on delete cascade on update cascade
);
-INSERT INTO "country" (id, iso_name, name)
-VALUES
- ('096ca6c4-5c04-47a4-0063-4b4cc6f6b671', 'HR', 'Croatia'),
- ('096ca6c4-5c04-47a4-0063-4b4cc6f6b672', 'US', 'USA'),
- ('096ca6c4-5c04-47a4-0063-4b4cc6f6b673', 'FR', 'France'),
- ('096ca6c4-5c04-47a4-0063-4b4cc6f6b674', 'GB', 'Great Britain');
+CREATE TABLE IF NOT EXISTS "webhook"
+(
+ id uuid,
+ created timestamp NOT NULL,
+ url varchar(256) NOT NULL,
+ country char(2) NOT NULL,
+ retry_count int NOT NULL,
+ on_created bool NOT NULL,
+ on_edited bool NOT NULL,
+ on_deleted bool NOT NULL,
-INSERT INTO "holiday" (id, country, date, name, description, is_state, is_religious)
-VALUES
- ('096ca6c4-5c04-47a4-b363-4b4cc6f6b671', 'HR', '2023-01-01', 'Nova godina', '', true, false),
- ('096ca6c4-5c04-47a4-b363-4b4cc6f6b672', 'HR', '2023-01-06', 'Sveta tri kralja', '', true, true),
- ('096ca6c4-5c04-47a4-b363-4b4cc6f6b673', 'HR', '2023-04-09', 'Uskrs', '', true, true),
- ('096ca6c4-5c04-47a4-b363-4b4cc6f6b674', 'HR', '2023-04-10', 'Uskrsni ponedjeljak', '', true, true),
- ('096ca6c4-5c04-47a4-b363-4b4cc6f6b675', 'HR', '2023-05-01', 'Praznik rada', '', true, false),
- ('096ca6c4-5c04-47a4-b363-4b4cc6f6b676', 'HR', '2023-05-30', 'Dan državnosti', '', true, false),
- ('096ca6c4-5c04-47a4-b363-4b4cc6f6b677', 'HR', '2023-06-08', 'Tijelovo', '', true, true),
- ('096ca6c4-5c04-47a4-b363-4b4cc6f6b678', 'HR', '2023-06-22', 'Dan antifašističke borbe', '', true, false),
- ('096ca6c4-5c04-47a4-b363-4b4cc6f6b679', 'HR', '2023-08-05', 'Dan pobjede i domovinske zahvalnost', '', true, false),
- ('096ca6c4-5c04-47a4-b363-4b4cc6f6b680', 'HR', '2023-08-15', 'Velika gospa', '', true, true),
- ('096ca6c4-5c04-47a4-b363-4b4cc6f6b681', 'HR', '2023-11-01', 'Dan svih svetih', '', true, true),
- ('096ca6c4-5c04-47a4-b363-4b4cc6f6b682', 'HR', '2023-11-18', 'Dan sjećanja na žrtve Domovinskog rata', '', true, false),
- ('096ca6c4-5c04-47a4-b363-4b4cc6f6b683', 'HR', '2023-12-25', 'Božić', '', true, true),
- ('096ca6c4-5c04-47a4-b363-4b4cc6f6b684', 'HR', '2023-12-26', 'Štefanje', '', true, true);
+ primary key (id),
+ constraint fk_country_id foreign key (country)
+ references country(iso_name) on delete cascade on update cascade
+);
+CREATE TABLE IF NOT EXISTS "job"
+(
+ id uuid,
+ webhook_id uuid NOT NULL,
+ created timestamp NOT NULL,
+ success bool NOT NULL,
+ success_time timestamp,
+ retry_count int NOT NULL,
+
+ content varchar(1024) NOT NULL,
+
+ primary key (id),
+ constraint fk_webhook_id foreign key (webhook_id)
+ references webhook(id) on delete cascade on update cascade
+);
\ No newline at end of file
diff --git a/db/dev/v1_1.sql b/db/dev/v1_1.sql
new file mode 100644
index 0000000..9494a59
--- /dev/null
+++ b/db/dev/v1_1.sql
@@ -0,0 +1,33 @@
+
+INSERT INTO "country" (id, iso_name, name)
+VALUES
+ ('096ca6c4-5c04-47a4-0063-4b4cc6f6b671', 'HR', 'Croatia'),
+ ('096ca6c4-5c04-47a4-0063-4b4cc6f6b672', 'US', 'USA'),
+ ('096ca6c4-5c04-47a4-0063-4b4cc6f6b673', 'FR', 'France'),
+ ('096ca6c4-5c04-47a4-0063-4b4cc6f6b674', 'GB', 'Great Britain');
+
+INSERT INTO "holiday" (id, country, date, name, description, is_state, is_religious)
+VALUES
+ ('096ca6c4-5c04-47a4-b363-4b4cc6f6b671', 'HR', '2023-01-01', 'Nova godina', '', true, false),
+ ('096ca6c4-5c04-47a4-b363-4b4cc6f6b672', 'HR', '2023-01-06', 'Sveta tri kralja', '', true, true),
+ ('096ca6c4-5c04-47a4-b363-4b4cc6f6b673', 'HR', '2023-04-09', 'Uskrs', '', true, true),
+ ('096ca6c4-5c04-47a4-b363-4b4cc6f6b674', 'HR', '2023-04-10', 'Uskrsni ponedjeljak', '', true, true),
+ ('096ca6c4-5c04-47a4-b363-4b4cc6f6b675', 'HR', '2023-05-01', 'Praznik rada', '', true, false),
+ ('096ca6c4-5c04-47a4-b363-4b4cc6f6b676', 'HR', '2023-05-30', 'Dan državnosti', '', true, false),
+ ('096ca6c4-5c04-47a4-b363-4b4cc6f6b677', 'HR', '2023-06-08', 'Tijelovo', '', true, true),
+ ('096ca6c4-5c04-47a4-b363-4b4cc6f6b678', 'HR', '2023-06-22', 'Dan antifašističke borbe', '', true, false),
+ ('096ca6c4-5c04-47a4-b363-4b4cc6f6b679', 'HR', '2023-08-05', 'Dan pobjede i domovinske zahvalnost', '', true, false),
+ ('096ca6c4-5c04-47a4-b363-4b4cc6f6b680', 'HR', '2023-08-15', 'Velika gospa', '', true, true),
+ ('096ca6c4-5c04-47a4-b363-4b4cc6f6b681', 'HR', '2023-11-01', 'Dan svih svetih', '', true, true),
+ ('096ca6c4-5c04-47a4-b363-4b4cc6f6b682', 'HR', '2023-11-18', 'Dan sjećanja na žrtve Domovinskog rata', '', true, false),
+ ('096ca6c4-5c04-47a4-b363-4b4cc6f6b683', 'HR', '2023-12-25', 'Božić', '', true, true),
+ ('096ca6c4-5c04-47a4-b363-4b4cc6f6b684', 'HR', '2023-12-26', 'Štefanje', '', true, true);
+
+INSERT INTO "webhook" (id, created, url, country, retry_count, on_created, on_edited, on_deleted)
+VALUES
+ ('b0b0a6c4-5c04-47a4-b363-4b4cc6f6b671', current_timestamp, 'https://hr.bbr-dev.info', 'HR', 10, true, true, true),
+ ('b0b0a6c4-5c04-47a4-b363-4b4cc6f6b672', current_timestamp, 'https://hr2.bbr-dev.info', 'HR', 10, true, false, false);
+
+INSERT INTO "job" (id, webhook_id, created, success, success_time, retry_count, content)
+VALUES
+ ('c0c0a6c4-5c04-47a4-b363-4b4cc6f6b671', 'b0b0a6c4-5c04-47a4-b363-4b4cc6f6b671', current_timestamp, false, null, 8, '{"type": "created", "holiday": {"id": "096ca6c4-5c04-47a4-b363-4b4cc6f6b671", "name": "Nova godina", "date": "2023-01-01"}}');
\ No newline at end of file
diff --git a/db/prod/v1_1.sql b/db/prod/v1_1.sql
new file mode 100644
index 0000000..0798199
--- /dev/null
+++ b/db/prod/v1_1.sql
@@ -0,0 +1,34 @@
+
+CREATE TABLE IF NOT EXISTS "webhook"
+(
+ id uuid,
+ created timestamp NOT NULL,
+ url varchar(256) NOT NULL,
+ country char(2) NOT NULL,
+ retry_count int NOT NULL,
+ on_created bool NOT NULL,
+ on_edited bool NOT NULL,
+ on_deleted bool NOT NULL,
+
+ primary key (id),
+ constraint fk_country_id foreign key (country)
+ references country(iso_name) on delete cascade on update cascade
+);
+
+CREATE TABLE IF NOT EXISTS "job"
+(
+ id uuid,
+ webhook_id uuid NOT NULL,
+
+ created timestamp NOT NULL,
+ success bool NOT NULL,
+ success_time timestamp NOT NULL,
+
+ retry_count int NOT NULL,
+
+ content varchar(1024) NOT NULL,
+
+ primary key (id),
+ constraint fk_webhook_id foreign key (webhook_id)
+ references webhook(id) on delete cascade on update cascade
+);
\ No newline at end of file
diff --git a/main.go b/main.go
index 1f64b86..5b62d5f 100644
--- a/main.go
+++ b/main.go
@@ -8,6 +8,7 @@ import (
_ "github.com/lib/pq"
"holiday-api/holiday"
"holiday-api/migration"
+ "holiday-api/webhook"
"html/template"
"log"
"net/http"
@@ -56,10 +57,15 @@ func main() {
holidayService := holiday.HolidayService{DB: client}
countryService := holiday.CountryService{DB: client}
yearService := holiday.YearService{DB: client}
+ webhookService := webhook.WebhookService{
+ DB: client,
+ Events: make(chan webhook.Event, 10),
+ Authorization: "TODO", // TODO add authorization fetching
+ }
g.GET("/api/v1/holidays", getHolidays(holidayService))
- setupAdminDashboard(g.Group("/admin"), holidayService, countryService, yearService)
+ setupAdminDashboard(g.Group("/admin"), holidayService, countryService, yearService, webhookService)
g.GET("/", func(c *gin.Context) {
year := time.Now().Year()
@@ -121,18 +127,23 @@ func loadTemplates(g *gin.Engine) {
"templates/admin_dashboard.gohtml",
"templates/countries.gohtml",
+ "templates/webhooks.gohtml",
"templates/dialogs/add-holiday.gohtml",
"templates/dialogs/edit-holiday.gohtml",
"templates/dialogs/delete-holiday.gohtml",
"templates/dialogs/check-is-a-holiday.gohtml",
+ "templates/dialogs/add-webhook.gohtml",
+ "templates/dialogs/edit-webhook.gohtml",
+ "templates/dialogs/delete-webhook.gohtml",
+
"templates/dialogs/edit-country.gohtml",
"templates/dialogs/delete-country.gohtml",
)
}
-func setupAdminDashboard(adminDashboard *gin.RouterGroup, service holiday.HolidayService, countryService holiday.CountryService, yearService holiday.YearService) {
+func setupAdminDashboard(adminDashboard *gin.RouterGroup, service holiday.HolidayService, countryService holiday.CountryService, yearService holiday.YearService, webhookService webhook.WebhookService) {
adminDashboard.Use(gin.BasicAuth(loadAuth()))
adminDashboard.GET("/", func(c *gin.Context) {
@@ -252,10 +263,71 @@ func setupAdminDashboard(adminDashboard *gin.RouterGroup, service holiday.Holida
c.Redirect(http.StatusSeeOther, "/admin/countries")
})
+ adminDashboard.GET("/webhooks", func(c *gin.Context) {
+ webhooks, _ := webhookService.Find()
+ c.HTML(http.StatusOK, "webhooks.gohtml", gin.H{"Webhooks": webhooks})
+ })
+ adminDashboard.POST("/webhooks", func(c *gin.Context) {
+ request := struct {
+ Id *string `form:"id"`
+ Url string `form:"url" binding:"required,min=1"`
+ RetryCount int `form:"retry_count" binding:"required,min=1,max=20"`
+ Country string `form:"country" binding:"len=2"`
+ OnCreated bool `form:"on_created"`
+ OnEdited bool `form:"on_edited"`
+ OnDeleted bool `form:"on_deleted"`
+ }{}
+ if err := c.ShouldBind(&request); err != nil {
+ c.AbortWithError(http.StatusBadRequest, err)
+ return
+ }
+
+ hook := webhook.Webhook{
+ Url: request.Url,
+ Country: request.Country,
+ RetryCount: request.RetryCount,
+ OnCreated: request.OnCreated,
+ OnEdited: request.OnEdited,
+ OnDeleted: request.OnDeleted,
+ }
+
+ var err error
+ if request.Id != nil {
+ hook.Id = uuid.MustParse(*request.Id)
+ hook, err = webhookService.Update(hook)
+ } else {
+ hook.Id = uuid.Must(uuid.NewRandom())
+ hook.Created = time.Now()
+ hook, err = webhookService.Create(hook)
+ }
+ if err != nil {
+ c.AbortWithError(http.StatusInternalServerError, err)
+ } else {
+ c.Redirect(http.StatusSeeOther, "/admin/webhooks")
+ }
+ })
+ adminDashboard.POST("/webhooks/:id/delete", func(c *gin.Context) {
+ id := uuid.MustParse(c.Param("id"))
+ _, err := webhookService.FindById(id)
+ if err != nil {
+ c.AbortWithError(http.StatusNotFound, err)
+ return
+ }
+ if err := webhookService.Delete(id); err != nil {
+ c.AbortWithError(http.StatusInternalServerError, err)
+ return
+ }
+ c.Redirect(http.StatusSeeOther, "/admin/webhooks")
+ })
+
adminDashboard.GET("/dialogs/add-holiday", func(c *gin.Context) {
countries, _ := countryService.Find()
c.HTML(http.StatusOK, "add-holiday.gohtml", gin.H{"Countries": countries})
})
+ adminDashboard.GET("/dialogs/add-webhook", func(c *gin.Context) {
+ countries, _ := countryService.Find()
+ c.HTML(http.StatusOK, "add-webhook.gohtml", gin.H{"Countries": countries})
+ })
adminDashboard.GET("/dialogs/edit-holiday", func(c *gin.Context) {
id := uuid.MustParse(c.Query("id"))
hol, err := service.FindById(id)
@@ -266,6 +338,16 @@ func setupAdminDashboard(adminDashboard *gin.RouterGroup, service holiday.Holida
countries, _ := countryService.Find()
c.HTML(http.StatusOK, "edit-holiday.gohtml", gin.H{"Countries": countries, "Holiday": hol})
})
+ adminDashboard.GET("/dialogs/edit-webhook", func(c *gin.Context) {
+ id := uuid.MustParse(c.Query("id"))
+ hook, err := webhookService.FindById(id)
+ if err != nil {
+ c.AbortWithError(http.StatusNotFound, err)
+ return
+ }
+ countries, _ := countryService.Find()
+ c.HTML(http.StatusOK, "edit-webhook.gohtml", gin.H{"Countries": countries, "Webhook": hook})
+ })
adminDashboard.GET("/dialogs/edit-country", func(c *gin.Context) {
id := uuid.MustParse(c.Query("id"))
country, err := countryService.FindById(id)
@@ -293,6 +375,15 @@ func setupAdminDashboard(adminDashboard *gin.RouterGroup, service holiday.Holida
}
c.HTML(http.StatusOK, "delete-country.gohtml", gin.H{"Country": country})
})
+ adminDashboard.GET("/dialogs/delete-webhook", func(c *gin.Context) {
+ id := uuid.MustParse(c.Query("id"))
+ hook, err := webhookService.FindById(id)
+ if err != nil {
+ c.AbortWithError(http.StatusNotFound, err)
+ return
+ }
+ c.HTML(http.StatusOK, "delete-webhook.gohtml", gin.H{"Webhook": hook})
+ })
}
func loadAuth() map[string]string {
diff --git a/migration/migration.go b/migration/migration.go
index 64f48de..5ac77f0 100644
--- a/migration/migration.go
+++ b/migration/migration.go
@@ -97,8 +97,7 @@ func executeMigration(db *sqlx.DB, name string, script string) error {
var err error = nil
if _, e := tx.Exec(script); e != nil {
err = e
- }
- if _, e := tx.Exec(createMigration, name, hash(script)); e != nil {
+ } else if _, e := tx.Exec(createMigration, name, hash(script)); e != nil {
err = e
}
if err != nil {
diff --git a/templates/admin_dashboard.gohtml b/templates/admin_dashboard.gohtml
index 06f16cb..d3692c4 100644
--- a/templates/admin_dashboard.gohtml
+++ b/templates/admin_dashboard.gohtml
@@ -18,6 +18,7 @@
diff --git a/templates/countries.gohtml b/templates/countries.gohtml
index 6c2fe13..a82f4ef 100644
--- a/templates/countries.gohtml
+++ b/templates/countries.gohtml
@@ -18,6 +18,7 @@
diff --git a/templates/dialogs/add-webhook.gohtml b/templates/dialogs/add-webhook.gohtml
new file mode 100644
index 0000000..da4d5fb
--- /dev/null
+++ b/templates/dialogs/add-webhook.gohtml
@@ -0,0 +1,35 @@
+
\ No newline at end of file
diff --git a/templates/dialogs/delete-webhook.gohtml b/templates/dialogs/delete-webhook.gohtml
new file mode 100644
index 0000000..ac9eed0
--- /dev/null
+++ b/templates/dialogs/delete-webhook.gohtml
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/templates/dialogs/edit-webhook.gohtml b/templates/dialogs/edit-webhook.gohtml
new file mode 100644
index 0000000..0083d5e
--- /dev/null
+++ b/templates/dialogs/edit-webhook.gohtml
@@ -0,0 +1,36 @@
+
\ No newline at end of file
diff --git a/templates/webhooks.gohtml b/templates/webhooks.gohtml
new file mode 100644
index 0000000..93abac1
--- /dev/null
+++ b/templates/webhooks.gohtml
@@ -0,0 +1,63 @@
+
+
+
+
+
+ Holiday-api | Admin dashboard
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Country |
+ Url |
+ Retry count |
+ On create |
+ On update |
+ On delete |
+ |
+
+
+
+
+ {{range $entry := .Webhooks}}
+
+ {{$entry.Country}} |
+ {{$entry.Url}} |
+ {{$entry.RetryCount}} |
+ |
+ |
+ |
+
+
+
+ |
+
+ {{end}}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/webhook/model.go b/webhook/model.go
new file mode 100644
index 0000000..23ba5e0
--- /dev/null
+++ b/webhook/model.go
@@ -0,0 +1,73 @@
+package webhook
+
+import (
+ "encoding/json"
+ "github.com/google/uuid"
+ "time"
+)
+
+type EventType string
+
+const (
+ TypeCreated EventType = "created"
+ TypeEdited EventType = "edited"
+ TypeDeleted EventType = "deleted"
+)
+
+type Event struct {
+ Type EventType `json:"type"`
+ Holiday struct {
+ Id uuid.UUID `json:"id"`
+ Name string `json:"name"`
+ Country string `json:"country"`
+ Description string `json:"description"`
+ Date time.Time `json:"date"`
+ IsStateHoliday bool `json:"is_state_holiday"`
+ IsReligiousHoliday bool `json:"is_religious_holiday"`
+ } `json:"holiday"`
+}
+
+func (e Event) Json() string {
+ content, err := json.Marshal(e)
+ if err != nil {
+ panic(err)
+ }
+ return string(content)
+}
+
+type Webhook struct {
+ Id uuid.UUID `db:"id"`
+ Created time.Time `db:"created"`
+ Url string `db:"url"`
+ Country string `db:"country"`
+ RetryCount int `db:"retry_count"`
+ OnCreated bool `db:"on_created"`
+ OnEdited bool `db:"on_edited"`
+ OnDeleted bool `db:"on_deleted"`
+}
+
+func (w Webhook) ShouldEmit(e Event) bool {
+ if w.Country == e.Holiday.Country {
+ if e.Type == TypeCreated && w.OnCreated {
+ return true
+ } else if e.Type == TypeEdited && w.OnEdited {
+ return true
+ } else if e.Type == TypeDeleted && w.OnDeleted {
+ return true
+ }
+ }
+ return false
+}
+
+type Job struct {
+ Id uuid.UUID `db:"id"`
+ WebhookId uuid.UUID `db:"webhook_id"`
+
+ Created time.Time `db:"created"`
+ Success bool `db:"success"`
+ SuccessTime *time.Time `db:"success_time"`
+
+ RetryCount int `db:"retry_count"`
+
+ Content string `db:"content"`
+}
diff --git a/webhook/webhook_service.go b/webhook/webhook_service.go
new file mode 100644
index 0000000..254ad7e
--- /dev/null
+++ b/webhook/webhook_service.go
@@ -0,0 +1,141 @@
+package webhook
+
+import (
+ "github.com/google/uuid"
+ "github.com/jmoiron/sqlx"
+ "log"
+ "net/http"
+ "strings"
+ "time"
+)
+
+type WebhookService struct {
+ DB *sqlx.DB
+ Events <-chan Event
+ Authorization string
+}
+
+func (w *WebhookService) Listen() {
+ for event := range w.Events {
+ webhooks, _ := w.Find()
+ var usedWebhooks []Webhook
+ for _, webhook := range webhooks {
+ usedWebhooks = append(usedWebhooks, webhook)
+ }
+ w.CreateJobs(usedWebhooks, event)
+ }
+}
+
+func (w *WebhookService) Run() {
+ for {
+ jobs, err := w.findRunningJobs()
+ if err != nil {
+ log.Printf("failed fetching running jobs: %v", err)
+ }
+ for _, job := range jobs {
+ job := w.ExecuteJob(job)
+ if err := w.UpdateJob(job); err != nil {
+ log.Printf("failed updating job %v: %v", job.Id, err)
+ }
+ }
+ time.Sleep(2 * time.Hour)
+ }
+}
+
+func (w *WebhookService) Find() ([]Webhook, error) {
+ var webhooks []Webhook
+ return webhooks, w.DB.Select(&webhooks, `SELECT * FROM "webhook"`)
+}
+
+func (w *WebhookService) FindAllJobs() ([]Job, error) {
+ var jobs []Job
+ return jobs, w.DB.Select(&jobs, `SELECT * FROM "job"`)
+}
+
+func (w *WebhookService) FindJobs(webhookId uuid.UUID) ([]Job, error) {
+ var jobs []Job
+ return jobs, w.DB.Select(&jobs, `SELECT * FROM "job" WHERE id = $1`, webhookId)
+}
+
+func (w *WebhookService) findRunningJobs() ([]Job, error) {
+ var jobs []Job
+ return jobs, w.DB.Select(&jobs, `SELECT * FROM "job" WHERE retry_count > 0 ORDER BY created asc`)
+}
+
+func (w *WebhookService) Create(webhook Webhook) (Webhook, error) {
+ _, err := w.DB.Exec("INSERT INTO webhook (id, created, url, country, retry_count, on_created, on_edited, on_deleted) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
+ &webhook.Id, &webhook.Created, &webhook.Url, &webhook.Country, &webhook.RetryCount, &webhook.OnCreated, &webhook.OnEdited, &webhook.OnDeleted,
+ )
+ return webhook, err
+}
+
+func (w *WebhookService) Update(webhook Webhook) (Webhook, error) {
+ _, err := w.DB.Exec("UPDATE webhook SET url = $2, country = $3, retry_count = $4, on_created = $5, on_edited = $6, on_deleted = $7 WHERE id = $1",
+ &webhook.Id, &webhook.Url, &webhook.Country, &webhook.RetryCount, &webhook.OnCreated, &webhook.OnEdited, &webhook.OnDeleted,
+ )
+ return webhook, err
+}
+
+func (w *WebhookService) CreateJobs(webhooks []Webhook, event Event) {
+ json := event.Json()
+
+ tx, _ := w.DB.Begin()
+ for _, webhook := range webhooks {
+ job := Job{
+ Id: uuid.Must(uuid.NewRandom()),
+ WebhookId: webhook.Id,
+ Created: time.Now(),
+ Success: false,
+ SuccessTime: nil,
+ RetryCount: webhook.RetryCount,
+ Content: json,
+ }
+ _, err := tx.Exec("INSERT INTO job (id, webhook_id, created, success, success_time, retry_count, content) VALUES ($1, $2, $3, $4, $5, $6, $7)",
+ &job.Id, &job.WebhookId, &job.Created, &job.Success, &job.SuccessTime, &job.RetryCount, &job.Content,
+ )
+ if err != nil {
+ tx.Rollback()
+ return
+ }
+ }
+ tx.Commit()
+}
+
+func (w *WebhookService) ExecuteJob(job Job) Job {
+ webhook, err := w.FindById(job.WebhookId)
+ if err != nil {
+ panic(err)
+ }
+ log.Printf("executing job [%d:%d] %v: POST %s", webhook.RetryCount-job.RetryCount, webhook.RetryCount, job.Id, webhook.Url)
+ request, err := http.NewRequest("POST", webhook.Url, strings.NewReader(job.Content))
+ request.Header.Add("authorization", "Api "+w.Authorization)
+
+ response, err := http.DefaultClient.Do(request)
+ if err != nil || response.StatusCode != http.StatusOK {
+ job.RetryCount--
+ } else {
+ job.Success = true
+ currentTime := time.Now()
+ job.SuccessTime = ¤tTime
+ }
+ return job
+}
+
+func (w *WebhookService) FindById(webhookId uuid.UUID) (Webhook, error) {
+ var webhook Webhook
+ return webhook, w.DB.Get(&webhook, `SELECT * FROM "webhook" WHERE id = $1`, webhookId)
+}
+
+func (w *WebhookService) UpdateJob(job Job) error {
+ _, err := w.DB.Exec(`UPDATE job SET success = $2, success_time = $3, retry_count = $4 WHERE id = $1`,
+ &job.Id, &job.Success, &job.SuccessTime, &job.RetryCount,
+ )
+ return err
+}
+
+func (w *WebhookService) Delete(webhookId uuid.UUID) error {
+ _, err := w.DB.Exec(`DELETE FROM "webhook" WHERE id = $1`,
+ &webhookId,
+ )
+ return err
+}