Completed first refactoring with Go generics
This commit is contained in:
parent
8382cc4222
commit
3cdfa72403
16 changed files with 501 additions and 164 deletions
1
go.mod
1
go.mod
|
@ -15,6 +15,7 @@ require (
|
|||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||
github.com/glebarez/sqlite v1.9.0 // indirect
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible // indirect
|
||||
github.com/gocarina/gocsv v0.0.0-20230616125104-99d496ca653d // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/kr/pretty v0.2.1 // indirect
|
||||
|
|
3
go.sum
3
go.sum
|
@ -7,6 +7,8 @@ github.com/glebarez/sqlite v1.9.0 h1:Aj6bPA12ZEx5GbSF6XADmCkYXlljPNUY+Zf1EQxynXs
|
|||
github.com/glebarez/sqlite v1.9.0/go.mod h1:YBYCoyupOao60lzp1MVBLEjZfgkq0tdB1voAQ09K9zw=
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
||||
github.com/gocarina/gocsv v0.0.0-20230616125104-99d496ca653d h1:KbPOUXFUDJxwZ04vbmDOc3yuruGvVO+LOa7cVER3yWw=
|
||||
github.com/gocarina/gocsv v0.0.0-20230616125104-99d496ca653d/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
|
@ -37,6 +39,7 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
|
|
|
@ -1,12 +1,44 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Participant struct {
|
||||
ID string `gorm:"primaryKey"`
|
||||
ID string `csv:"id" gorm:"primaryKey"`
|
||||
|
||||
Firstname string
|
||||
Lastname string
|
||||
Firstname string `csv:"firstname"`
|
||||
Lastname string `csv:"lastname"`
|
||||
|
||||
Token uint
|
||||
Token uint `csv:"token"`
|
||||
|
||||
Attributes map[string]string
|
||||
}
|
||||
|
||||
func (p *Participant) String() string {
|
||||
return fmt.Sprintf("%s %s", p.Lastname, p.Firstname)
|
||||
}
|
||||
|
||||
func (p *Participant) GetID() string {
|
||||
return p.ID
|
||||
}
|
||||
|
||||
func (p *Participant) SetID(id string) {
|
||||
p.ID = id
|
||||
}
|
||||
|
||||
func (p *Participant) GetHash() string {
|
||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(strings.Join(append([]string{p.Lastname, p.Firstname}, p.AttributesToSlice()...), ""))))
|
||||
}
|
||||
|
||||
func (p *Participant) AttributesToSlice() []string {
|
||||
result := make([]string, len(p.Attributes)*2)
|
||||
|
||||
for k, v := range p.Attributes {
|
||||
result = append(result, k, v)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
5
store/collection.go
Normal file
5
store/collection.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
package store
|
||||
|
||||
import "git.andreafazzi.eu/andrea/probo/models"
|
||||
|
||||
type CollectionStore = Store[*models.Collection]
|
|
@ -9,45 +9,46 @@ import (
|
|||
"git.andreafazzi.eu/andrea/probo/store"
|
||||
)
|
||||
|
||||
func NewCollectionFileStore() (*FileStore[*models.Collection, *store.Store[*models.Collection]], error) {
|
||||
return NewFileStore[*models.Collection](
|
||||
store.NewStore[*models.Collection](),
|
||||
filepath.Join(BaseDir, CollectionsDir),
|
||||
"collection",
|
||||
".json",
|
||||
func(s *store.Store[*models.Collection], filepath string, content []byte) (*models.Collection, error) {
|
||||
collection := new(models.Collection)
|
||||
err := json.Unmarshal(content, &collection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
type CollectionFileStore = FileStore[*models.Collection, *store.Store[*models.Collection]]
|
||||
|
||||
c, err := s.Create(collection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var DefaultCollectionDir = filepath.Join(BaseDir, CollectionsDir)
|
||||
|
||||
return c, nil
|
||||
},
|
||||
func(s *store.Store[*models.Collection], filePath string, collection *models.Collection) error {
|
||||
jsonData, err := json.Marshal(collection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.Write(jsonData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
func NewCollectionFileStore(config *FileStoreConfig[*models.Collection, *store.CollectionStore]) (*CollectionFileStore, error) {
|
||||
return NewFileStore[*models.Collection](config, store.NewStore[*models.Collection]())
|
||||
}
|
||||
|
||||
func DefaultUnmarshalCollectionFunc(s *store.Store[*models.Collection], filepath string, content []byte) (*models.Collection, error) {
|
||||
collection := new(models.Collection)
|
||||
err := json.Unmarshal(content, &collection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := s.Create(collection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func DefaultMarshalCollectionFunc(s *store.Store[*models.Collection], filePath string, collection *models.Collection) error {
|
||||
jsonData, err := json.Marshal(collection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.Write(jsonData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -48,7 +48,14 @@ func (t *collectionTestSuite) TestCreateCollection() {
|
|||
},
|
||||
})
|
||||
|
||||
store, err := NewCollectionFileStore()
|
||||
store, err := NewCollectionFileStore(
|
||||
&FileStoreConfig[*models.Collection, *store.CollectionStore]{
|
||||
FilePathConfig: FilePathConfig{"testdata", "collection", ".json"},
|
||||
IndexDirFunc: DefaultIndexDirFunc[*models.Collection, *store.CollectionStore],
|
||||
UnmarshalFunc: DefaultUnmarshalCollectionFunc,
|
||||
MarshalFunc: DefaultMarshalCollectionFunc,
|
||||
},
|
||||
)
|
||||
t.Nil(err)
|
||||
|
||||
c := new(models.Collection)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package file
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
|
@ -12,49 +13,121 @@ import (
|
|||
"git.andreafazzi.eu/andrea/probo/store"
|
||||
)
|
||||
|
||||
type IndexDirFunc[T store.Storable, K Storer[T]] func(s *FileStore[T, K]) error
|
||||
|
||||
var (
|
||||
ErrorMetaHeaderIsNotPresent = errors.New("Meta header was not found in file.")
|
||||
|
||||
BaseDir = "data"
|
||||
QuizzesDir = "quizzes"
|
||||
CollectionsDir = "collections"
|
||||
BaseDir = "data"
|
||||
QuizzesDir = "quizzes"
|
||||
CollectionsDir = "collections"
|
||||
ParticipantsDir = "participants"
|
||||
)
|
||||
|
||||
type Storer[T store.Storable] interface {
|
||||
store.Storer[T]
|
||||
// store.FilterStorer[T]
|
||||
}
|
||||
|
||||
type FileStore[T store.Storable, K Storer[T]] struct {
|
||||
Storer K
|
||||
|
||||
type FilePathConfig struct {
|
||||
Dir string
|
||||
FilePrefix string
|
||||
FileSuffix string
|
||||
}
|
||||
|
||||
MarshalFunc func(K, string, []byte) (T, error)
|
||||
UnmarshalFunc func(K, string, T) error
|
||||
type FileStoreConfig[T store.Storable, K Storer[T]] struct {
|
||||
FilePathConfig
|
||||
FilepathFunc func(T, *FilePathConfig) string
|
||||
UnmarshalFunc func(K, string, []byte) (T, error)
|
||||
MarshalFunc func(K, string, T) error
|
||||
IndexDirFunc func(*FileStore[T, K]) error
|
||||
}
|
||||
|
||||
type FileStore[T store.Storable, K Storer[T]] struct {
|
||||
*FileStoreConfig[T, K]
|
||||
|
||||
Storer K
|
||||
|
||||
lock sync.RWMutex
|
||||
paths map[string]string
|
||||
}
|
||||
|
||||
func NewFileStore[T store.Storable, K Storer[T]](
|
||||
storer K,
|
||||
dir string,
|
||||
prefix string,
|
||||
suffix string,
|
||||
marshalFunc func(K, string, []byte) (T, error),
|
||||
unmarshalFunc func(K, string, T) error,
|
||||
) (*FileStore[T, K], error) {
|
||||
func DefaultJSONUnmarshalFunc[T store.Storable, K Storer[T]](s K, filepath string, content []byte) (T, error) {
|
||||
entity := new(T)
|
||||
err := json.Unmarshal(content, &entity)
|
||||
if err != nil {
|
||||
return *entity, err
|
||||
}
|
||||
|
||||
c, err := s.Create(*entity)
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func DefaultJSONMarshalFunc[T store.Storable, K Storer[T]](s K, filePath string, entity T) error {
|
||||
jsonData, err := json.Marshal(entity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.Write(jsonData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func DefaultIndexDirFunc[T store.Storable, K Storer[T]](s *FileStore[T, K]) error {
|
||||
files, err := os.ReadDir(s.Dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entityFiles := make([]fs.DirEntry, 0)
|
||||
|
||||
for _, file := range files {
|
||||
filename := file.Name()
|
||||
if !file.IsDir() && strings.HasSuffix(filename, s.FileSuffix) {
|
||||
entityFiles = append(entityFiles, file)
|
||||
}
|
||||
}
|
||||
|
||||
for _, file := range entityFiles {
|
||||
filename := file.Name()
|
||||
fullPath := filepath.Join(s.Dir, filename)
|
||||
|
||||
content, err := os.ReadFile(fullPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entity, err := s.UnmarshalFunc(s.Storer, fullPath, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.SetPath(entity, fullPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func NewFileStore[T store.Storable, K Storer[T]](config *FileStoreConfig[T, K], storer K) (*FileStore[T, K], error) {
|
||||
store := &FileStore[T, K]{
|
||||
Storer: storer,
|
||||
Dir: dir,
|
||||
FilePrefix: prefix,
|
||||
FileSuffix: suffix,
|
||||
MarshalFunc: marshalFunc,
|
||||
UnmarshalFunc: unmarshalFunc,
|
||||
paths: make(map[string]string, 0),
|
||||
FileStoreConfig: config,
|
||||
Storer: storer,
|
||||
paths: make(map[string]string, 0),
|
||||
}
|
||||
|
||||
err := store.IndexDir()
|
||||
|
@ -72,9 +145,15 @@ func (s *FileStore[T, K]) Create(entity T) (T, error) {
|
|||
return e, err
|
||||
}
|
||||
|
||||
filePath := filepath.Join(s.Dir, fmt.Sprintf("%s_%v%s", s.FilePrefix, e.GetID(), s.FileSuffix))
|
||||
var filePath string
|
||||
|
||||
err = s.UnmarshalFunc(s.Storer, filePath, e)
|
||||
if s.FilepathFunc == nil {
|
||||
filePath = filepath.Join(s.Dir, fmt.Sprintf("%s_%v%s", s.FilePrefix, e.GetID(), s.FileSuffix))
|
||||
} else {
|
||||
filePath = s.FilepathFunc(entity, &s.FilePathConfig)
|
||||
}
|
||||
|
||||
err = s.MarshalFunc(s.Storer, filePath, e)
|
||||
if err != nil {
|
||||
return e, err
|
||||
}
|
||||
|
@ -90,15 +169,13 @@ func (s *FileStore[T, K]) Update(entity T, id string) (T, error) {
|
|||
return e, err
|
||||
}
|
||||
|
||||
filePath := filepath.Join(s.Dir, fmt.Sprintf("%s_%v%s", s.FilePrefix, e.GetID(), s.FileSuffix))
|
||||
filePath := s.GetPath(e)
|
||||
|
||||
err = s.UnmarshalFunc(s.Storer, filePath, e)
|
||||
err = s.MarshalFunc(s.Storer, filePath, e)
|
||||
if err != nil {
|
||||
return e, err
|
||||
}
|
||||
|
||||
s.SetPath(e, filePath)
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
|
@ -125,39 +202,7 @@ func (s *FileStore[T, K]) Delete(id string) (T, error) {
|
|||
}
|
||||
|
||||
func (s *FileStore[T, K]) IndexDir() error {
|
||||
files, err := os.ReadDir(s.Dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entityFiles := make([]fs.DirEntry, 0)
|
||||
|
||||
for _, file := range files {
|
||||
filename := file.Name()
|
||||
if !file.IsDir() && strings.HasSuffix(filename, s.FileSuffix) {
|
||||
entityFiles = append(entityFiles, file)
|
||||
}
|
||||
}
|
||||
|
||||
for _, file := range entityFiles {
|
||||
filename := file.Name()
|
||||
fullPath := filepath.Join(s.Dir, filename)
|
||||
|
||||
content, err := os.ReadFile(fullPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entity, err := s.MarshalFunc(s.Storer, fullPath, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.SetPath(entity, fullPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
return s.IndexDirFunc(s)
|
||||
}
|
||||
|
||||
func (s *FileStore[T, K]) GetPath(entity T) string {
|
||||
|
|
|
@ -13,5 +13,6 @@ func TestRunner(t *testing.T) {
|
|||
t,
|
||||
new(quizTestSuite),
|
||||
new(collectionTestSuite),
|
||||
new(participantTestSuite),
|
||||
)
|
||||
}
|
||||
|
|
51
store/file/participant.go
Normal file
51
store/file/participant.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package file
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"git.andreafazzi.eu/andrea/probo/models"
|
||||
"git.andreafazzi.eu/andrea/probo/store"
|
||||
)
|
||||
|
||||
type ParticipantFileStore = FileStore[*models.Participant, *store.Store[*models.Participant]]
|
||||
|
||||
func NewParticipantFileStore(config *FileStoreConfig[*models.Participant, *store.Store[*models.Participant]]) (*ParticipantFileStore, error) {
|
||||
return NewFileStore[*models.Participant](config, store.NewStore[*models.Participant]())
|
||||
}
|
||||
|
||||
func DefaultUnmarshalParticipantFunc(s *store.Store[*models.Participant], filepath string, content []byte) (*models.Participant, error) {
|
||||
participant := new(models.Participant)
|
||||
err := json.Unmarshal(content, &participant)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := s.Create(participant)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func DefaultMarshalParticipantFunc(s *store.Store[*models.Participant], filePath string, participant *models.Participant) error {
|
||||
jsonData, err := json.Marshal(participant)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.Write(jsonData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
44
store/file/participant_test.go
Normal file
44
store/file/participant_test.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
package file
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"git.andreafazzi.eu/andrea/probo/models"
|
||||
"git.andreafazzi.eu/andrea/probo/store"
|
||||
"github.com/remogatto/prettytest"
|
||||
)
|
||||
|
||||
type participantTestSuite struct {
|
||||
prettytest.Suite
|
||||
}
|
||||
|
||||
func (t *participantTestSuite) TestCreate() {
|
||||
filePathConfig := FilePathConfig{"testdata/participants", "participant", ".json"}
|
||||
pStore, err := NewParticipantFileStore(
|
||||
&FileStoreConfig[*models.Participant, *store.ParticipantStore]{
|
||||
FilePathConfig: filePathConfig,
|
||||
IndexDirFunc: DefaultIndexDirFunc[*models.Participant, *store.ParticipantStore],
|
||||
UnmarshalFunc: DefaultJSONUnmarshalFunc[*models.Participant, *store.ParticipantStore],
|
||||
MarshalFunc: DefaultJSONMarshalFunc[*models.Participant, *store.ParticipantStore],
|
||||
})
|
||||
t.Nil(err)
|
||||
|
||||
if !t.Failed() {
|
||||
p, err := pStore.Create(&models.Participant{
|
||||
Lastname: "Doe",
|
||||
Firstname: "John",
|
||||
Attributes: map[string]string{"class": "1 D LIN"},
|
||||
})
|
||||
|
||||
t.Nil(err)
|
||||
|
||||
defer os.Remove(pStore.GetPath(p))
|
||||
|
||||
if !t.Failed() {
|
||||
exists, err := os.Stat(pStore.GetPath(p))
|
||||
|
||||
t.Nil(err)
|
||||
t.Not(t.Nil(exists))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ import (
|
|||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -15,60 +14,59 @@ import (
|
|||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func NewQuizFileStore() (*FileStore[*models.Quiz, *store.QuizStore], error) {
|
||||
return NewFileStore[*models.Quiz, *store.QuizStore](
|
||||
store.NewQuizStore(),
|
||||
filepath.Join(BaseDir, QuizzesDir),
|
||||
"quiz",
|
||||
".md",
|
||||
func(s *store.QuizStore, filepath string, content []byte) (*models.Quiz, error) {
|
||||
quiz, meta, err := models.MarkdownToQuiz(string(content))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
type QuizFileStore = FileStore[*models.Quiz, *store.QuizStore]
|
||||
|
||||
var errQuizAlreadyPresent *store.ErrQuizAlreadyPresent
|
||||
func NewQuizFileStore(config *FileStoreConfig[*models.Quiz, *store.QuizStore]) (*QuizFileStore, error) {
|
||||
return NewFileStore[*models.Quiz, *store.QuizStore](config, store.NewQuizStore())
|
||||
}
|
||||
|
||||
q, err := s.Create(quiz)
|
||||
if err != nil && !errors.As(err, &errQuizAlreadyPresent) {
|
||||
return nil, err
|
||||
}
|
||||
func DefaultUnmarshalQuizFunc(s *store.QuizStore, filepath string, content []byte) (*models.Quiz, error) {
|
||||
quiz, meta, err := models.MarkdownToQuiz(string(content))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if meta == nil {
|
||||
writeQuizHeader(filepath, &models.Meta{
|
||||
ID: q.ID,
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
}
|
||||
var errQuizAlreadyPresent *store.ErrQuizAlreadyPresent
|
||||
|
||||
return q, nil
|
||||
},
|
||||
func(s *store.QuizStore, filePath string, quiz *models.Quiz) error {
|
||||
markdown, err := models.QuizToMarkdown(quiz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
q, err := s.Create(quiz)
|
||||
if err != nil && !errors.As(err, &errQuizAlreadyPresent) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
file, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if meta == nil {
|
||||
writeQuizHeader(filepath, &models.Meta{
|
||||
ID: q.ID,
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
return q, nil
|
||||
}
|
||||
|
||||
markdownWithMetaHeader, err := addMetaHeaderToMarkdown(markdown, &quiz.Meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func DefaultMarshalQuizFunc(s *store.QuizStore, filePath string, quiz *models.Quiz) error {
|
||||
markdown, err := models.QuizToMarkdown(quiz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = file.Write([]byte(markdownWithMetaHeader))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
defer file.Close()
|
||||
|
||||
markdownWithMetaHeader, err := addMetaHeaderToMarkdown(markdown, &quiz.Meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = file.Write([]byte(markdownWithMetaHeader))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeQuizHeader(path string, meta *models.Meta) (*models.Meta, error) {
|
||||
|
@ -214,3 +212,57 @@ func removeQuizHeader(path string) (*models.Meta, error) {
|
|||
|
||||
return &meta, nil
|
||||
}
|
||||
|
||||
// filepath.Join(BaseDir, QuizzesDir),
|
||||
// "quiz",
|
||||
// ".md",
|
||||
// DefaultIndexDirFunc,
|
||||
// nil,
|
||||
// func(s *store.QuizStore, filepath string, content []byte) (*models.Quiz, error) {
|
||||
// quiz, meta, err := models.MarkdownToQuiz(string(content))
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// var errQuizAlreadyPresent *store.ErrQuizAlreadyPresent
|
||||
|
||||
// q, err := s.Create(quiz)
|
||||
// if err != nil && !errors.As(err, &errQuizAlreadyPresent) {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// if meta == nil {
|
||||
// writeQuizHeader(filepath, &models.Meta{
|
||||
// ID: q.ID,
|
||||
// CreatedAt: time.Now(),
|
||||
// })
|
||||
// }
|
||||
|
||||
// return q, nil
|
||||
// },
|
||||
// func(s *store.QuizStore, filePath string, quiz *models.Quiz) error {
|
||||
// markdown, err := models.QuizToMarkdown(quiz)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// file, err := os.Create(filePath)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// defer file.Close()
|
||||
|
||||
// markdownWithMetaHeader, err := addMetaHeaderToMarkdown(markdown, &quiz.Meta)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// _, err = file.Write([]byte(markdownWithMetaHeader))
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// },
|
||||
// )
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"path/filepath"
|
||||
|
||||
"git.andreafazzi.eu/andrea/probo/models"
|
||||
"git.andreafazzi.eu/andrea/probo/store"
|
||||
"github.com/remogatto/prettytest"
|
||||
)
|
||||
|
||||
|
@ -13,12 +14,16 @@ type quizTestSuite struct {
|
|||
prettytest.Suite
|
||||
}
|
||||
|
||||
func (t *quizTestSuite) BeforeAll() {
|
||||
BaseDir = "testdata"
|
||||
}
|
||||
|
||||
func (t *quizTestSuite) TestReadAll() {
|
||||
store, err := NewQuizFileStore()
|
||||
filePathConfig := FilePathConfig{"testdata/quizzes", "quiz", ".md"}
|
||||
store, err := NewQuizFileStore(
|
||||
&FileStoreConfig[*models.Quiz, *store.QuizStore]{
|
||||
FilePathConfig: filePathConfig,
|
||||
IndexDirFunc: DefaultIndexDirFunc[*models.Quiz, *store.QuizStore],
|
||||
UnmarshalFunc: DefaultUnmarshalQuizFunc,
|
||||
MarshalFunc: DefaultMarshalQuizFunc,
|
||||
},
|
||||
)
|
||||
t.Nil(err)
|
||||
|
||||
if !t.Failed() {
|
||||
|
@ -36,7 +41,15 @@ func (t *quizTestSuite) TestReadAll() {
|
|||
}
|
||||
|
||||
func (t *quizTestSuite) TestCreate() {
|
||||
store, err := NewQuizFileStore()
|
||||
filePathConfig := FilePathConfig{"testdata/quizzes", "quiz", ".md"}
|
||||
store, err := NewQuizFileStore(
|
||||
&FileStoreConfig[*models.Quiz, *store.QuizStore]{
|
||||
FilePathConfig: filePathConfig,
|
||||
IndexDirFunc: DefaultIndexDirFunc[*models.Quiz, *store.QuizStore],
|
||||
UnmarshalFunc: DefaultUnmarshalQuizFunc,
|
||||
MarshalFunc: DefaultMarshalQuizFunc,
|
||||
},
|
||||
)
|
||||
t.Nil(err)
|
||||
|
||||
if !t.Failed() {
|
||||
|
@ -90,7 +103,15 @@ func (t *quizTestSuite) TestCreate() {
|
|||
}
|
||||
|
||||
func (t *quizTestSuite) TestDelete() {
|
||||
store, err := NewQuizFileStore()
|
||||
filePathConfig := FilePathConfig{"testdata/quizzes", "quiz", ".md"}
|
||||
store, err := NewQuizFileStore(
|
||||
&FileStoreConfig[*models.Quiz, *store.QuizStore]{
|
||||
FilePathConfig: filePathConfig,
|
||||
IndexDirFunc: DefaultIndexDirFunc[*models.Quiz, *store.QuizStore],
|
||||
UnmarshalFunc: DefaultUnmarshalQuizFunc,
|
||||
MarshalFunc: DefaultMarshalQuizFunc,
|
||||
},
|
||||
)
|
||||
t.Nil(err)
|
||||
|
||||
if !t.Failed() {
|
||||
|
@ -121,7 +142,15 @@ func (t *quizTestSuite) TestDelete() {
|
|||
}
|
||||
|
||||
func (t *quizTestSuite) TestUpdate() {
|
||||
store, err := NewQuizFileStore()
|
||||
filePathConfig := FilePathConfig{"testdata/quizzes", "quiz", ".md"}
|
||||
store, err := NewQuizFileStore(
|
||||
&FileStoreConfig[*models.Quiz, *store.QuizStore]{
|
||||
FilePathConfig: filePathConfig,
|
||||
IndexDirFunc: DefaultIndexDirFunc[*models.Quiz, *store.QuizStore],
|
||||
UnmarshalFunc: DefaultUnmarshalQuizFunc,
|
||||
MarshalFunc: DefaultMarshalQuizFunc,
|
||||
},
|
||||
)
|
||||
t.Nil(err)
|
||||
|
||||
if !t.Failed() {
|
||||
|
@ -161,7 +190,15 @@ func (t *quizTestSuite) TestUpdate() {
|
|||
}
|
||||
|
||||
func (t *quizTestSuite) TestAutowriteHeader() {
|
||||
store, err := NewQuizFileStore()
|
||||
filePathConfig := FilePathConfig{"testdata/quizzes", "quiz", ".md"}
|
||||
store, err := NewQuizFileStore(
|
||||
&FileStoreConfig[*models.Quiz, *store.QuizStore]{
|
||||
FilePathConfig: filePathConfig,
|
||||
IndexDirFunc: DefaultIndexDirFunc[*models.Quiz, *store.QuizStore],
|
||||
UnmarshalFunc: DefaultUnmarshalQuizFunc,
|
||||
MarshalFunc: DefaultMarshalQuizFunc,
|
||||
},
|
||||
)
|
||||
t.Nil(err)
|
||||
|
||||
if !t.Failed() {
|
||||
|
|
4
store/file/testdata/quizzes/quiz_5.md
vendored
4
store/file/testdata/quizzes/quiz_5.md
vendored
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
id: 57ebde24-51a0-421e-a641-4a5c198473c3
|
||||
created_at: 2023-11-13T21:02:55.786449849+01:00
|
||||
id: edadaa90-802f-4a74-83cc-4b4cb5192f28
|
||||
created_at: 2023-11-17T16:50:38.479350601+01:00
|
||||
updated_at: 0001-01-01T00:00:00Z
|
||||
---
|
||||
This quiz is initially without metadata.
|
||||
|
|
5
store/participant.go
Normal file
5
store/participant.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
package store
|
||||
|
||||
import "git.andreafazzi.eu/andrea/probo/models"
|
||||
|
||||
type ParticipantStore = Store[*models.Participant]
|
52
store/participant_test.go
Normal file
52
store/participant_test.go
Normal file
|
@ -0,0 +1,52 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"git.andreafazzi.eu/andrea/probo/models"
|
||||
"github.com/remogatto/prettytest"
|
||||
)
|
||||
|
||||
type participantTestSuite struct {
|
||||
prettytest.Suite
|
||||
}
|
||||
|
||||
func (t *participantTestSuite) TestCreate() {
|
||||
store := NewStore[*models.Participant]()
|
||||
|
||||
p_1, err := store.Create(&models.Participant{
|
||||
Lastname: "Doe",
|
||||
Firstname: "John",
|
||||
})
|
||||
|
||||
t.Nil(err)
|
||||
|
||||
p_2, err := store.Create(&models.Participant{
|
||||
Lastname: "Doe",
|
||||
Firstname: "John",
|
||||
Attributes: map[string]string{"class": "1 D LIN"},
|
||||
})
|
||||
|
||||
t.Nil(err)
|
||||
|
||||
t.False(p_1.GetHash() == p_2.GetHash())
|
||||
}
|
||||
|
||||
func (t *participantTestSuite) TestUpdate() {
|
||||
store := NewStore[*models.Participant]()
|
||||
|
||||
p, err := store.Create(&models.Participant{
|
||||
Lastname: "Doe",
|
||||
Firstname: "John",
|
||||
})
|
||||
|
||||
t.Nil(err)
|
||||
|
||||
updatedP, err := store.Update(&models.Participant{
|
||||
Lastname: "Doe",
|
||||
Firstname: "John",
|
||||
Attributes: map[string]string{"class": "1 D LIN"},
|
||||
}, p.ID)
|
||||
|
||||
t.Nil(err)
|
||||
|
||||
t.False(p.GetHash() == updatedP.GetHash())
|
||||
}
|
|
@ -11,5 +11,6 @@ func TestRunner(t *testing.T) {
|
|||
t,
|
||||
new(quizTestSuite),
|
||||
new(collectionTestSuite),
|
||||
new(participantTestSuite),
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue