fx build fix
Some checks failed
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/fx/1 Pipeline failed
ci/woodpecker/push/nats Pipeline was successful
ci/woodpecker/push/fx/2 Pipeline failed

This commit is contained in:
Stephan D
2025-11-08 00:40:01 +01:00
parent 49b86efecb
commit d367dddbbd
98 changed files with 3983 additions and 5063 deletions

View File

@@ -1,69 +0,0 @@
package indexable
import (
"context"
"github.com/tech/sendico/pkg/db/repository"
"github.com/tech/sendico/pkg/db/repository/builder"
"github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/model"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// Example usage of the generic IndexableDB with different types
// Example 1: Using with Project
func ExampleProjectIndexableDB(repo repository.Repository, logger mlogger.Logger, organizationRef primitive.ObjectID) {
// Define helper functions for Project
createEmpty := func() *model.Project {
return &model.Project{}
}
getIndexable := func(p *model.Project) *model.Indexable {
return &p.Indexable
}
// Create generic IndexableDB for Project
projectDB := NewIndexableDB(repo, logger, createEmpty, getIndexable)
// Use with organization filter
orgFilter := repository.OrgFilter(organizationRef)
projectDB.Reorder(context.Background(), primitive.NewObjectID(), 2, orgFilter)
}
// Example 3: Using with Task
func ExampleTaskIndexableDB(repo repository.Repository, logger mlogger.Logger, statusRef primitive.ObjectID) {
// Define helper functions for Task
createEmpty := func() *model.Task {
return &model.Task{}
}
getIndexable := func(t *model.Task) *model.Indexable {
return &t.Indexable
}
// Create generic IndexableDB for Task
taskDB := NewIndexableDB(repo, logger, createEmpty, getIndexable)
// Use with status filter
statusFilter := repository.Query().Comparison(repository.Field("statusRef"), builder.Eq, statusRef)
taskDB.Reorder(context.Background(), primitive.NewObjectID(), 3, statusFilter)
}
// Example 5: Using without any filter (global reordering)
func ExampleGlobalIndexableDB(repo repository.Repository, logger mlogger.Logger) {
// Define helper functions for any Indexable type
createEmpty := func() *model.Project {
return &model.Project{}
}
getIndexable := func(p *model.Project) *model.Indexable {
return &p.Indexable
}
// Create generic IndexableDB without filters
globalDB := NewIndexableDB(repo, logger, createEmpty, getIndexable)
// Use without any filter - reorders all items globally
globalDB.Reorder(context.Background(), primitive.NewObjectID(), 5, repository.Query())
}

View File

@@ -1,314 +0,0 @@
//go:build integration
// +build integration
package indexable
import (
"context"
"testing"
"time"
"github.com/tech/sendico/pkg/db/repository"
"github.com/tech/sendico/pkg/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/mongodb"
"github.com/testcontainers/testcontainers-go/wait"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.uber.org/zap"
)
func setupTestDB(t *testing.T) (repository.Repository, func()) {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
mongoContainer, err := mongodb.Run(ctx,
"mongo:latest",
mongodb.WithUsername("root"),
mongodb.WithPassword("password"),
testcontainers.WithWaitStrategy(wait.ForLog("Waiting for connections")),
)
require.NoError(t, err, "failed to start MongoDB container")
mongoURI, err := mongoContainer.ConnectionString(ctx)
require.NoError(t, err, "failed to get MongoDB connection string")
clientOptions := options.Client().ApplyURI(mongoURI)
client, err := mongo.Connect(ctx, clientOptions)
require.NoError(t, err, "failed to connect to MongoDB")
db := client.Database("testdb")
repo := repository.CreateMongoRepository(db, "projects")
cleanup := func() {
disconnect(ctx, t, client)
terminate(ctx, t, mongoContainer)
}
return repo, cleanup
}
func disconnect(ctx context.Context, t *testing.T, client *mongo.Client) {
if err := client.Disconnect(ctx); err != nil {
t.Logf("failed to disconnect from MongoDB: %v", err)
}
}
func terminate(ctx context.Context, t *testing.T, container testcontainers.Container) {
if err := container.Terminate(ctx); err != nil {
t.Logf("failed to terminate MongoDB container: %v", err)
}
}
func TestIndexableDB_Reorder(t *testing.T) {
repo, cleanup := setupTestDB(t)
defer cleanup()
ctx := context.Background()
organizationRef := primitive.NewObjectID()
logger := zap.NewNop()
// Create test projects with different indices
projects := []*model.Project{
{
ProjectBase: model.ProjectBase{
PermissionBound: model.PermissionBound{
OrganizationBoundBase: model.OrganizationBoundBase{
OrganizationRef: organizationRef,
},
},
Describable: model.Describable{Name: "Project A"},
Indexable: model.Indexable{Index: 0},
Mnemonic: "A",
State: model.ProjectStateActive,
},
},
{
ProjectBase: model.ProjectBase{
PermissionBound: model.PermissionBound{
OrganizationBoundBase: model.OrganizationBoundBase{
OrganizationRef: organizationRef,
},
},
Describable: model.Describable{Name: "Project B"},
Indexable: model.Indexable{Index: 1},
Mnemonic: "B",
State: model.ProjectStateActive,
},
},
{
ProjectBase: model.ProjectBase{
PermissionBound: model.PermissionBound{
OrganizationBoundBase: model.OrganizationBoundBase{
OrganizationRef: organizationRef,
},
},
Describable: model.Describable{Name: "Project C"},
Indexable: model.Indexable{Index: 2},
Mnemonic: "C",
State: model.ProjectStateActive,
},
},
{
ProjectBase: model.ProjectBase{
PermissionBound: model.PermissionBound{
OrganizationBoundBase: model.OrganizationBoundBase{
OrganizationRef: organizationRef,
},
},
Describable: model.Describable{Name: "Project D"},
Indexable: model.Indexable{Index: 3},
Mnemonic: "D",
State: model.ProjectStateActive,
},
},
}
// Insert projects into database
for _, project := range projects {
project.ID = primitive.NewObjectID()
err := repo.Insert(ctx, project, nil)
require.NoError(t, err)
}
// Create helper functions for Project type
createEmpty := func() *model.Project {
return &model.Project{}
}
getIndexable := func(p *model.Project) *model.Indexable {
return &p.Indexable
}
indexableDB := NewIndexableDB(repo, logger, createEmpty, getIndexable)
t.Run("Reorder_NoChange", func(t *testing.T) {
// Test reordering to the same position (should be no-op)
err := indexableDB.Reorder(ctx, projects[1].ID, 1, repository.Query())
require.NoError(t, err)
// Verify indices haven't changed
var result model.Project
err = repo.Get(ctx, projects[0].ID, &result)
require.NoError(t, err)
assert.Equal(t, 0, result.Index)
err = repo.Get(ctx, projects[1].ID, &result)
require.NoError(t, err)
assert.Equal(t, 1, result.Index)
})
t.Run("Reorder_MoveDown", func(t *testing.T) {
// Move Project A (index 0) to index 2
err := indexableDB.Reorder(ctx, projects[0].ID, 2, repository.Query())
require.NoError(t, err)
// Verify the reordering:
// Project A should now be at index 2
// Project B should be at index 0
// Project C should be at index 1
// Project D should remain at index 3
var result model.Project
// Check Project A (moved to index 2)
err = repo.Get(ctx, projects[0].ID, &result)
require.NoError(t, err)
assert.Equal(t, 2, result.Index)
// Check Project B (shifted to index 0)
err = repo.Get(ctx, projects[1].ID, &result)
require.NoError(t, err)
assert.Equal(t, 0, result.Index)
// Check Project C (shifted to index 1)
err = repo.Get(ctx, projects[2].ID, &result)
require.NoError(t, err)
assert.Equal(t, 1, result.Index)
// Check Project D (unchanged)
err = repo.Get(ctx, projects[3].ID, &result)
require.NoError(t, err)
assert.Equal(t, 3, result.Index)
})
t.Run("Reorder_MoveUp", func(t *testing.T) {
// Reset indices for this test
for i, project := range projects {
project.Index = i
err := repo.Update(ctx, project)
require.NoError(t, err)
}
// Move Project C (index 2) to index 0
err := indexableDB.Reorder(ctx, projects[2].ID, 0, repository.Query())
require.NoError(t, err)
// Verify the reordering:
// Project C should now be at index 0
// Project A should be at index 1
// Project B should be at index 2
// Project D should remain at index 3
var result model.Project
// Check Project C (moved to index 0)
err = repo.Get(ctx, projects[2].ID, &result)
require.NoError(t, err)
assert.Equal(t, 0, result.Index)
// Check Project A (shifted to index 1)
err = repo.Get(ctx, projects[0].ID, &result)
require.NoError(t, err)
assert.Equal(t, 1, result.Index)
// Check Project B (shifted to index 2)
err = repo.Get(ctx, projects[1].ID, &result)
require.NoError(t, err)
assert.Equal(t, 2, result.Index)
// Check Project D (unchanged)
err = repo.Get(ctx, projects[3].ID, &result)
require.NoError(t, err)
assert.Equal(t, 3, result.Index)
})
t.Run("Reorder_WithFilter", func(t *testing.T) {
// Reset indices for this test
for i, project := range projects {
project.Index = i
err := repo.Update(ctx, project)
require.NoError(t, err)
}
// Test reordering with organization filter
orgFilter := repository.OrgFilter(organizationRef)
err := indexableDB.Reorder(ctx, projects[0].ID, 2, orgFilter)
require.NoError(t, err)
// Verify the reordering worked with filter
var result model.Project
err = repo.Get(ctx, projects[0].ID, &result)
require.NoError(t, err)
assert.Equal(t, 2, result.Index)
})
}
func TestIndexableDB_EdgeCases(t *testing.T) {
repo, cleanup := setupTestDB(t)
defer cleanup()
ctx := context.Background()
organizationRef := primitive.NewObjectID()
logger := zap.NewNop()
// Create a single project for edge case testing
project := &model.Project{
ProjectBase: model.ProjectBase{
PermissionBound: model.PermissionBound{
OrganizationBoundBase: model.OrganizationBoundBase{
OrganizationRef: organizationRef,
},
},
Describable: model.Describable{Name: "Test Project"},
Indexable: model.Indexable{Index: 0},
Mnemonic: "TEST",
State: model.ProjectStateActive,
},
}
project.ID = primitive.NewObjectID()
err := repo.Insert(ctx, project, nil)
require.NoError(t, err)
// Create helper functions for Project type
createEmpty := func() *model.Project {
return &model.Project{}
}
getIndexable := func(p *model.Project) *model.Indexable {
return &p.Indexable
}
indexableDB := NewIndexableDB(repo, logger, createEmpty, getIndexable)
t.Run("Reorder_SingleItem", func(t *testing.T) {
// Test reordering a single item (should work but have no effect)
err := indexableDB.Reorder(ctx, project.ID, 0, repository.Query())
require.NoError(t, err)
var result model.Project
err = repo.Get(ctx, project.ID, &result)
require.NoError(t, err)
assert.Equal(t, 0, result.Index)
})
t.Run("Reorder_InvalidObjectID", func(t *testing.T) {
// Test reordering with an invalid object ID
invalidID := primitive.NewObjectID()
err := indexableDB.Reorder(ctx, invalidID, 1, repository.Query())
require.Error(t, err) // Should fail because object doesn't exist
})
}

View File

@@ -116,10 +116,6 @@ func IndexFilter(index int) builder.Query {
return Query().Filter(IndexField(), index)
}
func TagRefsField() builder.Field {
return Field(model.TagRefsField)
}
func IndexOpFilter(index int, operation builder.MongoOperation) builder.Query {
return Query().Comparison(IndexField(), operation, index)
}

View File

@@ -1,16 +0,0 @@
package tag
import (
"context"
"github.com/tech/sendico/pkg/auth"
"github.com/tech/sendico/pkg/model"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type DB interface {
auth.ProtectedDB[*model.Tag]
List(ctx context.Context, accountRef, organizationRef, parentRef primitive.ObjectID, cursor *model.ViewCursor) ([]model.Tag, error)
All(ctx context.Context, organizationRef primitive.ObjectID, limit, offset *int64) ([]model.Tag, error)
SetArchived(ctx context.Context, accountRef, organizationRef, tagRef primitive.ObjectID, archived, cascade bool) error
}