switched to bindings + validators for forms

This commit is contained in:
BroodjeAap 2022-07-25 18:03:35 +00:00
parent de64cd6aab
commit 18449ed0a5
6 changed files with 81 additions and 151 deletions

75
main.go
View file

@ -42,6 +42,7 @@ func (web Web) createWatch(c *gin.Context) {
errMap, err := bindAndValidateWatch(&watch, c)
if err != nil {
c.HTML(http.StatusBadRequest, "500", errMap)
return
}
web.db.Create(&watch)
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/watch/view/%d", watch.ID))
@ -74,83 +75,37 @@ func (web Web) createURL(c *gin.Context) {
c.HTML(http.StatusInternalServerError, "500", errMap)
return
}
web.db.Create(url)
web.db.Create(&url)
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/watch/view/%d", url.WatchID))
}
func (web Web) createQuery(c *gin.Context) {
watch_id, err := strconv.ParseUint(c.PostForm("watch_id"), 10, 64)
watch_id, err := strconv.ParseUint(c.PostForm("w_id"), 10, 64)
if err != nil {
c.Redirect(http.StatusSeeOther, "/watch/new")
return // TODO response
log.Print(err)
c.HTML(http.StatusInternalServerError, "500", gin.H{})
return
}
url_id, err := strconv.ParseUint(c.PostForm("url_id"), 10, 64)
var query Query
errMap, err := bindAndValidateQuery(&query, c)
if err != nil {
c.Redirect(http.StatusSeeOther, "/watch/new")
return // TODO response
}
name := c.PostForm("name")
if name == "" {
c.Redirect(http.StatusSeeOther, "/watch/new")
c.HTML(http.StatusBadRequest, "500", errMap)
return
}
typ := c.PostForm("type")
if typ == "" {
c.Redirect(http.StatusSeeOther, "/watch/new")
return
}
query := c.PostForm("query")
if query == "" {
c.Redirect(http.StatusSeeOther, "/watch/new")
return
}
query_model := &Query{
URLID: uint(url_id),
Name: name,
Type: typ,
Query: query,
}
web.db.Create(query_model)
web.db.Create(&query)
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/watch/view/%d", watch_id))
}
func (web Web) createFilter(c *gin.Context) {
query_id, err := strconv.ParseUint(c.PostForm("query_id"), 10, 64)
var filter Filter
errMap, err := bindAndValidateFilter(&filter, c)
if err != nil {
log.Print(err)
c.Redirect(http.StatusSeeOther, "/watch/new")
return // TODO response
}
name := c.PostForm("name")
if name == "" {
log.Print(name)
c.Redirect(http.StatusSeeOther, "/watch/new")
c.HTML(http.StatusBadRequest, "500", errMap)
return
}
typ := c.PostForm("type")
if typ == "" {
log.Print(typ)
c.Redirect(http.StatusSeeOther, "/watch/new")
return
}
from := c.PostForm("from")
if from == "" {
log.Print(from)
c.Redirect(http.StatusSeeOther, "/watch/new")
return
}
to := c.PostForm("to")
log.Print("To:", to)
filter_model := &Filter{
QueryID: uint(query_id),
Name: name,
Type: typ,
From: from,
To: to,
}
web.db.Create(filter_model)
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/query/edit/%d", query_id))
web.db.Create(&filter)
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/query/edit/%d", filter.QueryID))
}
func (web Web) editQuery(c *gin.Context) {

View file

@ -6,36 +6,36 @@ import (
type Watch struct {
gorm.Model
Name string `form:"name" yaml:"name" binding:"required" validate:"min=1"`
Name string `form:"watch_name" yaml:"watch_name" binding:"required" validate:"min=1"`
Interval int `form:"interval" yaml:"interval" binding:"required"`
URLs []URL
}
type URL struct {
gorm.Model
WatchID uint `form:"watch_id" yaml:"watch_id" binding:"required"`
WatchID uint `form:"url_watch_id" yaml:"url_watch_id" binding:"required"`
Watch *Watch `form:"watch" yaml:"watch" validate:"omitempty"`
Name string `form:"name" yaml:"name" binding:"required"`
URL string `form:"url" yaml:"url" binding:"required,url"`
Name string `form:"url_name" yaml:"url_name" binding:"required" validate:"min=1"`
URL string `form:"url" yaml:"url" binding:"required,url" validate:"min=1"`
Queries []Query
}
type Query struct {
gorm.Model
URLID uint `form:"url_id" yaml:"url_id" binding:"required"`
URL URL
Name string `form:"name" yaml:"name" binding:"required"`
Type string `form:"type" yaml:"type" binding:"required"`
URLID uint `form:"query_url_id" yaml:"query_url_id" binding:"required"`
URL *URL
Name string `form:"query_name" yaml:"query_name" binding:"required" validate:"min=1"`
Type string `form:"query_type" yaml:"query_type" binding:"required" validate:"oneof=css xpath regex json"`
Query string `form:"query" yaml:"query" binding:"required"`
Filters []Filter
}
type Filter struct {
gorm.Model
QueryID uint `form:"query_id" yaml:"query_id" binding:"required"`
Query Query
Name string `form:"name" yaml:"name" binding:"required"`
Type string `form:"type" yaml:"type" binding:"required"`
QueryID uint `form:"filter_query_id" yaml:"filter_query_id" binding:"required"`
Query *Query
Name string `form:"filter_name" yaml:"filter_name" binding:"required" validate:"min=1"`
Type string `form:"filter_type" yaml:"filter_type" binding:"required" validate:"oneof=replace regex substring"`
From string `form:"from" yaml:"from" binding:"required"`
To string `form:"to" yaml:"to" binding:"required"`
}

View file

@ -66,30 +66,27 @@
<div class="modal-body">
<ul class="nav nav-tabs" id="newFilterTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="replace-tab" data-bs-toggle="tab" data-bs-target="#replace-tab-pane" type="button" role="tab" aria-controls="home-tab-pane" aria-selected="true">Replace</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="regex-tab" data-bs-toggle="tab" data-bs-target="#regex-tab-pane" type="button" role="tab" aria-controls="profile-tab-pane" aria-selected="false">Regex</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="substring-tab" data-bs-toggle="tab" data-bs-target="#substring-tab-pane" type="button" role="tab" aria-controls="contact-tab-pane" aria-selected="false">Substring</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="lua-tab" data-bs-toggle="tab" data-bs-target="#lua-tab-pane" type="button" role="tab" aria-selected="false">Lua</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link active" id="replace-tab" data-bs-toggle="tab" data-bs-target="#replace-tab-pane" type="button" role="tab" aria-controls="home-tab-pane" aria-selected="true">Replace</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="regex-tab" data-bs-toggle="tab" data-bs-target="#regex-tab-pane" type="button" role="tab" aria-controls="profile-tab-pane" aria-selected="false">Regex</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="substring-tab" data-bs-toggle="tab" data-bs-target="#substring-tab-pane" type="button" role="tab" aria-controls="contact-tab-pane" aria-selected="false">Substring</button>
</li>
</ul>
<div class="tab-content" id="newFilterTabsContent">
<div class="tab-pane fade show active" id="replace-tab-pane" role="tabpanel" aria-labelledby="replace-tab" tabindex="0">
<form action="/filter/create" method="post">
<div class="mb-3 m-3 row">
<input type="hidden" name="query_id" value="{{ .Query.ID }}" >
<input type="hidden" name="type" value="replace" >
<input type="hidden" name="filter_query_id" value="{{ .Query.ID }}" >
<input type="hidden" name="filter_type" value="replace" >
<label for="nameInput" class="col-sm-2 col-form-label">Name:</label>
<div class="col-sm-10 p-2">
<input type="text" class="form-control" name="name" id="nameInput" placeholder="Name">
<input type="text" class="form-control" name="filter_name" id="nameInput" placeholder="Name">
</div>
<label for="fromInput" class="col-sm-2 col-form-label">From:</label>
<div class="col-sm-10 p-2">
@ -106,12 +103,12 @@
<div class="tab-pane fade" id="regex-tab-pane" role="tabpanel" aria-labelledby="regex-tab" tabindex="0">
<form action="/filter/create" method="post">
<div class="mb-3 m-3 row">
<input type="hidden" name="query_id" value="{{ .Query.ID }}" >
<input type="hidden" name="type" value="regex" >
<input type="hidden" name="filter_query_id" value="{{ .Query.ID }}" >
<input type="hidden" name="filter_type" value="regex" >
<label for="nameInput" class="col-sm-2 col-form-label">Name:</label>
<div class="col-sm-10 p-2">
<input type="text" class="form-control" name="name" id="nameInput" placeholder="Name">
<input type="text" class="form-control" name="filter_name" id="nameInput" placeholder="Name">
</div>
<label for="fromInput" class="col-sm-2 col-form-label">From:</label>
<div class="col-sm-10 p-2">
@ -128,13 +125,13 @@
<div class="tab-pane fade" id="substring-tab-pane" role="tabpanel" aria-labelledby="substring-tab" tabindex="0">
<form action="/filter/create" method="post">
<div class="mb-3 m-3 row">
<input type="hidden" name="query_id" value="{{ .Query.ID }}" >
<input type="hidden" name="type" value="substring" >
<input type="hidden" name="filter_query_id" value="{{ .Query.ID }}" >
<input type="hidden" name="filter_type" value="substring" >
<input type="hidden" name="to" value="-" >
<label for="nameInput" class="col-sm-2 col-form-label">Name:</label>
<div class="col-sm-10 p-2">
<input type="text" class="form-control" name="name" id="nameInput" placeholder="Name">
<input type="text" class="form-control" name="filter_name" id="nameInput" placeholder="Name">
</div>
<label for="fromInput" class="col-sm-2 col-form-label">From:</label>
<div class="col-sm-10 p-2">
@ -144,27 +141,6 @@
</div>
</form>
</div>
<div class="tab-pane fade" id="lua-tab-pane" role="tabpanel" aria-labelledby="lua-tab" tabindex="0">
<form action="/filter/create" method="post">
<div class="mb-3 m-3 row">
<input type="hidden" name="query_id" value="{{ .Query.ID }}" >
<input type="hidden" name="type" value="lua" >
<input type="hidden" name="to" value="-" >
<label for="nameInput" class="col-sm-2 col-form-label">Name:</label>
<div class="col-sm-10 p-2">
<input type="text" class="form-control" name="name" id="nameInput" placeholder="Name">
</div>
<label for="fromInput" class="col-sm-2 col-form-label">From:</label>
<select name="from" class="form-select"> <!-- TODO Lua stuff -->
<option>lua_func1</option>
<option>lua_func2</option>
<option>lua_func3</option>
</select>
<button class="btn btn-primary mt-4">Add Filter</button>
</div>
</form>
</div>
</div>
</div>
</div>

View file

@ -2,7 +2,7 @@
<form action="/watch/create" method="post">
<div class="mb-3">
<label for="name" class="form-label">Watch Name</label>
<input name="name" type="text" class="form-control" id="name" placeholder="Watch">
<input name="watch_name" type="text" class="form-control" id="name" placeholder="Watch">
</div>
<div class="mb-3">
<label for="interval" class="form-label">Interval (seconds) - TODO </label>
@ -10,5 +10,4 @@
</div>
<input type="submit" class="btn btn-primary mb-3" value="Create">
</form>
{{ . }}
{{end}}

View file

@ -26,13 +26,16 @@
<div class="col h4 text-center" >{{ .Name }}</div>
<div class="col h5 text-end"><a class="btn btn-success btn-sm" href="/query/edit/{{ .ID }}">Edit</a></div>
</div>
<div class="text-center text-muted">
{{ .Query }}
</div>
</div>
<div class="card-body">
<form action="/filter/create" method="post">
<input type="hidden" name="query_id" value="{{ .ID }}" >
<input type="hidden" name="watch_id" value="{{ $.ID }}" >
<table class="table table-hover caption-top">
<tbody>
<input type="hidden" name="query_id" value="{{ .ID }}" >
<input type="hidden" name="w_id" value="{{ $.ID }}" >
<table class="table table-hover caption-top">
<tbody>
{{ if .Filters }}
{{ range .Filters }}
<tr>
<td>{{ .Name }}</td>
@ -42,48 +45,35 @@
<td></td>
</tr>
{{ end }}
{{ else }}
<tr>
<td><input type="text" class="form-control" name="name" placeholder="Filter Name"></td>
<td>
<select class="form-control" id="type" name="type">
<option>Replace</option>
<option>Regex Replace</option>
<option>Substring</option>
<option>Lua</option>
</select>
</td>
<td><input type="text" class="form-control" name="from" placeholder="From"></td>
<td><input type="text" class="form-control" name="to" placeholder="To"></td>
<td><button class="btn btn-primary">Add Filter</button></td>
<td class="text-center h3">No filters yet, click "Edit" to add</td>
</tr>
</tbody>
</table>
</form>
{{ end }}
</tbody>
</table>
</div>
<div class="card-footer">
<div class="row">
<div class="col-2">{{ .Type }}</div>
<div class="col-8">{{ .Query }}</div>
</div>
</div>
</div>
{{ end }}
<form action="/query/create" method="post">
<input type="hidden" name="url_id" value="{{ .ID }}" >
<input type="hidden" name="watch_id" value="{{ $.ID }}" >
<input type="hidden" name="query_url_id" value="{{ .ID }}" >
<input type="hidden" name="w_id" value="{{ $.ID }}" >
<table class="table table-hover caption-top">
<tbody>
<tr>
<td>
<input type="text" class="form-control" name="name" placeholder="Query Name">
<input type="text" class="form-control" name="query_name" placeholder="Query Name">
</td>
<td>
<select class="form-control" id="type" name="type">
<option>CSS Selector</option>
<option>XPath</option>
<option>Regex</option>
<option>JSON</option>
<option>Lua</option>
<select class="form-control" id="query_type" name="query_type">
<option value="css">CSS Selector</option>
<option value="xpath">XPath</option>
<option value="regex">Regex</option>
<option value="json">JSON</option>
<!-- additions/changes should also be added to Query.Type oneof validator -->
</select>
</td>
<td>
@ -106,13 +96,13 @@
<div class="card-body">
<form action="/url/create" method="post">
<div class="form-group mb-2">
<input type="text" class="form-control" name="name" id="urlName" placeholder="URL Name">
<input type="text" class="form-control" name="url_name" id="urlName" placeholder="URL Name">
</div>
<div class="form-group mb-2">
<input type="url" class="form-control" name="url" id="url" placeholder="URL">
</div>
<input type="hidden" name="watch_id" value="{{ .ID }}" >
<input type="hidden" name="url_watch_id" value="{{ .ID }}" >
<input class="btn btn-primary" type="submit" value="Create URL">
</form>
</div>

10
util.go
View file

@ -17,6 +17,16 @@ func bindAndValidateURL(url *URL, c *gin.Context) (map[string]string, error) {
return validate(err), err
}
func bindAndValidateQuery(query *Query, c *gin.Context) (map[string]string, error) {
err := c.ShouldBind(query)
return validate(err), err
}
func bindAndValidateFilter(filter *Filter, c *gin.Context) (map[string]string, error) {
err := c.ShouldBind(filter)
return validate(err), err
}
func prettyError(fieldError validator.FieldError) string {
switch fieldError.Tag() {
case "required":