refactor(errors): new error "helpers", following a more golang way

This commit is contained in:
Gustavo "Guz" L. de Mello
2024-07-24 15:23:07 -03:00
parent 2b5366d407
commit 046e0f9259
5 changed files with 152 additions and 18 deletions

View File

@@ -0,0 +1,20 @@
package errors
import (
"fmt"
"net/http"
"strings"
)
type ErrMissingParams struct {
defaultErr
Params []string `json:"params"`
}
func NewErrMissingParams(params ...string) ErrMissingParams {
return ErrMissingParams{Params: params}
}
func (e ErrMissingParams) Error() string {
return fmt.Sprintf("Missing parameters: %s.", strings.Join(e.Params, ", "))
}
func (e ErrMissingParams) Status() int { return http.StatusBadRequest }

View File

@@ -0,0 +1,15 @@
package errors
import (
"errors"
"net/http"
)
type ErrInternal struct {
defaultErr
Err string `json:"error"`
}
func NewErrInternal(err ...error) ErrInternal { return ErrInternal{Err: errors.Join(err...).Error()} }
func (e ErrInternal) Error() string { return e.Err }
func (e ErrInternal) Status() int { return http.StatusInternalServerError }

View File

@@ -0,0 +1,90 @@
package errors
import (
"net/http"
"strings"
"extrovert/templates/layouts"
"encoding/json"
)
type Error interface {
Error() string
Status() int
ServeHTTP(w http.ResponseWriter, r *http.Request)
Component() templ.Component
JSON() string
}
type defaultErr struct{}
func (e defaultErr) Error() string {
return "Error: This method should have been overridden :')"
}
func (e defaultErr) Status() int {
return http.StatusNotImplemented
}
func (e defaultErr) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if strings.Contains(r.Header.Get("Accept"), "text/html") {
w.Header().Set("Content-Type", "text/html")
err := e.Component().Render(r.Context(), w)
if err != nil {
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte("Unable to render error message, using JSON representation: " + e.JSON()))
w.WriteHeader(http.StatusInternalServerError)
return
}
} else {
w.Header().Set("Content-Type", "application/json")
_, err := w.Write([]byte(e.JSON()))
if err != nil {
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte("Unable to send error information due to: " + err.Error()))
w.WriteHeader(http.StatusInternalServerError)
return
}
}
w.WriteHeader(e.Status())
}
func (e defaultErr) JSON() string {
type jsonErr struct {
Error string `json:"error"`
Info any `json:"info"`
}
js, err := json.Marshal(jsonErr{
Error: e.Error(),
Info: e,
})
if err != nil {
js, _ = json.Marshal(jsonErr{
Error: "Unable to parse JSON of error",
Info: err.Error(),
})
}
return string(js)
}
templ (e defaultErr) Component() {
@layouts.Page("Error") {
<dialog open>
<article>
<header>
<p>Error</p>
</header>
<p>
{ e.Error() }
</p>
<footer>
<a href={ templ.SafeURL("/") }>
<button>Return to homepage</button>
</a>
</footer>
</article>
</dialog>
}
}

View File

@@ -4,13 +4,18 @@ import (
"extrovert/templates/layouts" "extrovert/templates/layouts"
"extrovert/components" "extrovert/components"
"net/http" "net/http"
"extrovert/internals/router/errors"
e "errors"
) )
type Homepage struct{} type Homepage struct{}
func (h Homepage) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h Homepage) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
_ = h.page().Render(context.Background(), w) err := h.page().Render(context.Background(), w)
if err != nil {
errors.NewErrInternal(e.New("Unable to render dashboard"), err).ServeHTTP(w, r)
}
} }
templ (h Homepage) page() { templ (h Homepage) page() {

View File

@@ -1,10 +1,11 @@
package routes package routes
import ( import (
e "errors"
"io" "io"
"net/http" "net/http"
"extrovert/internals" "extrovert/internals/router/errors"
) )
type AITxt struct{} type AITxt struct{}
@@ -13,23 +14,24 @@ func (_ AITxt) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Cache-Control", "max-age=604800, stale-while-revalidate=86400, stale-if-error=86400") w.Header().Add("Cache-Control", "max-age=604800, stale-while-revalidate=86400, stale-if-error=86400")
w.Header().Add("CDN-Cache-Control", "max-age=604800") w.Header().Add("CDN-Cache-Control", "max-age=604800")
error := internals.HttpErrorHelper(w) list, err := http.Get("https://raw.githubusercontent.com/ai-robots-txt/ai.robots.txt/main/ai.txt")
if err != nil {
aiList, err := http.Get("https://raw.githubusercontent.com/ai-robots-txt/ai.robots.txt/main/ai.txt") errors.NewErrInternal(e.New("Unable to fetch ai.txt list"), err).ServeHTTP(w, r)
if error("Error trying to create ai block list", err, http.StatusInternalServerError) {
return return
} }
bytes, err := io.ReadAll(aiList.Body) bytes, err := io.ReadAll(list.Body)
if error("Error trying to create ai block list", err, http.StatusInternalServerError) { if err != nil {
return errors.NewErrInternal(e.New("Unable to read dynamic ai.txt list"), err).ServeHTTP(w, r)
}
_, err = io.WriteString(w, string(bytes))
if error("Error trying to create ai block list", err, http.StatusInternalServerError) {
return return
} }
w.Header().Add("Content-Type", "text/plain") w.Header().Add("Content-Type", "text/plain")
_, err = w.Write(bytes)
if err != nil {
errors.NewErrInternal(e.New("Unable to write ai.txt list"), err).ServeHTTP(w, r)
return
}
} }
type RobotsTxt struct{} type RobotsTxt struct{}
@@ -38,19 +40,21 @@ func (_ RobotsTxt) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Cache-Control", "max-age=604800, stale-while-revalidate=86400, stale-if-error=86400") w.Header().Add("Cache-Control", "max-age=604800, stale-while-revalidate=86400, stale-if-error=86400")
w.Header().Add("CDN-Cache-Control", "max-age=604800") w.Header().Add("CDN-Cache-Control", "max-age=604800")
error := internals.HttpErrorHelper(w) list, err := http.Get("https://raw.githubusercontent.com/ai-robots-txt/ai.robots.txt/main/robots.txt")
aiList, err := http.Get("https://raw.githubusercontent.com/ai-robots-txt/ai.robots.txt/main/robots.txt") if err != nil {
if error("Error trying to create robots block list", err, http.StatusInternalServerError) { errors.NewErrInternal(e.New("Unable to fetch robots.txt list"), err).ServeHTTP(w, r)
return return
} }
bytes, err := io.ReadAll(aiList.Body) bytes, err := io.ReadAll(list.Body)
if error("Error trying to create robots block list", err, http.StatusInternalServerError) { if err != nil {
errors.NewErrInternal(e.New("Unable to read dynamic robots.txt list"), err).ServeHTTP(w, r)
return return
} }
_, err = io.WriteString(w, string(bytes)) _, err = io.WriteString(w, string(bytes))
if error("Error trying to create robots block list", err, http.StatusInternalServerError) { if err != nil {
errors.NewErrInternal(e.New("Unable to write robots.txt list"), err).ServeHTTP(w, r)
return return
} }
w.Header().Add("Content-Type", "text/plain") w.Header().Add("Content-Type", "text/plain")