service backend
This commit is contained in:
20
api/pkg/db/internal/mongo/policiesdb/all.go
Normal file
20
api/pkg/db/internal/mongo/policiesdb/all.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package policiesdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tech/sendico/pkg/db/repository"
|
||||
"github.com/tech/sendico/pkg/db/storable"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
mutil "github.com/tech/sendico/pkg/mutil/db"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func (db *PoliciesDB) All(ctx context.Context, organizationRef primitive.ObjectID) ([]model.PolicyDescription, error) {
|
||||
// all documents
|
||||
filter := repository.Query().Or(
|
||||
repository.Filter(storable.OrganizationRefField, nil),
|
||||
repository.OrgFilter(organizationRef),
|
||||
)
|
||||
return mutil.GetObjects[model.PolicyDescription](ctx, db.Logger, filter, nil, db.Repository)
|
||||
}
|
||||
13
api/pkg/db/internal/mongo/policiesdb/builtin.go
Normal file
13
api/pkg/db/internal/mongo/policiesdb/builtin.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package policiesdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tech/sendico/pkg/db/repository"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
)
|
||||
|
||||
func (db *PoliciesDB) GetBuiltInPolicy(ctx context.Context, resourceType mservice.Type, policy *model.PolicyDescription) error {
|
||||
return db.FindOne(ctx, repository.Filter("resourceTypes", resourceType), policy)
|
||||
}
|
||||
21
api/pkg/db/internal/mongo/policiesdb/db.go
Normal file
21
api/pkg/db/internal/mongo/policiesdb/db.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package policiesdb
|
||||
|
||||
import (
|
||||
"github.com/tech/sendico/pkg/db/template"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
type PoliciesDB struct {
|
||||
template.DBImp[*model.PolicyDescription]
|
||||
}
|
||||
|
||||
func Create(logger mlogger.Logger, db *mongo.Database) (*PoliciesDB, error) {
|
||||
p := &PoliciesDB{
|
||||
DBImp: *template.Create[*model.PolicyDescription](logger, mservice.Policies, db),
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
353
api/pkg/db/internal/mongo/policiesdb/db_test.go
Normal file
353
api/pkg/db/internal/mongo/policiesdb/db_test.go
Normal file
@@ -0,0 +1,353 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package policiesdb_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
// Your internal packages
|
||||
"github.com/tech/sendico/pkg/db/internal/mongo/policiesdb"
|
||||
"github.com/tech/sendico/pkg/db/repository"
|
||||
"github.com/tech/sendico/pkg/db/repository/builder"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
// Model package (contains PolicyDescription + Describable)
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
// Testcontainers
|
||||
"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"
|
||||
)
|
||||
|
||||
// Helper to terminate container
|
||||
func terminate(t *testing.T, ctx context.Context, container *mongodb.MongoDBContainer) {
|
||||
err := container.Terminate(ctx)
|
||||
require.NoError(t, err, "failed to terminate MongoDB container")
|
||||
}
|
||||
|
||||
// Helper to disconnect client
|
||||
func disconnect(t *testing.T, ctx context.Context, client *mongo.Client) {
|
||||
err := client.Disconnect(context.Background())
|
||||
require.NoError(t, err, "failed to disconnect from MongoDB")
|
||||
}
|
||||
|
||||
// Helper to drop the Policies collection
|
||||
func cleanupCollection(t *testing.T, ctx context.Context, db *mongo.Database) {
|
||||
// The actual collection name is typically the value returned by
|
||||
// (&model.PolicyDescription{}).Collection(), or something similar.
|
||||
// Make sure it matches what your code uses (often "policies" or "policyDescription").
|
||||
err := db.Collection((&model.PolicyDescription{}).Collection()).Drop(ctx)
|
||||
require.NoError(t, err, "failed to drop collection between sub-tests")
|
||||
}
|
||||
|
||||
func TestPoliciesDB(t *testing.T) {
|
||||
// Create context with reasonable timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
// Start MongoDB test container
|
||||
mongoC, 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")
|
||||
defer terminate(t, ctx, mongoC)
|
||||
|
||||
// Get connection URI
|
||||
mongoURI, err := mongoC.ConnectionString(ctx)
|
||||
require.NoError(t, err, "failed to get connection string")
|
||||
|
||||
// Connect client
|
||||
clientOpts := options.Client().ApplyURI(mongoURI)
|
||||
client, err := mongo.Connect(ctx, clientOpts)
|
||||
require.NoError(t, err, "failed to connect to MongoDB")
|
||||
defer disconnect(t, ctx, client)
|
||||
|
||||
// Create test DB
|
||||
db := client.Database("testdb")
|
||||
|
||||
// Use a no-op logger (or real logger if you prefer)
|
||||
logger := zap.NewNop()
|
||||
|
||||
// Create an instance of PoliciesDB
|
||||
pdb, err := policiesdb.Create(logger, db)
|
||||
require.NoError(t, err, "unexpected error creating PoliciesDB")
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Each sub-test below starts by dropping the collection.
|
||||
// ---------------------------------------------------------
|
||||
|
||||
t.Run("CreateAndGet", func(t *testing.T) {
|
||||
cleanupCollection(t, ctx, db) // ensure no leftover data
|
||||
|
||||
desc := "Test policy description"
|
||||
policy := &model.PolicyDescription{
|
||||
Describable: model.Describable{
|
||||
Name: "TestPolicy",
|
||||
Description: &desc,
|
||||
},
|
||||
}
|
||||
require.NoError(t, pdb.Create(ctx, policy))
|
||||
|
||||
result := &model.PolicyDescription{}
|
||||
err := pdb.Get(ctx, policy.ID, result)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, policy.ID, result.ID)
|
||||
assert.Equal(t, "TestPolicy", result.Name)
|
||||
assert.NotNil(t, result.Description)
|
||||
assert.Equal(t, "Test policy description", *result.Description)
|
||||
})
|
||||
|
||||
t.Run("Get_NotFound", func(t *testing.T) {
|
||||
cleanupCollection(t, ctx, db)
|
||||
|
||||
// Attempt to get a non-existent ID
|
||||
nonExistentID := primitive.NewObjectID()
|
||||
result := &model.PolicyDescription{}
|
||||
err := pdb.Get(ctx, nonExistentID, result)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, errors.Is(err, merrors.ErrNoData))
|
||||
})
|
||||
|
||||
t.Run("Update", func(t *testing.T) {
|
||||
cleanupCollection(t, ctx, db)
|
||||
|
||||
originalDesc := "Original description"
|
||||
policy := &model.PolicyDescription{
|
||||
Describable: model.Describable{
|
||||
Name: "OriginalName",
|
||||
Description: &originalDesc,
|
||||
},
|
||||
}
|
||||
require.NoError(t, pdb.Create(ctx, policy))
|
||||
|
||||
newDesc := "Updated description"
|
||||
policy.Name = "UpdatedName"
|
||||
policy.Description = &newDesc
|
||||
|
||||
err := pdb.Update(ctx, policy)
|
||||
require.NoError(t, err)
|
||||
|
||||
updated := &model.PolicyDescription{}
|
||||
err = pdb.Get(ctx, policy.ID, updated)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "UpdatedName", updated.Name)
|
||||
assert.NotNil(t, updated.Description)
|
||||
assert.Equal(t, "Updated description", *updated.Description)
|
||||
})
|
||||
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
cleanupCollection(t, ctx, db)
|
||||
|
||||
desc := "To be deleted"
|
||||
policy := &model.PolicyDescription{
|
||||
Describable: model.Describable{
|
||||
Name: "WillDelete",
|
||||
Description: &desc,
|
||||
},
|
||||
}
|
||||
require.NoError(t, pdb.Create(ctx, policy))
|
||||
|
||||
err := pdb.Delete(ctx, policy.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
deleted := &model.PolicyDescription{}
|
||||
err = pdb.Get(ctx, policy.ID, deleted)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, errors.Is(err, merrors.ErrNoData))
|
||||
})
|
||||
|
||||
t.Run("DeleteMany", func(t *testing.T) {
|
||||
cleanupCollection(t, ctx, db)
|
||||
|
||||
desc1 := "Will be deleted 1"
|
||||
desc2 := "Will be deleted 2"
|
||||
pol1 := &model.PolicyDescription{
|
||||
Describable: model.Describable{
|
||||
Name: "BatchDelete1",
|
||||
Description: &desc1,
|
||||
},
|
||||
}
|
||||
pol2 := &model.PolicyDescription{
|
||||
Describable: model.Describable{
|
||||
Name: "BatchDelete2",
|
||||
Description: &desc2,
|
||||
},
|
||||
}
|
||||
require.NoError(t, pdb.Create(ctx, pol1))
|
||||
require.NoError(t, pdb.Create(ctx, pol2))
|
||||
|
||||
q := repository.Query().RegEx(repository.Field("description"), "^Will be deleted", "")
|
||||
err := pdb.DeleteMany(ctx, q)
|
||||
require.NoError(t, err)
|
||||
|
||||
res1 := &model.PolicyDescription{}
|
||||
err1 := pdb.Get(ctx, pol1.ID, res1)
|
||||
assert.Error(t, err1)
|
||||
assert.True(t, errors.Is(err1, merrors.ErrNoData))
|
||||
|
||||
res2 := &model.PolicyDescription{}
|
||||
err2 := pdb.Get(ctx, pol2.ID, res2)
|
||||
assert.Error(t, err2)
|
||||
assert.True(t, errors.Is(err2, merrors.ErrNoData))
|
||||
})
|
||||
|
||||
t.Run("FindOne", func(t *testing.T) {
|
||||
cleanupCollection(t, ctx, db)
|
||||
|
||||
desc := "Unique find test"
|
||||
policy := &model.PolicyDescription{
|
||||
Describable: model.Describable{
|
||||
Name: "FindOneTest",
|
||||
Description: &desc,
|
||||
},
|
||||
}
|
||||
require.NoError(t, pdb.Create(ctx, policy))
|
||||
|
||||
// Match by name == "FindOneTest"
|
||||
q := repository.Query().Comparison(repository.Field("name"), builder.Eq, "FindOneTest")
|
||||
|
||||
found := &model.PolicyDescription{}
|
||||
err := pdb.FindOne(ctx, q, found)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, policy.ID, found.ID)
|
||||
assert.Equal(t, "FindOneTest", found.Name)
|
||||
assert.NotNil(t, found.Description)
|
||||
assert.Equal(t, "Unique find test", *found.Description)
|
||||
})
|
||||
|
||||
t.Run("All", func(t *testing.T) {
|
||||
cleanupCollection(t, ctx, db)
|
||||
|
||||
// Insert some policies (orgA, orgB, nil org)
|
||||
orgA := primitive.NewObjectID()
|
||||
orgB := primitive.NewObjectID()
|
||||
|
||||
descA := "Org A policy"
|
||||
policyA := &model.PolicyDescription{
|
||||
Describable: model.Describable{
|
||||
Name: "PolicyA",
|
||||
Description: &descA,
|
||||
},
|
||||
OrganizationRef: &orgA, // belongs to orgA
|
||||
}
|
||||
descB := "Org B policy"
|
||||
policyB := &model.PolicyDescription{
|
||||
Describable: model.Describable{
|
||||
Name: "PolicyB",
|
||||
Description: &descB,
|
||||
},
|
||||
OrganizationRef: &orgB, // belongs to orgB
|
||||
}
|
||||
descNil := "No org policy"
|
||||
policyNil := &model.PolicyDescription{
|
||||
Describable: model.Describable{
|
||||
Name: "PolicyNil",
|
||||
Description: &descNil,
|
||||
},
|
||||
// nil => built-in
|
||||
}
|
||||
require.NoError(t, pdb.Create(ctx, policyA))
|
||||
require.NoError(t, pdb.Create(ctx, policyB))
|
||||
require.NoError(t, pdb.Create(ctx, policyNil))
|
||||
|
||||
// Suppose the requirement is: "All" returns
|
||||
// - policies for the requested org
|
||||
// - plus built-in (nil) ones
|
||||
resultsA, err := pdb.All(ctx, orgA)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, resultsA, 2) // orgA + built-in
|
||||
|
||||
var idsA []primitive.ObjectID
|
||||
for _, r := range resultsA {
|
||||
idsA = append(idsA, r.ID)
|
||||
}
|
||||
assert.Contains(t, idsA, policyA.ID)
|
||||
assert.Contains(t, idsA, policyNil.ID)
|
||||
assert.NotContains(t, idsA, policyB.ID)
|
||||
|
||||
resultsB, err := pdb.All(ctx, orgB)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, resultsB, 2) // orgB + built-in
|
||||
|
||||
var idsB []primitive.ObjectID
|
||||
for _, r := range resultsB {
|
||||
idsB = append(idsB, r.ID)
|
||||
}
|
||||
assert.Contains(t, idsB, policyB.ID)
|
||||
assert.Contains(t, idsB, policyNil.ID)
|
||||
assert.NotContains(t, idsB, policyA.ID)
|
||||
})
|
||||
|
||||
t.Run("Policies", func(t *testing.T) {
|
||||
cleanupCollection(t, ctx, db)
|
||||
|
||||
desc1 := "PolicyOne"
|
||||
pol1 := &model.PolicyDescription{
|
||||
Describable: model.Describable{
|
||||
Name: "PolicyOne",
|
||||
Description: &desc1,
|
||||
},
|
||||
}
|
||||
desc2 := "PolicyTwo"
|
||||
pol2 := &model.PolicyDescription{
|
||||
Describable: model.Describable{
|
||||
Name: "PolicyTwo",
|
||||
Description: &desc2,
|
||||
},
|
||||
}
|
||||
desc3 := "PolicyThree"
|
||||
pol3 := &model.PolicyDescription{
|
||||
Describable: model.Describable{
|
||||
Name: "PolicyThree",
|
||||
Description: &desc3,
|
||||
},
|
||||
}
|
||||
require.NoError(t, pdb.Create(ctx, pol1))
|
||||
require.NoError(t, pdb.Create(ctx, pol2))
|
||||
require.NoError(t, pdb.Create(ctx, pol3))
|
||||
|
||||
// 1) Request pol1, pol2
|
||||
results12, err := pdb.Policies(ctx, []primitive.ObjectID{pol1.ID, pol2.ID})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, results12, 2)
|
||||
// IDs might be out of order, so we do a set-like check
|
||||
var set12 []primitive.ObjectID
|
||||
for _, r := range results12 {
|
||||
set12 = append(set12, r.ID)
|
||||
}
|
||||
assert.Contains(t, set12, pol1.ID)
|
||||
assert.Contains(t, set12, pol2.ID)
|
||||
|
||||
// 2) Request pol1, pol3, plus a random ID
|
||||
fakeID := primitive.NewObjectID()
|
||||
results13Fake, err := pdb.Policies(ctx, []primitive.ObjectID{pol1.ID, pol3.ID, fakeID})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, results13Fake, 2) // pol1 + pol3 only
|
||||
var set13Fake []primitive.ObjectID
|
||||
for _, r := range results13Fake {
|
||||
set13Fake = append(set13Fake, r.ID)
|
||||
}
|
||||
assert.Contains(t, set13Fake, pol1.ID)
|
||||
assert.Contains(t, set13Fake, pol3.ID)
|
||||
|
||||
// 3) Request with empty slice => expect no results
|
||||
resultsEmpty, err := pdb.Policies(ctx, []primitive.ObjectID{})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, resultsEmpty, 0)
|
||||
})
|
||||
}
|
||||
18
api/pkg/db/internal/mongo/policiesdb/policies.go
Normal file
18
api/pkg/db/internal/mongo/policiesdb/policies.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package policiesdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tech/sendico/pkg/db/repository"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
mutil "github.com/tech/sendico/pkg/mutil/db"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func (db *PoliciesDB) Policies(ctx context.Context, refs []primitive.ObjectID) ([]model.PolicyDescription, error) {
|
||||
if len(refs) == 0 {
|
||||
return []model.PolicyDescription{}, nil
|
||||
}
|
||||
filter := repository.Query().In(repository.IDField(), refs)
|
||||
return mutil.GetObjects[model.PolicyDescription](ctx, db.Logger, filter, nil, db.Repository)
|
||||
}
|
||||
Reference in New Issue
Block a user