Implement database mapping for vote types

main
Jan Dittberner 2 years ago
parent e1af6876c1
commit aa3a1b0cc7

@ -100,7 +100,9 @@ func parseConfig(configFile string) (*Config, error) {
} }
if len(config.CsrfKey) != csrfKeyLength { 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 return config, nil

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

@ -41,6 +41,8 @@ import (
"git.cacert.org/cacert-boardvoting/internal/models" "git.cacert.org/cacert-boardvoting/internal/models"
) )
const sessionHours = 12
var ( var (
version = "undefined" version = "undefined"
commit = "undefined" commit = "undefined"
@ -95,7 +97,7 @@ func main() {
sessionManager := scs.New() sessionManager := scs.New()
sessionManager.Store = sqlite3store.New(db.DB) sessionManager.Store = sqlite3store.New(db.DB)
sessionManager.Lifetime = 12 * time.Hour sessionManager.Lifetime = sessionHours * time.Hour
app := &application{ app := &application{
errorLog: errorLog, errorLog: errorLog,
@ -145,7 +147,12 @@ func setupFormDecoder() *form.Decoder {
decoder := form.NewDecoder() decoder := form.NewDecoder()
decoder.RegisterCustomTypeFunc(func(values []string) (interface{}, error) { 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)) }, new(models.VoteType))
return decoder return decoder

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

@ -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 package validator
import ( import (
@ -41,16 +58,6 @@ func MinChars(value string, n int) bool {
return utf8.RuneCountInString(value) >= n 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 { func PermittedStr(value string, permittedValues ...string) bool {
for i := range permittedValues { for i := range permittedValues {
if value == permittedValues[i] { if value == permittedValues[i] {

Loading…
Cancel
Save