Skip to content

Commit

Permalink
call transaction Dao events only after commit, added totalPages to th…
Browse files Browse the repository at this point in the history
…e search response and updated the tests
  • Loading branch information
ganigeorgiev committed Aug 9, 2022
1 parent 8288da8 commit f8f785d
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 78 deletions.
4 changes: 2 additions & 2 deletions apis/collection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ func TestCollectionImport(t *testing.T) {
},
},
{
Name: "authorized as admin + successfull collections save",
Name: "authorized as admin + successful collections save",
Method: http.MethodPut,
Url: "/api/collections/import",
Body: strings.NewReader(`{
Expand Down Expand Up @@ -601,7 +601,7 @@ func TestCollectionImport(t *testing.T) {
},
},
{
Name: "authorized as admin + successfull collections save and old non-system collections deletion",
Name: "authorized as admin + successful collections save and old non-system collections deletion",
Method: http.MethodPut,
Url: "/api/collections/import",
Body: strings.NewReader(`{
Expand Down
12 changes: 8 additions & 4 deletions apis/realtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,13 +210,17 @@ func (api *realtimeApi) bindEvents() {
return nil
})

api.app.OnRecordAfterCreateRequest().Add(func(e *core.RecordCreateEvent) error {
api.broadcastRecord("create", e.Record)
api.app.OnModelAfterCreate().Add(func(e *core.ModelEvent) error {
if record, ok := e.Model.(*models.Record); ok {
api.broadcastRecord("create", record)
}
return nil
})

api.app.OnRecordAfterUpdateRequest().Add(func(e *core.RecordUpdateEvent) error {
api.broadcastRecord("update", e.Record)
api.app.OnModelAfterUpdate().Add(func(e *core.ModelEvent) error {
if record, ok := e.Model.(*models.Record); ok {
api.broadcastRecord("update", record)
}
return nil
})

Expand Down
2 changes: 1 addition & 1 deletion cmd/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ func init() {
`

func migrateCollectionsHandler(app core.App, args []string) error {
createArgs := []string{"collections_import"}
createArgs := []string{"collections_snapshot"}
createArgs = append(createArgs, args...)

dao := daos.New(app.DB())
Expand Down
24 changes: 12 additions & 12 deletions daos/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,11 @@ func (dao *Dao) RunInTransaction(fn func(txDao *Dao) error) error {
// so execute the function within the current transaction
return fn(dao)
case *dbx.DB:
afterCalls := []afterCallGroup{}

return txOrDB.Transactional(func(tx *dbx.Tx) error {
txError := txOrDB.Transactional(func(tx *dbx.Tx) error {
txDao := New(tx)

afterCalls := []afterCallGroup{}

if dao.BeforeCreateFunc != nil {
txDao.BeforeCreateFunc = func(eventDao *Dao, m models.Model) error {
return dao.BeforeCreateFunc(eventDao, m)
Expand Down Expand Up @@ -103,23 +102,24 @@ func (dao *Dao) RunInTransaction(fn func(txDao *Dao) error) error {
}
}

if err := fn(txDao); err != nil {
return err
}
return fn(txDao)
})

// execute after event calls on successfull transaction
if txError == nil {
// execute after event calls on successful transaction
for _, call := range afterCalls {
if call.Action == "create" {
switch call.Action {
case "create":
dao.AfterCreateFunc(call.EventDao, call.Model)
} else if call.Action == "update" {
case "update":
dao.AfterUpdateFunc(call.EventDao, call.Model)
} else if call.Action == "delete" {
case "delete":
dao.AfterDeleteFunc(call.EventDao, call.Model)
}
}
}

return nil
})
return txError
}

return errors.New("Failed to start transaction (unknown dao.db)")
Expand Down
96 changes: 50 additions & 46 deletions daos/base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,32 +365,34 @@ func TestDaoTransactionHooksCallsOnFailure(t *testing.T) {

existingModel, _ := testApp.Dao().FindAdminByEmail("[email protected]")

baseDao.RunInTransaction(func(txDao *daos.Dao) error {
// test create
// ---
newModel := &models.Admin{}
newModel.Email = "[email protected]"
newModel.SetPassword("123456")
if err := txDao.Save(newModel); err != nil {
t.Fatal(err)
}
baseDao.RunInTransaction(func(txDao1 *daos.Dao) error {
return txDao1.RunInTransaction(func(txDao2 *daos.Dao) error {
// test create
// ---
newModel := &models.Admin{}
newModel.Email = "[email protected]"
newModel.SetPassword("123456")
if err := txDao2.Save(newModel); err != nil {
t.Fatal(err)
}

// test update (twice)
// ---
if err := txDao.Save(existingModel); err != nil {
t.Fatal(err)
}
if err := txDao.Save(existingModel); err != nil {
t.Fatal(err)
}
// test update (twice)
// ---
if err := txDao2.Save(existingModel); err != nil {
t.Fatal(err)
}
if err := txDao2.Save(existingModel); err != nil {
t.Fatal(err)
}

// test delete
// ---
if err := txDao.Delete(existingModel); err != nil {
t.Fatal(err)
}
// test delete
// ---
if err := txDao2.Delete(existingModel); err != nil {
t.Fatal(err)
}

return errors.New("test_tx_error")
return errors.New("test_tx_error")
})
})

if beforeCreateFuncCalls != 1 {
Expand Down Expand Up @@ -451,32 +453,34 @@ func TestDaoTransactionHooksCallsOnSuccess(t *testing.T) {

existingModel, _ := testApp.Dao().FindAdminByEmail("[email protected]")

baseDao.RunInTransaction(func(txDao *daos.Dao) error {
// test create
// ---
newModel := &models.Admin{}
newModel.Email = "[email protected]"
newModel.SetPassword("123456")
if err := txDao.Save(newModel); err != nil {
t.Fatal(err)
}
baseDao.RunInTransaction(func(txDao1 *daos.Dao) error {
return txDao1.RunInTransaction(func(txDao2 *daos.Dao) error {
// test create
// ---
newModel := &models.Admin{}
newModel.Email = "[email protected]"
newModel.SetPassword("123456")
if err := txDao2.Save(newModel); err != nil {
t.Fatal(err)
}

// test update (twice)
// ---
if err := txDao.Save(existingModel); err != nil {
t.Fatal(err)
}
if err := txDao.Save(existingModel); err != nil {
t.Fatal(err)
}
// test update (twice)
// ---
if err := txDao2.Save(existingModel); err != nil {
t.Fatal(err)
}
if err := txDao2.Save(existingModel); err != nil {
t.Fatal(err)
}

// test delete
// ---
if err := txDao.Delete(existingModel); err != nil {
t.Fatal(err)
}
// test delete
// ---
if err := txDao2.Delete(existingModel); err != nil {
t.Fatal(err)
}

return nil
return nil
})
})

if beforeCreateFuncCalls != 1 {
Expand Down
1 change: 0 additions & 1 deletion daos/record_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,6 @@ func TestFindUserRelatedRecords(t *testing.T) {
}

if len(records) != len(scenario.expectedIds) {
fmt.Println(records[0])
t.Errorf("(%d) Expected %d records, got %d (%v)", i, len(scenario.expectedIds), len(records), records)
continue
}
Expand Down
5 changes: 2 additions & 3 deletions tools/migrate/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,8 @@ func NewRunner(db *dbx.DB, migrationsList MigrationsList) (*Runner, error) {
// Run interactively executes the current runner with the provided args.
//
// The following commands are supported:
// - up - applies all migrations
// - down [n] - reverts the last n applied migrations
// - create NEW_MIGRATION_NAME - create NEW_MIGRATION_NAME.go file from a migration template
// - up - applies all migrations
// - down [n] - reverts the last n applied migrations
func (r *Runner) Run(args ...string) error {
cmd := "up"
if len(args) > 0 {
Expand Down
6 changes: 5 additions & 1 deletion tools/search/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Result struct {
Page int `json:"page"`
PerPage int `json:"perPage"`
TotalItems int `json:"totalItems"`
TotalPages int `json:"totalPages"`
Items any `json:"items"`
}

Expand Down Expand Up @@ -213,10 +214,12 @@ func (s *Provider) Exec(items any) (*Result, error) {
s.perPage = MaxPerPage
}

totalPages := int(math.Ceil(float64(totalCount) / float64(s.perPage)))

// normalize page according to the total count
if s.page <= 0 || totalCount == 0 {
s.page = 1
} else if totalPages := int(math.Ceil(float64(totalCount) / float64(s.perPage))); s.page > totalPages {
} else if s.page > totalPages {
s.page = totalPages
}

Expand All @@ -233,6 +236,7 @@ func (s *Provider) Exec(items any) (*Result, error) {
Page: s.page,
PerPage: s.perPage,
TotalItems: int(totalCount),
TotalPages: totalPages,
Items: items,
}, nil
}
Expand Down
16 changes: 8 additions & 8 deletions tools/search/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ func TestProviderExecNonEmptyQuery(t *testing.T) {
[]FilterData{},
"",
false,
`{"page":1,"perPage":10,"totalItems":2,"items":[{"test1":1,"test2":"test2.1","test3":""},{"test1":2,"test2":"test2.2","test3":""}]}`,
`{"page":1,"perPage":10,"totalItems":2,"totalPages":1,"items":[{"test1":1,"test2":"test2.1","test3":""},{"test1":2,"test2":"test2.2","test3":""}]}`,
[]string{
"SELECT COUNT(*) FROM `test` WHERE NOT (`test1` IS NULL) ORDER BY `test1` ASC",
"SELECT * FROM `test` WHERE NOT (`test1` IS NULL) ORDER BY `test1` ASC LIMIT 10",
Expand All @@ -250,7 +250,7 @@ func TestProviderExecNonEmptyQuery(t *testing.T) {
[]FilterData{},
"",
false,
`{"page":1,"perPage":30,"totalItems":2,"items":[{"test1":1,"test2":"test2.1","test3":""},{"test1":2,"test2":"test2.2","test3":""}]}`,
`{"page":1,"perPage":30,"totalItems":2,"totalPages":1,"items":[{"test1":1,"test2":"test2.1","test3":""},{"test1":2,"test2":"test2.2","test3":""}]}`,
[]string{
"SELECT COUNT(*) FROM `test` WHERE NOT (`test1` IS NULL) ORDER BY `test1` ASC",
"SELECT * FROM `test` WHERE NOT (`test1` IS NULL) ORDER BY `test1` ASC LIMIT 30",
Expand Down Expand Up @@ -286,7 +286,7 @@ func TestProviderExecNonEmptyQuery(t *testing.T) {
[]FilterData{"test2 != null", "test1 >= 2"},
"",
false,
`{"page":1,"perPage":` + fmt.Sprint(MaxPerPage) + `,"totalItems":1,"items":[{"test1":2,"test2":"test2.2","test3":""}]}`,
`{"page":1,"perPage":` + fmt.Sprint(MaxPerPage) + `,"totalItems":1,"totalPages":1,"items":[{"test1":2,"test2":"test2.2","test3":""}]}`,
[]string{
"SELECT COUNT(*) FROM `test` WHERE ((NOT (`test1` IS NULL)) AND (COALESCE(test2, '') != COALESCE(null, ''))) AND (test1 >= 2) ORDER BY `test1` ASC, `test2` DESC",
"SELECT * FROM `test` WHERE ((NOT (`test1` IS NULL)) AND (COALESCE(test2, '') != COALESCE(null, ''))) AND (test1 >= 2) ORDER BY `test1` ASC, `test2` DESC LIMIT 200",
Expand All @@ -300,7 +300,7 @@ func TestProviderExecNonEmptyQuery(t *testing.T) {
[]FilterData{"test3 != ''"},
"",
false,
`{"page":1,"perPage":10,"totalItems":0,"items":[]}`,
`{"page":1,"perPage":10,"totalItems":0,"totalPages":0,"items":[]}`,
[]string{
"SELECT COUNT(*) FROM `test` WHERE (NOT (`test1` IS NULL)) AND (COALESCE(test3, '') != COALESCE('', '')) ORDER BY `test1` ASC, `test3` ASC",
"SELECT * FROM `test` WHERE (NOT (`test1` IS NULL)) AND (COALESCE(test3, '') != COALESCE('', '')) ORDER BY `test1` ASC, `test3` ASC LIMIT 10",
Expand All @@ -314,7 +314,7 @@ func TestProviderExecNonEmptyQuery(t *testing.T) {
[]FilterData{},
"",
false,
`{"page":2,"perPage":1,"totalItems":2,"items":[{"test1":2,"test2":"test2.2","test3":""}]}`,
`{"page":2,"perPage":1,"totalItems":2,"totalPages":2,"items":[{"test1":2,"test2":"test2.2","test3":""}]}`,
[]string{
"SELECT COUNT(*) FROM `test` WHERE NOT (`test1` IS NULL) ORDER BY `test1` ASC",
"SELECT * FROM `test` WHERE NOT (`test1` IS NULL) ORDER BY `test1` ASC LIMIT 1 OFFSET 1",
Expand All @@ -328,7 +328,7 @@ func TestProviderExecNonEmptyQuery(t *testing.T) {
[]FilterData{},
"test.test1",
false,
`{"page":2,"perPage":1,"totalItems":2,"items":[{"test1":2,"test2":"test2.2","test3":""}]}`,
`{"page":2,"perPage":1,"totalItems":2,"totalPages":2,"items":[{"test1":2,"test2":"test2.2","test3":""}]}`,
[]string{
"SELECT COUNT(DISTINCT(test.test1)) FROM `test` WHERE NOT (`test1` IS NULL) ORDER BY `test1` ASC",
"SELECT * FROM `test` WHERE NOT (`test1` IS NULL) ORDER BY `test1` ASC LIMIT 1 OFFSET 1",
Expand Down Expand Up @@ -403,7 +403,7 @@ func TestProviderParseAndExec(t *testing.T) {
{
"",
false,
`{"page":1,"perPage":123,"totalItems":2,"items":[{"test1":1,"test2":"test2.1","test3":""},{"test1":2,"test2":"test2.2","test3":""}]}`,
`{"page":1,"perPage":123,"totalItems":2,"totalPages":1,"items":[{"test1":1,"test2":"test2.1","test3":""},{"test1":2,"test2":"test2.2","test3":""}]}`,
},
// invalid query
{
Expand Down Expand Up @@ -439,7 +439,7 @@ func TestProviderParseAndExec(t *testing.T) {
{
"page=3&perPage=555&filter=test1>1&sort=-test2,test3",
false,
`{"page":1,"perPage":200,"totalItems":1,"items":[{"test1":2,"test2":"test2.2","test3":""}]}`,
`{"page":1,"perPage":200,"totalItems":1,"totalPages":1,"items":[{"test1":2,"test2":"test2.2","test3":""}]}`,
},
}

Expand Down

0 comments on commit f8f785d

Please sign in to comment.