From 9301790408c5c369fbf215d1a2c2be283a2563a1 Mon Sep 17 00:00:00 2001 From: Niko Abeler Date: Sat, 8 Jul 2023 11:08:55 +0200 Subject: [PATCH] binary service --- app/binary_service.go | 22 ++++++++++++ app/entry_register.go | 9 ++++- app/repository/interfaces.go | 5 +++ cmd/owl/main.go | 9 +++-- domain/model/binary_file.go | 7 ++++ domain/model/image_entry.go | 4 +-- infra/binary_file_repository.go | 51 ++++++++++++++++++++++++++++ infra/binary_file_repository_test.go | 47 +++++++++++++++++++++++++ infra/entry_repository_test.go | 36 ++++++++++++++++++++ web/app.go | 16 ++++++--- 10 files changed, 195 insertions(+), 11 deletions(-) create mode 100644 app/binary_service.go create mode 100644 domain/model/binary_file.go create mode 100644 infra/binary_file_repository.go create mode 100644 infra/binary_file_repository_test.go diff --git a/app/binary_service.go b/app/binary_service.go new file mode 100644 index 0000000..4ef64b7 --- /dev/null +++ b/app/binary_service.go @@ -0,0 +1,22 @@ +package app + +import ( + "owl-blogs/app/repository" + "owl-blogs/domain/model" +) + +type BinaryService struct { + repo repository.BinaryRepository +} + +func NewBinaryFileService(repo repository.BinaryRepository) *BinaryService { + return &BinaryService{repo: repo} +} + +func (s *BinaryService) Create(name string, file []byte) (*model.BinaryFile, error) { + return s.repo.Create(name, file) +} + +func (s *BinaryService) FindById(id string) (*model.BinaryFile, error) { + return s.repo.FindById(id) +} diff --git a/app/entry_register.go b/app/entry_register.go index a6dd53b..90c4e7e 100644 --- a/app/entry_register.go +++ b/app/entry_register.go @@ -47,5 +47,12 @@ func (r *EntryTypeRegistry) Type(name string) (model.Entry, error) { if _, ok := r.types[name]; !ok { return nil, errors.New("entry type not registered") } - return r.types[name], nil + + val := reflect.ValueOf(r.types[name]) + if val.Kind() == reflect.Ptr { + val = reflect.Indirect(val) + } + newEntry := reflect.New(val.Type()).Interface().(model.Entry) + + return newEntry, nil } diff --git a/app/repository/interfaces.go b/app/repository/interfaces.go index 7a9cf2b..8eed3dc 100644 --- a/app/repository/interfaces.go +++ b/app/repository/interfaces.go @@ -12,3 +12,8 @@ type EntryRepository interface { FindById(id string) (model.Entry, error) FindAll(types *[]string) ([]model.Entry, error) } + +type BinaryRepository interface { + Create(name string, data []byte) (*model.BinaryFile, error) + FindById(id string) (*model.BinaryFile, error) +} diff --git a/cmd/owl/main.go b/cmd/owl/main.go index 815b8cb..e4c3aeb 100644 --- a/cmd/owl/main.go +++ b/cmd/owl/main.go @@ -14,9 +14,12 @@ func App(db infra.Database) *web.WebApp { registry.Register(&model.ImageEntry{}) - repo := infra.NewEntryRepository(db, registry) - entryService := app.NewEntryService(repo) - return web.NewWebApp(entryService, registry) + entryRepo := infra.NewEntryRepository(db, registry) + binRepo := infra.NewBinaryFileRepo(db) + + entryService := app.NewEntryService(entryRepo) + binaryService := app.NewBinaryFileService(binRepo) + return web.NewWebApp(entryService, registry, binaryService) } diff --git a/domain/model/binary_file.go b/domain/model/binary_file.go new file mode 100644 index 0000000..0c9da91 --- /dev/null +++ b/domain/model/binary_file.go @@ -0,0 +1,7 @@ +package model + +type BinaryFile struct { + Id string + Name string + Data []byte +} diff --git a/domain/model/image_entry.go b/domain/model/image_entry.go index 195be9e..9b92545 100644 --- a/domain/model/image_entry.go +++ b/domain/model/image_entry.go @@ -9,8 +9,8 @@ type ImageEntry struct { } type ImageEntryMetaData struct { - ImagePath string `owl:"inputType=file"` - Content string `owl:"inputType=text widget=textarea"` + ImageId string `owl:"inputType=file"` + Content string `owl:"inputType=text widget=textarea"` } func (e *ImageEntry) ID() string { diff --git a/infra/binary_file_repository.go b/infra/binary_file_repository.go new file mode 100644 index 0000000..d01ce42 --- /dev/null +++ b/infra/binary_file_repository.go @@ -0,0 +1,51 @@ +package infra + +import ( + "owl-blogs/domain/model" + + "github.com/google/uuid" + "github.com/jmoiron/sqlx" +) + +type sqlBinaryFile struct { + Id string `db:"id"` + Name string `db:"name"` + Data []byte `db:"data"` +} + +type DefaultBinaryFileRepo struct { + db *sqlx.DB +} + +func NewBinaryFileRepo(db Database) *DefaultBinaryFileRepo { + sqlxdb := db.Get() + + // Create table if not exists + sqlxdb.MustExec(` + CREATE TABLE IF NOT EXISTS binary_files ( + id VARCHAR(255) PRIMARY KEY, + name VARCHAR(255) NOT NULL, + data BLOB NOT NULL + ); + `) + + return &DefaultBinaryFileRepo{db: sqlxdb} +} + +func (repo *DefaultBinaryFileRepo) Create(name string, data []byte) (*model.BinaryFile, error) { + id := uuid.New().String() + _, err := repo.db.Exec("INSERT INTO binary_files (id, name, data) VALUES (?, ?, ?)", id, name, data) + if err != nil { + return nil, err + } + return &model.BinaryFile{Id: id, Name: name, Data: data}, nil +} + +func (repo *DefaultBinaryFileRepo) FindById(id string) (*model.BinaryFile, error) { + var sqlFile sqlBinaryFile + err := repo.db.Get(&sqlFile, "SELECT * FROM binary_files WHERE id = ?", id) + if err != nil { + return nil, err + } + return &model.BinaryFile{Id: sqlFile.Id, Name: sqlFile.Name, Data: sqlFile.Data}, nil +} diff --git a/infra/binary_file_repository_test.go b/infra/binary_file_repository_test.go new file mode 100644 index 0000000..d4e7853 --- /dev/null +++ b/infra/binary_file_repository_test.go @@ -0,0 +1,47 @@ +package infra_test + +import ( + "owl-blogs/app/repository" + "owl-blogs/infra" + "owl-blogs/test" + "testing" + + "github.com/stretchr/testify/require" +) + +func setupBinaryRepo() repository.BinaryRepository { + db := test.NewMockDb() + repo := infra.NewBinaryFileRepo(db) + return repo +} + +func TestBinaryRepoCreate(t *testing.T) { + repo := setupBinaryRepo() + + file, err := repo.Create("name", []byte("😀 😃 😄 😁")) + require.NoError(t, err) + + file, err = repo.FindById(file.Id) + require.NoError(t, err) + require.Equal(t, file.Name, "name") + require.Equal(t, file.Data, []byte("😀 😃 😄 😁")) +} + +func TestBinaryRepoNoSideEffect(t *testing.T) { + repo := setupBinaryRepo() + + file, err := repo.Create("name1", []byte("111")) + require.NoError(t, err) + + file2, err := repo.Create("name2", []byte("222")) + require.NoError(t, err) + + file, err = repo.FindById(file.Id) + require.NoError(t, err) + file2, err = repo.FindById(file2.Id) + require.NoError(t, err) + require.Equal(t, file.Name, "name1") + require.Equal(t, file.Data, []byte("111")) + require.Equal(t, file2.Name, "name2") + require.Equal(t, file2.Data, []byte("222")) +} diff --git a/infra/entry_repository_test.go b/infra/entry_repository_test.go index 55bd9cf..949c9b2 100644 --- a/infra/entry_repository_test.go +++ b/infra/entry_repository_test.go @@ -131,3 +131,39 @@ func TestRepoUpdate(t *testing.T) { require.Equal(t, meta.Number, meta2.Number) require.Equal(t, meta.Date.Unix(), meta2.Date.Unix()) } + +func TestRepoNoSideEffect(t *testing.T) { + repo := setupRepo() + + entry1 := &test.MockEntry{} + now1 := time.Now() + err := repo.Create(entry1, &now1, &test.MockEntryMetaData{ + Str: "1", + Number: 1, + Date: now1, + }) + require.NoError(t, err) + + entry2 := &test.MockEntry{} + now2 := time.Now() + err = repo.Create(entry2, &now2, &test.MockEntryMetaData{ + Str: "2", + Number: 2, + Date: now2, + }) + require.NoError(t, err) + + r1, err := repo.FindById(entry1.ID()) + require.NoError(t, err) + r2, err := repo.FindById(entry2.ID()) + require.NoError(t, err) + + require.Equal(t, r1.MetaData().(*test.MockEntryMetaData).Str, "1") + require.Equal(t, r1.MetaData().(*test.MockEntryMetaData).Number, 1) + require.Equal(t, r1.MetaData().(*test.MockEntryMetaData).Date.Unix(), now1.Unix()) + + require.Equal(t, r2.MetaData().(*test.MockEntryMetaData).Str, "2") + require.Equal(t, r2.MetaData().(*test.MockEntryMetaData).Number, 2) + require.Equal(t, r2.MetaData().(*test.MockEntryMetaData).Date.Unix(), now2.Unix()) + +} diff --git a/web/app.go b/web/app.go index 7b5eb6a..1532664 100644 --- a/web/app.go +++ b/web/app.go @@ -7,12 +7,13 @@ import ( ) type WebApp struct { - FiberApp *fiber.App - EntryService *app.EntryService - Registry *app.EntryTypeRegistry + FiberApp *fiber.App + EntryService *app.EntryService + BinaryService *app.BinaryService + Registry *app.EntryTypeRegistry } -func NewWebApp(entryService *app.EntryService, typeRegistry *app.EntryTypeRegistry) *WebApp { +func NewWebApp(entryService *app.EntryService, typeRegistry *app.EntryTypeRegistry, binService *app.BinaryService) *WebApp { app := fiber.New() indexHandler := NewIndexHandler(entryService) @@ -51,7 +52,12 @@ func NewWebApp(entryService *app.EntryService, typeRegistry *app.EntryTypeRegist // app.Post("/auth/token/", userAuthTokenHandler(repo)) // app.Get("/.well-known/oauth-authorization-server", userAuthMetadataHandler(repo)) // app.NotFound = http.HandlerFunc(notFoundHandler(repo)) - return &WebApp{FiberApp: app, EntryService: entryService, Registry: typeRegistry} + return &WebApp{ + FiberApp: app, + EntryService: entryService, + Registry: typeRegistry, + BinaryService: binService, + } } func (w *WebApp) Run() {