Skip to content

Commit

Permalink
fix: no timestamps conf (#4)
Browse files Browse the repository at this point in the history
* feat: add fill featured generic repo

* feat: add HardDelete method

* feat: add HardDeleteMany function

* refactor: generify SortOrders to be able to build precedence

* feat: add order constants

* refactor: examples

* refactor: raname internal log methods

* fix: logger tests

* refactor: use primitive.ObjectID instead of pointer

* fix: add testing and implementation for no timestamps

* fix: add updated add field on soft deletes

* deps: upgrade

* fix: assert dates

* ci: remove unneded lines
  • Loading branch information
danteay authored May 31, 2023
1 parent 3a2b374 commit d4c629b
Show file tree
Hide file tree
Showing 29 changed files with 442 additions and 156 deletions.
16 changes: 0 additions & 16 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ on:
pull_request:
branches:
- main
env:
FIREBASE_DEV_KEY: ${{ secrets.FIREBASE_DEV_KEY }}

jobs:
test:
Expand Down Expand Up @@ -36,20 +34,6 @@ jobs:
set -euo pipefail
go test -json -v -race -cover -coverprofile=coverage.out ./... 2>&1 | tee /tmp/gotest.log | gotestfmt
# - name: Convert out to xml
# run: |
# go install github.com/axw/gocov/gocov@latest
# go install github.com/AlekSi/gocov-xml@latest
# gocov convert coverage.out | gocov-xml > coverage.xml

# - name: Review coverage
# uses: codecov/codecov-action@v3
# with:
# token: ${{ secrets.CODECOV_TOKEN }}
# files: ./coverage.xml
# fail_ci_if_error: false
# verbose: true

lint:
name: 💅 Lint
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions count.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
)

// Count returns the number of documents in the collection that match the given search options.
func (r Repository[M, D, SF, UF]) Count(ctx context.Context, opts SF) (int64, error) {
filters, err := r.BuildSearchFilters(opts)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion count_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/assert"

"github.com/Drafteame/mgorepo/driver"
"github.com/Drafteame/mgorepo/seed"
"github.com/Drafteame/mgorepo/internal/seed"
)

func TestRepository_Count(t *testing.T) {
Expand Down
10 changes: 7 additions & 3 deletions create.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"go.mongodb.org/mongo-driver/bson"
)

// Create creates a new record on the collection based on the model.
func (r Repository[M, D, SF, UF]) Create(ctx context.Context, model M) (M, error) {
var zeroM M

Expand Down Expand Up @@ -61,7 +62,6 @@ func (r Repository[M, D, SF, UF]) createBuildData(model M) (bson.M, error) {
return bson.M{}, errors.Join(ErrCreatingDAO, errDao)
}

now := r.Now()
bsonData := bson.M{}

bsonBytes, err := bson.Marshal(dao)
Expand All @@ -75,8 +75,12 @@ func (r Repository[M, D, SF, UF]) createBuildData(model M) (bson.M, error) {
return bson.M{}, errors.Join(ErrCreatingDAO, err)
}

bsonData[r.createdAtField] = now
bsonData[r.updatedAtField] = now
if r.withTimestamps {
now := r.Now()

bsonData["createdAt"] = now
bsonData["updatedAt"] = now
}

return bsonData, nil
}
40 changes: 28 additions & 12 deletions create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

"github.com/Drafteame/mgorepo/clock"
"github.com/Drafteame/mgorepo/driver"
ptesting "github.com/Drafteame/mgorepo/testing"
ptesting "github.com/Drafteame/mgorepo/internal/testing"
)

func TestRepository_Create(t *testing.T) {
Expand All @@ -20,15 +20,31 @@ func TestRepository_Create(t *testing.T) {

c := clock.NewTest(time.Now()).ForceUTC()

expected := testModel{
Identifier: "identifier",
}

repo := newTestRepository(d).SetClock(c)
model, err := repo.Create(context.Background(), expected)

assert.Nil(t, err)
assert.NotEmpty(t, model.ID)
ptesting.AssertDate(t, c.Now(), model.CreatedAt)
ptesting.AssertDate(t, c.Now(), model.UpdatedAt)
t.Run("success create", func(t *testing.T) {
expected := testModel{
Identifier: "identifier",
}

repo := newTestRepository(d).SetClock(c)
model, err := repo.Create(context.Background(), expected)

assert.Nil(t, err)
assert.NotEmpty(t, model.ID)
ptesting.AssertDate(t, c.Now(), model.CreatedAt)
ptesting.AssertDate(t, c.Now(), model.UpdatedAt)
})

t.Run("success create with no timestamps", func(t *testing.T) {
expected := testModel{
Identifier: "identifier",
}

repo := newTestRepository(d).SetClock(c).WithTimestamps(false)
model, err := repo.Create(context.Background(), expected)

assert.Nil(t, err)
assert.NotEmpty(t, model.ID)
ptesting.AssertEmptyDate(t, model.CreatedAt)
ptesting.AssertEmptyDate(t, model.UpdatedAt)
})
}
11 changes: 10 additions & 1 deletion delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ import (
"go.mongodb.org/mongo-driver/bson/primitive"
)

// Delete deletes a document by id. If the repository is configured to not use timestamps, this operation will be
// applied as a hard delete.
func (r Repository[M, D, SF, UF]) Delete(ctx context.Context, id string) (int64, error) {
if !r.withTimestamps {
return r.HardDelete(ctx, id)
}

oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
r.logErrorf(err, actionDelete, "error converting %s to ObjectID", id)
Expand All @@ -18,9 +24,12 @@ func (r Repository[M, D, SF, UF]) Delete(ctx context.Context, id string) (int64,
{Key: "_id", Value: oid},
}

now := r.Now()

data := bson.M{
"$set": bson.M{
r.deletedAtField: r.Now(),
r.updatedAtField: now,
r.deletedAtField: now,
},
}

Expand Down
15 changes: 14 additions & 1 deletion delete_many.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,26 @@ import (
"go.mongodb.org/mongo-driver/bson"
)

// DeleteMany deletes many documents from the collection. It returns the number of deleted documents and an error.
// If the repository has timestamps enabled, it will soft delete the documents. Otherwise, it will hard delete them.
func (r Repository[M, D, SF, UF]) DeleteMany(ctx context.Context, filters SF) (int64, error) {
if !r.withTimestamps {
return r.HardDeleteMany(ctx, filters)
}

bf, err := r.deleteManyFilters(filters)
if err != nil {
return 0, err
}

data := bson.D{{Key: "$set", Value: bson.D{{Key: r.deletedAtField, Value: r.Now()}}}}
now := r.Now()

data := bson.M{
"$set": bson.M{
r.updatedAtField: now,
r.deletedAtField: now,
},
}

r.logDebugf(actionDeleteMany, "filters: %+v data: %+v", bf, data)

Expand Down
73 changes: 68 additions & 5 deletions delete_many_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import (

"github.com/Drafteame/mgorepo/clock"
"github.com/Drafteame/mgorepo/driver"
"github.com/Drafteame/mgorepo/seed"
"github.com/Drafteame/mgorepo/internal/seed"
ptesting "github.com/Drafteame/mgorepo/internal/testing"
)

func TestRepository_DeleteMany(t *testing.T) {
Expand All @@ -28,8 +29,9 @@ func TestRepository_DeleteMany(t *testing.T) {

for i := 0; i < 100; i++ {
oid := primitive.NewObjectID()

dao := testDAO{
ID: &oid,
ID: oid,
Identifier: "test",
Sortable: randomNumber(),
}
Expand All @@ -38,10 +40,14 @@ func TestRepository_DeleteMany(t *testing.T) {

seed.InsertMany(t, db, collection, daos...)

filters := newSearchFilters().WithSortableGreaterThan(50)

repo := newTestRepository(d).SetClock(c)

filters := newSearchFilters().WithSortableGreaterThan(50)
bsonFilters, err := repo.BuildSearchFilters(filters)
if err != nil {
t.Fatal(err)
}

total, err := repo.Count(context.Background(), filters)
if err != nil {
t.Fatal(err)
Expand All @@ -58,14 +64,71 @@ func TestRepository_DeleteMany(t *testing.T) {
}

assert.Equal(t, totalAfterDel, total-deleted)

cursor, errFind := db.Collection(collection).Find(context.Background(), bsonFilters)
if errFind != nil {
t.Fatal(errFind)
}

for cursor.Next(context.Background()) {
var dao testDAO
if errDecode := cursor.Decode(&dao); errDecode != nil {
t.Fatal(errDecode)
}

ptesting.AssertDate(t, c.Now(), dao.DeletedAt.Time().UTC())
ptesting.AssertDate(t, c.Now(), dao.UpdatedAt.Time().UTC())
}
})

t.Run("error delete many with empty filters", func(t *testing.T) {
repo := newTestRepository(d)

_, err := repo.DeleteMany(context.Background(), newSearchFilters())

assert.Error(t, err)
assert.ErrorIs(t, err, ErrEmptyFilters)
})

t.Run("delete many with no timestamps", func(t *testing.T) {
daos := make([]any, 0, 100)
c := clock.NewTest(time.Now()).ForceUTC()

for i := 0; i < 100; i++ {
oid := primitive.NewObjectID()
dao := testDAO{
ID: oid,
Identifier: "test",
Sortable: randomNumber(),
}
daos = append(daos, dao)
}

seed.InsertMany(t, db, collection, daos...)

repo := newTestRepository(d).SetClock(c).WithTimestamps(false)

filters := newSearchFilters().WithSortableGreaterThan(50)

bsonFilters, err := repo.BuildSearchFilters(filters)
if err != nil {
t.Fatal(err)
}

total, err := db.Collection(collection).CountDocuments(context.Background(), bsonFilters)
if err != nil {
t.Fatal(err)
}

deleted, errDelete := repo.DeleteMany(context.Background(), filters)
if errDelete != nil {
t.Fatal(errDelete)
}

remaining, err := db.Collection(collection).CountDocuments(context.Background(), bsonFilters)
if err != nil {
t.Fatal(err)
}

assert.Equal(t, total, remaining+deleted)
})
}
44 changes: 37 additions & 7 deletions delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (

"github.com/Drafteame/mgorepo/clock"
"github.com/Drafteame/mgorepo/driver"
"github.com/Drafteame/mgorepo/seed"
ptesting "github.com/Drafteame/mgorepo/testing"
"github.com/Drafteame/mgorepo/internal/seed"
ptesting "github.com/Drafteame/mgorepo/internal/testing"
)

func TestRepository_Delete(t *testing.T) {
Expand All @@ -28,7 +28,7 @@ func TestRepository_Delete(t *testing.T) {
c := clock.NewTest(time.Now()).ForceUTC()

data := testDAO{
ID: &oid,
ID: oid,
Sortable: 0,
Identifier: "asd",
CreatedAt: primitive.NewDateTimeFromTime(c.Now()),
Expand All @@ -50,18 +50,21 @@ func TestRepository_Delete(t *testing.T) {
}

ptesting.AssertDate(t, c.Now(), data.DeletedAt.Time().UTC())
ptesting.AssertDate(t, c.Now(), data.UpdatedAt.Time().UTC())
})

t.Run("success delete with no affected", func(t *testing.T) {
oid := primitive.NewObjectID()
c := clock.NewTest(time.Now()).ForceUTC()

pnow := primitive.NewDateTimeFromTime(c.Now())

data := testDAO{
ID: &oid,
ID: oid,
Sortable: 0,
Identifier: "asd",
CreatedAt: primitive.NewDateTimeFromTime(c.Now()),
UpdatedAt: primitive.NewDateTimeFromTime(c.Now()),
CreatedAt: pnow,
UpdatedAt: pnow,
}

seed.InsertOne(t, db, collection, data)
Expand All @@ -78,6 +81,33 @@ func TestRepository_Delete(t *testing.T) {
t.Fatal(errFind)
}

assert.Equal(t, primitive.DateTime(0), data.DeletedAt)
ptesting.AssertEmptyDate(t, data.DeletedAt)
ptesting.AssertDate(t, pnow, data.UpdatedAt)
})

t.Run("success delete with no timestamps", func(t *testing.T) {
oid := primitive.NewObjectID()

data := testDAO{
ID: oid,
Sortable: 0,
Identifier: "asd",
}

seed.InsertOne(t, db, collection, data)

repo := newTestRepository(d)

deletedCount, err := repo.Delete(context.Background(), oid.Hex())

assert.Nil(t, err)
assert.Equal(t, int64(1), deletedCount)

total, errCount := repo.Count(context.Background(), newSearchFilters())
if errCount != nil {
t.Fatal(errCount)
}

assert.Equal(t, int64(0), total)
})
}
Loading

0 comments on commit d4c629b

Please sign in to comment.