diff --git a/Taskfile.yml b/Taskfile.yml index 4a5c4ce..86e777a 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -13,6 +13,7 @@ tasks: - '**/*.go' cmds: - go fmt ./... + - go mod tidy vet: desc: Vet go code diff --git a/app.go b/app.go index 6b33a8c..9ffe16d 100644 --- a/app.go +++ b/app.go @@ -6,6 +6,7 @@ import ( "time" "codeberg.org/danjones000/lenore/config" + _ "codeberg.org/danjones000/lenore/imports" "codeberg.org/danjones000/lenore/store" vocab "github.com/go-ap/activitypub" "github.com/go-ap/client" diff --git a/cmd/lenore/main.go b/cmd/lenore/main.go index 0f7d6be..012cada 100644 --- a/cmd/lenore/main.go +++ b/cmd/lenore/main.go @@ -7,14 +7,19 @@ import ( "codeberg.org/danjones000/lenore" "codeberg.org/danjones000/lenore/config" - "github.com/go-ap/storage-sqlite" + "codeberg.org/danjones000/lenore/store" ) func main() { - conf := config.Config{BaseURL: "http://localhost:4523/"} - sqlConf := sqlite.Config{Path: "storage"} + conf := config.Config{ + BaseURL: "http://localhost:4523/", + Conn: config.ConnSettings{ + Store: "sqlite", + DSN: "storage", + }, + } - db, err := sqlite.New(sqlConf) + db, err := store.MakeStore(conf.Conn.Store, conf) if err != nil { panic(err) } diff --git a/config/config.go b/config/config.go index a0125fb..953ddce 100644 --- a/config/config.go +++ b/config/config.go @@ -4,6 +4,13 @@ type Config struct { Name string Env Env BaseURL string + Conn ConnSettings +} + +type ConnSettings struct { + Store string + DSN string + AdditionalSettings map[string]any } func (c Config) Environment() Env { diff --git a/go.mod b/go.mod index 418bc6a..2b3ca76 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,10 @@ require ( github.com/go-ap/activitypub v0.0.0-20240910141749-b4b8c8aa484c github.com/go-ap/client v0.0.0-20240910141951-13a4f3c4fd53 github.com/go-ap/fedbox v0.0.0-20240910163620-7bcedb2eb399 + github.com/go-ap/filters v0.0.0-20240910141936-c8f68cdf2bc9 github.com/go-ap/processing v0.0.0-20240910151355-8284a5ce9c22 github.com/go-ap/storage-sqlite v0.0.0-20240910151457-20fa80d963aa + github.com/openshift/osin v1.0.2-0.20220317075346-0f4d38c6e53f github.com/stretchr/testify v1.9.0 ) @@ -20,7 +22,6 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/go-ap/cache v0.0.0-20240910141827-94f8ac1a9133 // indirect github.com/go-ap/errors v0.0.0-20240910140019-1e9d33cc1568 // indirect - github.com/go-ap/filters v0.0.0-20240910141936-c8f68cdf2bc9 // indirect github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 // indirect github.com/go-chi/chi/v5 v5.1.0 // indirect github.com/go-fed/httpsig v1.1.0 // indirect @@ -31,7 +32,6 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.23 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect - github.com/openshift/osin v1.0.2-0.20220317075346-0f4d38c6e53f // indirect github.com/pborman/uuid v1.2.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect diff --git a/imports/.gitignore b/imports/.gitignore new file mode 100644 index 0000000..6a51c08 --- /dev/null +++ b/imports/.gitignore @@ -0,0 +1,3 @@ +* +!default.go +!.gitignore diff --git a/imports/default.go b/imports/default.go new file mode 100644 index 0000000..a757ab4 --- /dev/null +++ b/imports/default.go @@ -0,0 +1,5 @@ +package imports + +import ( + _ "codeberg.org/danjones000/lenore/store/sqlite" +) diff --git a/internal/testmocks/store.go b/internal/testmocks/store.go index a7ae185..d4a6e54 100644 --- a/internal/testmocks/store.go +++ b/internal/testmocks/store.go @@ -1,13 +1,17 @@ package testmocks import ( - "codeberg.org/danjones000/lenore/store" + "codeberg.org/danjones000/lenore/config" vocab "github.com/go-ap/activitypub" "github.com/go-ap/filters" ) type st struct{} +func (s *st) Bootstrap(config.Config) error { + return nil +} + func (s *st) Load(iri vocab.IRI, filters ...filters.Check) (vocab.Item, error) { i := vocab.ActorNew(iri, vocab.ActorType) return i, nil @@ -36,6 +40,6 @@ func (s *st) RemoveFrom(col vocab.IRI, it vocab.Item) error { func (s *st) Close() { } -func GetStore() store.Store { +func GetStore() *st { return &st{} } diff --git a/store/factory.go b/store/factory.go new file mode 100644 index 0000000..fbcc5c0 --- /dev/null +++ b/store/factory.go @@ -0,0 +1,34 @@ +package store + +import ( + "errors" + "fmt" + + "codeberg.org/danjones000/lenore/config" +) + +var ErrNoFactory = errors.New("unknown factory") + +type StoreFactory func(config.Config) (Store, error) + +var factories map[string]StoreFactory + +func init() { + factories = make(map[string]StoreFactory) +} + +func AddFactory(name string, f StoreFactory) { + factories[name] = f +} + +func GetFactory(name string) StoreFactory { + return factories[name] +} + +func MakeStore(name string, conf config.Config) (Store, error) { + f, ok := factories[name] + if !ok { + return nil, fmt.Errorf("%w: %s", ErrNoFactory, name) + } + return f(conf) +} diff --git a/store/factory_test.go b/store/factory_test.go new file mode 100644 index 0000000..38568ac --- /dev/null +++ b/store/factory_test.go @@ -0,0 +1,47 @@ +package store + +import ( + "testing" + + "codeberg.org/danjones000/lenore/config" + "codeberg.org/danjones000/lenore/internal/testmocks" + "github.com/stretchr/testify/assert" +) + +var f StoreFactory = func(config.Config) (Store, error) { + return testmocks.GetStore(), nil +} + +func TestAddFactory(t *testing.T) { + AddFactory("mock", f) + defer delete(factories, "mock") + _, ok := factories["mock"] + assert.True(t, ok) +} + +func TestGetFactoryNil(t *testing.T) { + f := GetFactory("mock") + assert.Nil(t, f) +} + +func TestGetFactoryNotNil(t *testing.T) { + AddFactory("mock", f) + defer delete(factories, "mock") + f := GetFactory("mock") + assert.NotNil(t, f) +} + +func TestMakeStoreError(t *testing.T) { + s, e := MakeStore("mock", config.Config{}) + assert.Nil(t, s) + assert.ErrorIs(t, e, ErrNoFactory) + assert.ErrorContains(t, e, ErrNoFactory.Error()+": mock") +} + +func TestMakeStoreNoError(t *testing.T) { + AddFactory("mock", f) + defer delete(factories, "mock") + s, e := MakeStore("mock", config.Config{}) + assert.NotNil(t, s) + assert.NoError(t, e) +} diff --git a/store/sqlite/repo.go b/store/sqlite/repo.go new file mode 100644 index 0000000..87a43e3 --- /dev/null +++ b/store/sqlite/repo.go @@ -0,0 +1,29 @@ +package sqlite + +import ( + "codeberg.org/danjones000/lenore/config" + "codeberg.org/danjones000/lenore/store" + "github.com/go-ap/storage-sqlite" +) + +func init() { + store.AddFactory("sqlite", MakeStore) +} + +func MakeStore(conf config.Config) (store.Store, error) { + sqlConf := sqlite.Config{Path: conf.Conn.DSN} + db, err := sqlite.New(sqlConf) + if err != nil { + return nil, err + } + return Repo{db}, nil +} + +type Repo struct { + store.PartStore +} + +func (r Repo) Bootstrap(config.Config) error { + // @todo + return nil +} diff --git a/store/store.go b/store/store.go index 6b5336b..de38416 100644 --- a/store/store.go +++ b/store/store.go @@ -1,6 +1,7 @@ package store import ( + "codeberg.org/danjones000/lenore/config" st "github.com/go-ap/fedbox/storage" proc "github.com/go-ap/processing" "github.com/openshift/osin" @@ -21,7 +22,19 @@ type ClientLister interface { GetClient(id string) (osin.Client, error) } +type Bootstrapper interface { + // Bootstrap should initialize the data store so that it can be used. + // This will be called every time the application starts, so it MUST be idempotent and doesn't delete existing data. + // An option is to run migrations in this method. + Bootstrap(config.Config) error +} + type Store interface { + Bootstrapper + PartStore +} + +type PartStore interface { ClientSaver ClientLister proc.Store