CI / ci (push) Successful in 8m31s
Co-authored-by: orchestrator-agent <20+orchestrator-agent@code.cubecraftcreations.com>
85 lines
3.3 KiB
Go
85 lines
3.3 KiB
Go
package server
|
|
|
|
import (
|
|
"errors"
|
|
"log/slog"
|
|
"net/http"
|
|
|
|
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/chat"
|
|
)
|
|
|
|
// chatWebhookBody is the PUT /settings/chat-webhook body. Both fields are
|
|
// required.
|
|
type chatWebhookBody struct {
|
|
URL *string `json:"url"`
|
|
SpaceLabel *string `json:"space_label"`
|
|
}
|
|
|
|
// getChatWebhook answers the stored webhook's settings, which never hold
|
|
// its URL.
|
|
func getChatWebhook(logger *slog.Logger, svc *chat.Service) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
settings, err := svc.Get(r.Context())
|
|
if err != nil {
|
|
logger.ErrorContext(r.Context(), "get chat webhook", "error", err)
|
|
writeError(w, r, logger, http.StatusInternalServerError, "internal", "internal error")
|
|
return
|
|
}
|
|
writeJSON(w, r, logger, http.StatusOK, settings)
|
|
}
|
|
}
|
|
|
|
// putChatWebhook stores the webhook URL sealed, with its space label, and
|
|
// answers the settings as GET does.
|
|
func putChatWebhook(logger *slog.Logger, svc *chat.Service) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
body, err := decodePatch[chatWebhookBody](http.MaxBytesReader(w, r.Body, maxSettingsBody))
|
|
if err != nil {
|
|
writeError(w, r, logger, http.StatusBadRequest, "invalid_request", err.Error())
|
|
return
|
|
}
|
|
if body.URL == nil || body.SpaceLabel == nil {
|
|
writeError(w, r, logger, http.StatusBadRequest, "invalid_request", "url and space_label are required")
|
|
return
|
|
}
|
|
settings, err := svc.Put(r.Context(), *body.URL, *body.SpaceLabel)
|
|
var invalid *chat.ValidationError
|
|
if errors.As(err, &invalid) {
|
|
writeError(w, r, logger, http.StatusBadRequest, "invalid_webhook", invalid.Error())
|
|
return
|
|
}
|
|
if err != nil {
|
|
logger.ErrorContext(r.Context(), "save chat webhook", "error", err)
|
|
writeError(w, r, logger, http.StatusInternalServerError, "internal", "internal error")
|
|
return
|
|
}
|
|
writeJSON(w, r, logger, http.StatusOK, settings)
|
|
}
|
|
}
|
|
|
|
// postChatWebhookTest sends chat.TestText to the stored webhook and answers
|
|
// 200 with the status Google Chat answered, whatever it was, and the
|
|
// recorded outcome. No webhook stored, or one replaced during the test, is
|
|
// 409; no answer from Google Chat is 502.
|
|
func postChatWebhookTest(logger *slog.Logger, svc *chat.Service) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
result, err := svc.Test(r.Context())
|
|
switch {
|
|
case errors.Is(err, chat.ErrNotConfigured):
|
|
writeError(w, r, logger, http.StatusConflict, "not_configured", "no Google Chat webhook is configured")
|
|
case errors.Is(err, chat.ErrWebhookChanged):
|
|
writeError(w, r, logger, http.StatusConflict, "webhook_changed", "the Google Chat webhook changed during the test; test it again")
|
|
case err != nil && r.Context().Err() != nil:
|
|
logger.WarnContext(r.Context(), "chat webhook test interrupted", "error", err)
|
|
case errors.Is(err, chat.ErrUnreachable):
|
|
logger.WarnContext(r.Context(), "chat webhook test failed", "error", err)
|
|
writeError(w, r, logger, http.StatusBadGateway, "chat_unreachable", "Google Chat did not answer the test message")
|
|
case err != nil:
|
|
logger.ErrorContext(r.Context(), "chat webhook test", "error", err)
|
|
writeError(w, r, logger, http.StatusInternalServerError, "internal", "internal error")
|
|
default:
|
|
writeJSON(w, r, logger, http.StatusOK, result)
|
|
}
|
|
}
|
|
}
|