filter enhancements
This commit is contained in:
@@ -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.",
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user