Add incl/excl filtering for deleted record

This commit is contained in:
Denis Arh
2020-04-30 08:34:20 +02:00
parent f291613516
commit efa2f28b8c
8 changed files with 77 additions and 7 deletions
+6
View File
@@ -746,6 +746,12 @@
"required": false,
"title": "Filtering condition (same as query, deprecated)"
},
{
"name": "deleted",
"required": false,
"title": "Exclude (0, default), include (1) or return only (2) deleted records",
"type": "uint"
},
{"type": "uint", "name": "limit", "title": "Limit"},
{"type": "uint", "name": "offset", "title": "Offset"},
{"type": "uint", "name": "page", "title": "Page number (1-based)"},
+6
View File
@@ -76,6 +76,12 @@
"title": "Filtering condition (same as query, deprecated)",
"type": "string"
},
{
"name": "deleted",
"required": false,
"title": "Exclude (0, default), include (1) or return only (2) deleted records",
"type": "uint"
},
{
"name": "limit",
"title": "Limit",
+5 -2
View File
@@ -77,8 +77,7 @@ func (r record) columns() []string {
func (r record) query() squirrel.SelectBuilder {
return squirrel.
Select(r.columns()...).
From(r.table() + " AS r").
Where("r.deleted_at IS NULL")
From(r.table() + " AS r")
}
// @todo: update to accepted DeletedAt column semantics from Messaging
@@ -92,6 +91,7 @@ func (r record) findOneBy(namespaceID uint64, field string, value interface{}) (
rec = &types.Record{}
q = r.query().
Where("r.deleted_at IS NULL").
Where(squirrel.Eq{field: value, "rel_namespace": namespaceID})
err = rh.FetchOne(r.db(), q, rec)
@@ -172,6 +172,9 @@ func (r record) buildQuery(module *types.Module, f types.RecordFilter) (query sq
return false
}
// Inc/exclude deleted records according to filter settings
query = rh.FilterNullByState(query, "r.deleted_at", f.Deleted)
// Parse filters.
if f.Query != "" {
var (
+30 -5
View File
@@ -2,6 +2,7 @@ package repository
import (
"fmt"
"github.com/cortezaproject/corteza-server/pkg/rh"
"strings"
"testing"
@@ -22,13 +23,19 @@ func TestRecordFinder(t *testing.T) {
}
ttc := []struct {
f types.RecordFilter
match []string
args []interface{}
err error
f types.RecordFilter
match []string
noMatch []string
args []interface{}
err error
}{
{
match: []string{"SELECT r.id, r.module_id, r.rel_namespace, r.owned_by, r.created_at, r.created_by, r.updated_at, r.updated_by, r.deleted_at, r.deleted_by FROM compose_record AS r WHERE r.deleted_at IS NULL AND r.module_id = ? AND r.rel_namespace = ?"},
match: []string{
"SELECT r.id, r.module_id, r.rel_namespace, r.owned_by, r.created_at, " +
"r.created_by, r.updated_at, r.updated_by, r.deleted_at, r.deleted_by " +
"FROM compose_record AS r " +
"WHERE r.module_id = ? AND r.rel_namespace = ? AND r.deleted_at IS NULL",
},
},
{
f: types.RecordFilter{Query: "id = 5 AND foo = 7"},
@@ -45,6 +52,18 @@ func TestRecordFinder(t *testing.T) {
},
args: []interface{}{"bar"},
},
{
f: types.RecordFilter{Deleted: rh.FilterStateExcluded},
match: []string{" r.deleted_at IS "},
},
{
f: types.RecordFilter{Deleted: rh.FilterStateInclusive},
noMatch: []string{" r.deleted_at IS "},
},
{
f: types.RecordFilter{Deleted: rh.FilterStateExclusive},
match: []string{" r.deleted_at IS NOT NULL"},
},
}
for _, tc := range ttc {
@@ -64,6 +83,12 @@ func TestRecordFinder(t *testing.T) {
" did not contain %q", sql, m)
}
for _, m := range tc.noMatch {
require.False(t, strings.Contains(sql, m),
"assertion failed; query %q \n "+
" must not contain %q", sql, m)
}
tc.args = append(tc.args, m.ID, m.NamespaceID)
require.True(t, fmt.Sprintf("%+v", args) == fmt.Sprintf("%+v", tc.args),
"assertion failed; args %+v \n "+
+2
View File
@@ -82,6 +82,8 @@ func (ctrl *Record) List(ctx context.Context, r *request.RecordList) (interface{
ModuleID: r.ModuleID,
Sort: r.Sort,
Deleted: rh.FilterState(r.Deleted),
PageFilter: rh.Paging(r),
}
)
+25
View File
@@ -138,6 +138,10 @@ type RecordList struct {
rawFilter string
Filter string
hasDeleted bool
rawDeleted string
Deleted uint
hasLimit bool
rawLimit string
Limit uint
@@ -178,6 +182,7 @@ func (r RecordList) Auditable() map[string]interface{} {
out["query"] = r.Query
out["filter"] = r.Filter
out["deleted"] = r.Deleted
out["limit"] = r.Limit
out["offset"] = r.Offset
out["page"] = r.Page
@@ -227,6 +232,11 @@ func (r *RecordList) Fill(req *http.Request) (err error) {
r.rawFilter = val
r.Filter = val
}
if val, ok := get["deleted"]; ok {
r.hasDeleted = true
r.rawDeleted = val
r.Deleted = parseUint(val)
}
if val, ok := get["limit"]; ok {
r.hasLimit = true
r.rawLimit = val
@@ -1448,6 +1458,21 @@ func (r *RecordList) GetFilter() string {
return r.Filter
}
// HasDeleted returns true if deleted was set
func (r *RecordList) HasDeleted() bool {
return r.hasDeleted
}
// RawDeleted returns raw value of deleted parameter
func (r *RecordList) RawDeleted() string {
return r.rawDeleted
}
// GetDeleted returns casted value of deleted parameter
func (r *RecordList) GetDeleted() uint {
return r.Deleted
}
// HasLimit returns true if limit was set
func (r *RecordList) HasLimit() bool {
return r.hasLimit
+2
View File
@@ -35,6 +35,8 @@ type (
// Standard paging fields & helpers
rh.PageFilter
Deleted rh.FilterState `json:"deleted"`
}
)
+1
View File
@@ -872,6 +872,7 @@ Compose records
| --------- | ---- | ------ | ----------- | ------- | --------- |
| query | string | GET | Record filtering query | N/A | NO |
| filter | string | GET | Filtering condition (same as query, deprecated) | N/A | NO |
| deleted | uint | GET | Exclude (0, default), include (1) or return only (2) deleted records | N/A | NO |
| limit | uint | GET | Limit | N/A | NO |
| offset | uint | GET | Offset | N/A | NO |
| page | uint | GET | Page number (1-based) | N/A | NO |