filter enhancements

This commit is contained in:
Gani Georgiev
2023-01-07 22:25:56 +02:00
parent d5775ff657
commit 9b880f5ab4
102 changed files with 3693 additions and 986 deletions
+1 -1
View File
@@ -215,7 +215,7 @@ func (form *CollectionUpsert) ensureExistingRelationCollectionId(value any) erro
continue
}
if _, err := form.dao.FindCollectionByNameOrId(options.CollectionId); err != nil {
if err := form.dao.FindById(&models.Collection{}, options.CollectionId); err != nil {
return validation.Errors{fmt.Sprint(i): validation.NewError(
"validation_field_invalid_relation",
"The relation collection doesn't exist.",
+9 -9
View File
@@ -52,7 +52,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
"collections": []
}`,
expectError: true,
expectCollectionsCount: 7,
expectCollectionsCount: 8,
expectEvents: nil,
},
{
@@ -82,7 +82,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
]
}`,
expectError: true,
expectCollectionsCount: 7,
expectCollectionsCount: 8,
expectEvents: map[string]int{
"OnModelBeforeCreate": 2,
},
@@ -101,7 +101,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
]
}`,
expectError: true,
expectCollectionsCount: 7,
expectCollectionsCount: 8,
expectEvents: map[string]int{
"OnModelBeforeCreate": 2,
},
@@ -137,7 +137,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
]
}`,
expectError: false,
expectCollectionsCount: 10,
expectCollectionsCount: 11,
expectEvents: map[string]int{
"OnModelBeforeCreate": 3,
"OnModelAfterCreate": 3,
@@ -160,7 +160,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
]
}`,
expectError: true,
expectCollectionsCount: 7,
expectCollectionsCount: 8,
expectEvents: map[string]int{
"OnModelBeforeCreate": 1,
},
@@ -202,7 +202,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
]
}`,
expectError: true,
expectCollectionsCount: 7,
expectCollectionsCount: 8,
expectEvents: map[string]int{
"OnModelBeforeDelete": 5,
},
@@ -253,7 +253,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
]
}`,
expectError: false,
expectCollectionsCount: 9,
expectCollectionsCount: 10,
expectEvents: map[string]int{
"OnModelBeforeUpdate": 1,
"OnModelAfterUpdate": 1,
@@ -341,8 +341,8 @@ func TestCollectionsImportSubmit(t *testing.T) {
"OnModelAfterUpdate": 2,
"OnModelBeforeCreate": 1,
"OnModelAfterCreate": 1,
"OnModelBeforeDelete": 5,
"OnModelAfterDelete": 5,
"OnModelBeforeDelete": 6,
"OnModelAfterDelete": 6,
},
},
}
+40 -36
View File
@@ -7,7 +7,6 @@ import (
"log"
"net/http"
"regexp"
"strconv"
"strings"
validation "github.com/go-ozzo/ozzo-validation/v4"
@@ -219,11 +218,6 @@ func (form *RecordUpsert) extractMultipartFormData(
// and lods it into the form.
//
// File upload is supported only via multipart/form-data.
//
// To DELETE previously uploaded file(s) you can suffix the field name
// with the file index or filename (eg. `myfile.0`) and set it to null or empty string.
// For single file upload fields, you can skip the index and directly
// reset the field using its field name (eg. `myfile = null`).
func (form *RecordUpsert) LoadRequest(r *http.Request, keyPrefix string) error {
requestData, uploadedFiles, err := form.extractRequestData(r, keyPrefix)
if err != nil {
@@ -345,29 +339,24 @@ func (form *RecordUpsert) RemoveFiles(key string, toDelete ...string) error {
}
// LoadData loads and normalizes the provided regular record data fields into the form.
//
// To DELETE previously uploaded file(s) you can suffix the field name
// with the file index or filename (eg. `myfile.0`) and set it to null or empty string.
// For single file upload fields, you can skip the index and directly
// reset the field using its field name (eg. `myfile = null`).
func (form *RecordUpsert) LoadData(requestData map[string]any) error {
// load base system fields
if v, ok := requestData["id"]; ok {
if v, ok := requestData[schema.FieldNameId]; ok {
form.Id = cast.ToString(v)
}
// load auth system fields
if form.record.Collection().IsAuth() {
if v, ok := requestData["username"]; ok {
if v, ok := requestData[schema.FieldNameUsername]; ok {
form.Username = cast.ToString(v)
}
if v, ok := requestData["email"]; ok {
if v, ok := requestData[schema.FieldNameEmail]; ok {
form.Email = cast.ToString(v)
}
if v, ok := requestData["emailVisibility"]; ok {
if v, ok := requestData[schema.FieldNameEmailVisibility]; ok {
form.EmailVisibility = cast.ToBool(v)
}
if v, ok := requestData["verified"]; ok {
if v, ok := requestData[schema.FieldNameVerified]; ok {
form.Verified = cast.ToBool(v)
}
if v, ok := requestData["password"]; ok {
@@ -381,8 +370,16 @@ func (form *RecordUpsert) LoadData(requestData map[string]any) error {
}
}
// extend the record schema data with the request data
extendedData := form.record.SchemaData()
// replace modifiers (if any)
requestData = form.record.ReplaceModifers(requestData)
// create a shallow copy of form.data
var extendedData = make(map[string]any, len(form.data))
for k, v := range form.data {
extendedData[k] = v
}
// extend form.data with the request data
rawData, err := json.Marshal(requestData)
if err != nil {
return err
@@ -393,8 +390,7 @@ func (form *RecordUpsert) LoadData(requestData map[string]any) error {
for _, field := range form.record.Collection().Schema.Fields() {
key := field.Name
value := extendedData[key]
value = field.PrepareValue(value)
value := field.PrepareValue(extendedData[key])
if field.Type != schema.FieldTypeFile {
form.data[key] = value
@@ -405,30 +401,32 @@ func (form *RecordUpsert) LoadData(requestData map[string]any) error {
// Delete previously uploaded file(s)
// -----------------------------------------------------------
oldNames := list.ToUniqueStringSlice(form.data[key])
oldNames := form.record.GetStringSlice(key)
submittedNames := list.ToUniqueStringSlice(value)
// ensure that all submitted names are existing to prevent accidental files deletions
if len(submittedNames) > len(oldNames) || len(list.SubtractSlice(submittedNames, oldNames)) != 0 {
return validation.Errors{
key: validation.NewError(
"validation_unknown_filenames",
"The field contains unknown filenames.",
),
}
}
// if empty value was set, mark all previously uploaded files for deletion
if len(list.ToUniqueStringSlice(value)) == 0 && len(oldNames) > 0 {
// otherwise check for "deleted" (aka. unsubmitted) file names
if len(submittedNames) == 0 && len(oldNames) > 0 {
form.RemoveFiles(key)
} else if len(oldNames) > 0 {
toDelete := []string{}
// search for individual file name to delete (eg. "file.test.png = null")
for _, name := range oldNames {
if v, ok := extendedData[key+"."+name]; ok && cast.ToString(v) == "" {
// submitted as a modifier or a new array
if !list.ExistInSlice(name, submittedNames) {
toDelete = append(toDelete, name)
}
}
// search for individual file index to delete (eg. "file.0 = null")
keyExp, _ := regexp.Compile(`^` + regexp.QuoteMeta(key) + `\.\d+$`)
for indexedKey := range extendedData {
if keyExp.MatchString(indexedKey) && cast.ToString(extendedData[indexedKey]) == "" {
index, indexErr := strconv.Atoi(indexedKey[len(key)+1:])
if indexErr != nil || index >= len(oldNames) {
continue
}
toDelete = append(toDelete, oldNames[index])
continue
}
}
@@ -436,6 +434,12 @@ func (form *RecordUpsert) LoadData(requestData map[string]any) error {
form.RemoveFiles(key, toDelete...)
}
}
// allow file key reasignments for file names sorting
// (only if all submitted values already exists)
if len(submittedNames) > 0 && len(list.SubtractSlice(submittedNames, oldNames)) == 0 {
form.data[key] = submittedNames
}
}
return nil
+16 -14
View File
@@ -89,9 +89,10 @@ func TestRecordUpsertLoadRequestJson(t *testing.T) {
"unknown": "test456",
// file fields unset/delete
"file_one": nil,
"file_many.0": "", // delete by index
"file_many.1": "test.png", // should be ignored
"file_many.300_WlbFWSGmW9.png": nil, // delete by filename
"file_many.0": "", // delete by index
"file_many-": []string{"test_MaWC6mWyrP.txt", "test_tC1Yc87DfC.txt"}, // multiple delete with modifier
"file_many.300_WlbFWSGmW9.png": nil, // delete by filename
"file_many.2": "test.png", // should be ignored
},
},
}
@@ -149,11 +150,12 @@ func TestRecordUpsertLoadRequestMultipart(t *testing.T) {
"a.b.text": "test123",
"a.b.unknown": "test456",
// file fields unset/delete
"a.b.file_one": "",
"a.b.file_many.0": "",
"a.b.file_many.300_WlbFWSGmW9.png": "test.png", // delete by name
"a.b.file_many.1": "test.png", // should be ignored
}, "file_many")
"a.b.file_one-": "test_d61b33QdDU.txt", // delete with modifier
"a.b.file_many.0": "", // delete by index
"a.b.file_many-": "test_tC1Yc87DfC.txt", // delete with modifier
"a.b.file_many.300_WlbFWSGmW9.png": "", // delete by filename
"a.b.file_many.2": "test.png", // should be ignored
}, "a.b.file_many")
if err != nil {
t.Fatal(err)
}
@@ -191,7 +193,7 @@ func TestRecordUpsertLoadRequestMultipart(t *testing.T) {
t.Fatal("Expect file_many field to be set")
}
manyfilesRemains := len(list.ToUniqueStringSlice(fileMany))
expectedRemains := 2 // -2 from 3 removed + 1 new upload
expectedRemains := 3 // 5 old; 3 deleted and 1 new uploaded
if manyfilesRemains != expectedRemains {
t.Fatalf("Expect file_many to be %d, got %d (%v)", expectedRemains, manyfilesRemains, fileMany)
}
@@ -465,7 +467,7 @@ func TestRecordUpsertSubmitFailure(t *testing.T) {
if v := recordAfter.Get("email"); v == "invalid" {
t.Fatalf("Expected record.email not to change, got %v", v)
}
if v := recordAfter.GetStringSlice("file_many"); len(v) != 3 {
if v := recordAfter.GetStringSlice("file_many"); len(v) != 5 {
t.Fatalf("Expected record.file_many not to change, got %v", v)
}
@@ -537,8 +539,8 @@ func TestRecordUpsertSubmitSuccess(t *testing.T) {
}
fileMany := (recordAfter.GetStringSlice("file_many"))
if len(fileMany) != 4 { // 1 replace + 1 new
t.Fatalf("Expected 4 record.file_many, got %d (%v)", len(fileMany), fileMany)
if len(fileMany) != 6 { // 1 replace + 1 new
t.Fatalf("Expected 6 record.file_many, got %d (%v)", len(fileMany), fileMany)
}
for _, f := range fileMany {
if !hasRecordFile(app, recordAfter, f) {
@@ -1009,7 +1011,7 @@ func TestRecordUpsertAddAndRemoveFiles(t *testing.T) {
}
fileMany := recordAfter.GetStringSlice("file_many")
if len(fileMany) != 3 {
t.Fatalf("Expected file_many to be 3, got %v", fileMany)
if len(fileMany) != 5 {
t.Fatalf("Expected file_many to be 5, got %v", fileMany)
}
}