Skip to content

Commit

Permalink
Add edit webhook functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
eduardolat committed Sep 2, 2024
1 parent 718c7bc commit 0453aa9
Show file tree
Hide file tree
Showing 6 changed files with 207 additions and 9 deletions.
1 change: 1 addition & 0 deletions internal/service/webhooks/update_webhook.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ UPDATE webhooks
SET
name = COALESCE(sqlc.narg('name'), name),
is_active = COALESCE(sqlc.narg('is_active'), is_active),
event_type = COALESCE(sqlc.narg('event_type'), event_type),
target_ids = COALESCE(sqlc.narg('target_ids'), target_ids),
url = COALESCE(sqlc.narg('url'), url),
method = COALESCE(sqlc.narg('method'), method),
Expand Down
42 changes: 39 additions & 3 deletions internal/view/web/dashboard/webhooks/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,23 +137,53 @@ func createAndUpdateWebhookForm(
))
}

pickedTargetIds := ""
if len(pickedWebhook.TargetIds) > 0 {
for _, tid := range pickedWebhook.TargetIds {
pickedTargetIds += fmt.Sprintf("'%s',", tid.String())
}
}

return html.Div(
html.Class("space-y-2"),

alpine.XData(`{
eventType: "",
targetIds: [],
eventType: "`+pickedWebhook.EventType+`",
targetIds: [`+pickedTargetIds+`],
isEventType(eventType) {
return this.eventType === eventType
},
init () {
autoGrowHeadersTextarea() {
textareaAutoGrow($refs.headersTextarea)
},
autoGrowBodyTextarea() {
textareaAutoGrow($refs.bodyTextarea)
},
formatHeadersTextarea() {
const el = $refs.headersTextarea
el.value = formatJson(el.value)
this.autoGrowHeadersTextarea()
},
formatBodyTextarea() {
const el = $refs.bodyTextarea
el.value = formatJson(el.value)
this.autoGrowBodyTextarea()
},
init() {
$watch('eventType', (value, oldValue) => {
if (value !== oldValue) {
this.targetIds = []
}
})
this.formatHeadersTextarea()
this.formatBodyTextarea()
}
}`),

Expand Down Expand Up @@ -296,6 +326,9 @@ func createAndUpdateWebhookForm(
Placeholder: `{ "Authorization": "Bearer my-token" }`,
HelpText: `By default it will send a { "Content-Type": "application/json" } header.`,
Children: []gomponents.Node{
alpine.XRef("headersTextarea"),
alpine.XOn("click.outside", "formatHeadersTextarea()"),
alpine.XOn("input", "autoGrowHeadersTextarea()"),
gomponents.If(
shouldPrefill, gomponents.Text(pickedWebhook.Headers.String),
),
Expand All @@ -308,6 +341,9 @@ func createAndUpdateWebhookForm(
Placeholder: `{ "key": "value" }`,
HelpText: `By default it will send an empty json object {}.`,
Children: []gomponents.Node{
alpine.XRef("bodyTextarea"),
alpine.XOn("click.outside", "formatBodyTextarea()"),
alpine.XOn("input", "autoGrowBodyTextarea()"),
gomponents.If(
shouldPrefill, gomponents.Text(pickedWebhook.Body.String),
),
Expand Down
4 changes: 2 additions & 2 deletions internal/view/web/dashboard/webhooks/create_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func createWebhookForm(
backups []dbgen.Backup,
) gomponents.Node {
return html.Form(
htmx.HxPost("/dashboard/webhooks"),
htmx.HxPost("/dashboard/webhooks/create"),
htmx.HxDisabledELT("find button[type='submit']"),
html.Class("space-y-2"),

Expand All @@ -111,7 +111,7 @@ func createWebhookButton() gomponents.Node {
Title: "Create webhook",
Content: []gomponents.Node{
html.Div(
htmx.HxGet("/dashboard/webhooks/create-form"),
htmx.HxGet("/dashboard/webhooks/create"),
htmx.HxSwap("outerHTML"),
htmx.HxTrigger("intersect once"),
html.Class("p-10 flex justify-center"),
Expand Down
153 changes: 153 additions & 0 deletions internal/view/web/dashboard/webhooks/edit_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package webhooks

import (
"database/sql"
"net/http"

lucide "github.com/eduardolat/gomponents-lucide"
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
"github.com/eduardolat/pgbackweb/internal/util/echoutil"
"github.com/eduardolat/pgbackweb/internal/validate"
"github.com/eduardolat/pgbackweb/internal/view/web/component"
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
"github.com/maragudk/gomponents"
"github.com/maragudk/gomponents/html"
)

type editWebhookDTO struct {
Name string `form:"name" validate:"required"`
EventType string `form:"event_type" validate:"required"`
TargetIds []uuid.UUID `form:"target_ids" validate:"required,gt=0"`
IsActive string `form:"is_active" validate:"required,oneof=true false"`
Url string `form:"url" validate:"required,url"`
Method string `form:"method" validate:"required,oneof=GET POST"`
Headers string `form:"headers" validate:"omitempty,json"`
Body string `form:"body" validate:"omitempty,json"`
}

func (h *handlers) editWebhookHandler(c echo.Context) error {
ctx := c.Request().Context()
webhookID, err := uuid.Parse(c.Param("webhookID"))
if err != nil {
return htmx.RespondToastError(c, err.Error())
}

var formData editWebhookDTO
if err := c.Bind(&formData); err != nil {
return htmx.RespondToastError(c, err.Error())
}
if err := validate.Struct(&formData); err != nil {
return htmx.RespondToastError(c, err.Error())
}

_, err = h.servs.WebhooksService.UpdateWebhook(
ctx, dbgen.WebhooksServiceUpdateWebhookParams{
WebhookID: webhookID,
Name: sql.NullString{String: formData.Name, Valid: true},
EventType: sql.NullString{String: formData.EventType, Valid: true},
TargetIds: formData.TargetIds,
IsActive: sql.NullBool{Bool: formData.IsActive == "true", Valid: true},
Url: sql.NullString{String: formData.Url, Valid: true},
Method: sql.NullString{String: formData.Method, Valid: true},
Headers: sql.NullString{String: formData.Headers, Valid: true},
Body: sql.NullString{String: formData.Body, Valid: true},
},
)
if err != nil {
return htmx.RespondToastError(c, err.Error())
}

return htmx.RespondAlertWithRefresh(c, "Webhook updated")
}

func (h *handlers) editWebhookFormHandler(c echo.Context) error {
ctx := c.Request().Context()
webhookID, err := uuid.Parse(c.Param("webhookID"))
if err != nil {
return htmx.RespondToastError(c, err.Error())
}

webhook, err := h.servs.WebhooksService.GetWebhook(ctx, webhookID)
if err != nil {
return htmx.RespondToastError(c, err.Error())
}

databases, err := h.servs.DatabasesService.GetAllDatabases(ctx)
if err != nil {
return htmx.RespondToastError(c, err.Error())
}

destinations, err := h.servs.DestinationsService.GetAllDestinations(ctx)
if err != nil {
return htmx.RespondToastError(c, err.Error())
}

backups, err := h.servs.BackupsService.GetAllBackups(ctx)
if err != nil {
return htmx.RespondToastError(c, err.Error())
}

return echoutil.RenderGomponent(c, http.StatusOK, editWebhookForm(
webhook, databases, destinations, backups,
))
}

func editWebhookForm(
webhook dbgen.Webhook,
databases []dbgen.DatabasesServiceGetAllDatabasesRow,
destinations []dbgen.DestinationsServiceGetAllDestinationsRow,
backups []dbgen.Backup,
) gomponents.Node {
return html.Form(
htmx.HxPost("/dashboard/webhooks/"+webhook.ID.String()+"/edit"),
htmx.HxDisabledELT("find button[type='submit']"),
html.Class("space-y-2"),

createAndUpdateWebhookForm(databases, destinations, backups, webhook),

html.Div(
html.Class("flex justify-end items-center space-x-2 pt-2"),
component.HxLoadingMd(),
html.Button(
html.Class("btn btn-primary"),
html.Type("submit"),
component.SpanText("Save"),
lucide.Save(),
),
),
)
}

func editWebhookButton(webhookID uuid.UUID) gomponents.Node {
mo := component.Modal(component.ModalParams{
Size: component.SizeLg,
Title: "Edit webhook",
Content: []gomponents.Node{
html.Div(
htmx.HxGet("/dashboard/webhooks/"+webhookID.String()+"/edit"),
htmx.HxSwap("outerHTML"),
htmx.HxTrigger("intersect once"),
html.Class("p-10 flex justify-center"),
component.HxLoadingMd(),
),
},
})

button := html.Button(
mo.OpenerAttr,
html.Class("btn btn-neutral btn-sm btn-square btn-ghost"),
lucide.Pencil(),
)

return html.Div(
html.Class("inline-block"),
mo.HTML,
html.Div(
html.Class("inline-block tooltip tooltip-right"),
html.Data("tip", "Edit webhook"),
button,
),
)
}
10 changes: 8 additions & 2 deletions internal/view/web/dashboard/webhooks/list_webhooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,17 @@ func listWebhooks(
html.Class("w-[40px]"),
html.Div(
html.Class("flex justify-start space-x-1"),
// editWebhookButton(webhook),
editWebhookButton(whook.ID),
// deleteWebhookButton(webhook.ID),
),
),
html.Td(component.SpanText(whook.Name)),
html.Td(
html.Div(
html.Class("flex items-center space-x-2"),
component.IsActivePing(whook.IsActive),
component.SpanText(whook.Name),
),
),
html.Td(component.SpanText(
func() string {
if name, ok := webhooks.FullEventTypes[whook.EventType]; ok {
Expand Down
6 changes: 4 additions & 2 deletions internal/view/web/dashboard/webhooks/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ func MountRouter(

parent.GET("", h.indexPageHandler)
parent.GET("/list", h.listWebhooksHandler)
parent.GET("/create-form", h.createWebhookFormHandler)
parent.POST("", h.createWebhookHandler)
parent.GET("/create", h.createWebhookFormHandler)
parent.POST("/create", h.createWebhookHandler)
parent.GET("/:webhookID/edit", h.editWebhookFormHandler)
parent.POST("/:webhookID/edit", h.editWebhookHandler)
// parent.DELETE("/:webhookID", h.deleteWebhookHandler)
// parent.POST("/:webhookID/edit", h.editWebhookHandler)
}

0 comments on commit 0453aa9

Please sign in to comment.