Implement database mapping for vote types

This commit is contained in:
Jan Dittberner 2022-05-22 21:47:27 +02:00
parent e1af6876c1
commit aa3a1b0cc7
5 changed files with 89 additions and 27 deletions

View file

@ -100,7 +100,9 @@ func parseConfig(configFile string) (*Config, error) {
}
if len(config.CsrfKey) != csrfKeyLength {
return nil, fmt.Errorf("CSRF key must be exactly %d bytes long but is %d bytes long", csrfKeyLength, len(config.CsrfKey))
return nil, fmt.Errorf(
"CSRF key must be exactly %d bytes long but is %d bytes long", csrfKeyLength, len(config.CsrfKey),
)
}
return config, nil

View file

@ -29,12 +29,13 @@ import (
"strings"
"time"
"git.cacert.org/cacert-boardvoting/internal/models"
"git.cacert.org/cacert-boardvoting/internal/validator"
"git.cacert.org/cacert-boardvoting/ui"
"github.com/Masterminds/sprig/v3"
"github.com/gorilla/csrf"
"github.com/julienschmidt/httprouter"
"git.cacert.org/cacert-boardvoting/internal/models"
"git.cacert.org/cacert-boardvoting/internal/validator"
"git.cacert.org/cacert-boardvoting/ui"
)
func newTemplateCache() (map[string]*template.Template, error) {
@ -295,12 +296,36 @@ func (app *application) newMotionSubmit(w http.ResponseWriter, r *http.Request)
return
}
form.CheckField(validator.NotBlank(form.Title), "title", "This field cannot be blank")
form.CheckField(validator.MinChars(form.Title, minimumTitleLength), "title", fmt.Sprintf("This field must be at least %d characters long", minimumTitleLength))
form.CheckField(validator.MaxChars(form.Title, maximumTitleLength), "title", fmt.Sprintf("This field must be at most %d characters long", maximumTitleLength))
form.CheckField(validator.NotBlank(form.Content), "content", "This field cannot be blank")
form.CheckField(validator.MinChars(form.Content, minimumContentLength), "content", fmt.Sprintf("This field must be at least %d characters long", minimumContentLength))
form.CheckField(validator.MaxChars(form.Content, maximumContentLength), "content", fmt.Sprintf("This field must be at most %d characters long", maximumContentLength))
form.CheckField(
validator.NotBlank(form.Title),
"title",
"This field cannot be blank",
)
form.CheckField(
validator.MinChars(form.Title, minimumTitleLength),
"title",
fmt.Sprintf("This field must be at least %d characters long", minimumTitleLength),
)
form.CheckField(
validator.MaxChars(form.Title, maximumTitleLength),
"title",
fmt.Sprintf("This field must be at most %d characters long", maximumTitleLength),
)
form.CheckField(
validator.NotBlank(form.Content),
"content",
"This field cannot be blank",
)
form.CheckField(
validator.MinChars(form.Content, minimumContentLength),
"content",
fmt.Sprintf("This field must be at least %d characters long", minimumContentLength),
)
form.CheckField(
validator.MaxChars(form.Content, maximumContentLength),
"content",
fmt.Sprintf("This field must be at most %d characters long", maximumContentLength),
)
form.CheckField(validator.PermittedStr(
form.Due,
"+3 days",

View file

@ -41,6 +41,8 @@ import (
"git.cacert.org/cacert-boardvoting/internal/models"
)
const sessionHours = 12
var (
version = "undefined"
commit = "undefined"
@ -95,7 +97,7 @@ func main() {
sessionManager := scs.New()
sessionManager.Store = sqlite3store.New(db.DB)
sessionManager.Lifetime = 12 * time.Hour
sessionManager.Lifetime = sessionHours * time.Hour
app := &application{
errorLog: errorLog,
@ -145,7 +147,12 @@ func setupFormDecoder() *form.Decoder {
decoder := form.NewDecoder()
decoder.RegisterCustomTypeFunc(func(values []string) (interface{}, error) {
return models.VoteTypeFromString(values[0])
v, err := models.VoteTypeFromString(values[0])
if err != nil {
return nil, fmt.Errorf("could not convert value %s: %w", values[0], err)
}
return v, nil
}, new(models.VoteType))
return decoder

View file

@ -20,6 +20,7 @@ package models
import (
"context"
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"log"
@ -55,9 +56,9 @@ func VoteTypeFromString(label string) (*VoteType, error) {
return nil, fmt.Errorf("unknown vote type %s", label)
}
func VoteTypeFromUint8(id uint8) (*VoteType, error) {
func VoteTypeFromInt(id int64) (*VoteType, error) {
for _, vt := range []*VoteType{VoteTypeMotion, VoteTypeVeto} {
if vt.id == id {
if int64(vt.id) == id {
return vt, nil
}
}
@ -65,6 +66,26 @@ func VoteTypeFromUint8(id uint8) (*VoteType, error) {
return nil, fmt.Errorf("unknown vote type id %d", id)
}
func (v *VoteType) Scan(src any) error {
value, ok := src.(int64)
if !ok {
return fmt.Errorf("could not cast %v of %T to uint8", src, src)
}
vt, err := VoteTypeFromInt(value)
if err != nil {
return err
}
*v = *vt
return nil
}
func (v *VoteType) Value() (driver.Value, error) {
return int64(v.id), nil
}
func (v *VoteType) QuorumAndMajority() (int, float32) {
const (
majorityDefault = 0.99
@ -162,7 +183,7 @@ type Motion struct {
Due time.Time
Modified time.Time
Tag string
VoteType VoteType
VoteType *VoteType
}
type ClosedMotion struct {
@ -180,7 +201,7 @@ type MotionModel struct {
func (m *MotionModel) Create(
ctx context.Context,
proponent *Voter,
voteType VoteType,
voteType *VoteType,
title, content string,
proposed, due time.Time,
) (int64, error) {
@ -400,7 +421,7 @@ type MotionForDisplay struct {
Proposed time.Time
Title string
Content string
Type VoteType `db:"votetype"`
Type *VoteType `db:"votetype"`
Status VoteStatus
Due time.Time
Modified time.Time

View file

@ -1,3 +1,20 @@
/*
Copyright 2022 CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package validator
import (
@ -41,16 +58,6 @@ func MinChars(value string, n int) bool {
return utf8.RuneCountInString(value) >= n
}
func PermittedInt(value int, permittedValues ...int) bool {
for i := range permittedValues {
if value == permittedValues[i] {
return true
}
}
return false
}
func PermittedStr(value string, permittedValues ...string) bool {
for i := range permittedValues {
if value == permittedValues[i] {