Add incl/excl filtering for deleted record
This commit is contained in:
@@ -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)"},
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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 "+
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -35,6 +35,8 @@ type (
|
||||
|
||||
// Standard paging fields & helpers
|
||||
rh.PageFilter
|
||||
|
||||
Deleted rh.FilterState `json:"deleted"`
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -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 |
|
||||
|
||||
Reference in New Issue
Block a user