initial v0.8 pre-release
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package validators
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -22,7 +21,7 @@ func UploadedFileSize(maxBytes int) validation.RuleFunc {
|
||||
return nil // nothing to validate
|
||||
}
|
||||
|
||||
if binary.Size(v.Bytes()) > maxBytes {
|
||||
if int(v.Header().Size) > maxBytes {
|
||||
return validation.NewError("validation_file_size_limit", fmt.Sprintf("Maximum allowed file size is %v bytes.", maxBytes))
|
||||
}
|
||||
|
||||
@@ -47,7 +46,16 @@ func UploadedFileMimeType(validTypes []string) validation.RuleFunc {
|
||||
return validation.NewError("validation_invalid_mime_type", "Unsupported file type.")
|
||||
}
|
||||
|
||||
filetype := mimetype.Detect(v.Bytes())
|
||||
f, err := v.Header().Open()
|
||||
if err != nil {
|
||||
return validation.NewError("validation_invalid_mime_type", "Unsupported file type.")
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
filetype, err := mimetype.DetectReader(f)
|
||||
if err != nil {
|
||||
return validation.NewError("validation_invalid_mime_type", "Unsupported file type.")
|
||||
}
|
||||
|
||||
for _, t := range validTypes {
|
||||
if filetype.Is(t) {
|
||||
|
||||
@@ -28,7 +28,7 @@ var requiredErr = validation.NewError("validation_required", "Missing required v
|
||||
func NewRecordDataValidator(
|
||||
dao *daos.Dao,
|
||||
record *models.Record,
|
||||
uploadedFiles []*rest.UploadedFile,
|
||||
uploadedFiles map[string][]*rest.UploadedFile,
|
||||
) *RecordDataValidator {
|
||||
return &RecordDataValidator{
|
||||
dao: dao,
|
||||
@@ -42,7 +42,7 @@ func NewRecordDataValidator(
|
||||
type RecordDataValidator struct {
|
||||
dao *daos.Dao
|
||||
record *models.Record
|
||||
uploadedFiles []*rest.UploadedFile
|
||||
uploadedFiles map[string][]*rest.UploadedFile
|
||||
}
|
||||
|
||||
// Validate validates the provided `data` by checking it against
|
||||
@@ -88,7 +88,7 @@ func (validator *RecordDataValidator) Validate(data map[string]any) error {
|
||||
|
||||
// check unique constraint
|
||||
if field.Unique && !validator.dao.IsRecordValueUnique(
|
||||
validator.record.Collection(),
|
||||
validator.record.Collection().Id,
|
||||
key,
|
||||
value,
|
||||
validator.record.GetId(),
|
||||
@@ -127,8 +127,6 @@ func (validator *RecordDataValidator) checkFieldValue(field *schema.SchemaField,
|
||||
return validator.checkFileValue(field, value)
|
||||
case schema.FieldTypeRelation:
|
||||
return validator.checkRelationValue(field, value)
|
||||
case schema.FieldTypeUser:
|
||||
return validator.checkUserValue(field, value)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -316,8 +314,8 @@ func (validator *RecordDataValidator) checkFileValue(field *schema.SchemaField,
|
||||
}
|
||||
|
||||
// extract the uploaded files
|
||||
files := make([]*rest.UploadedFile, 0, len(validator.uploadedFiles))
|
||||
for _, file := range validator.uploadedFiles {
|
||||
files := make([]*rest.UploadedFile, 0, len(validator.uploadedFiles[field.Name]))
|
||||
for _, file := range validator.uploadedFiles[field.Name] {
|
||||
if list.ExistInSlice(file.Name(), names) {
|
||||
files = append(files, file)
|
||||
}
|
||||
@@ -351,8 +349,8 @@ func (validator *RecordDataValidator) checkRelationValue(field *schema.SchemaFie
|
||||
|
||||
options, _ := field.Options.(*schema.RelationOptions)
|
||||
|
||||
if len(ids) > options.MaxSelect {
|
||||
return validation.NewError("validation_too_many_values", fmt.Sprintf("Select no more than %d", options.MaxSelect))
|
||||
if options.MaxSelect != nil && len(ids) > *options.MaxSelect {
|
||||
return validation.NewError("validation_too_many_values", fmt.Sprintf("Select no more than %d", *options.MaxSelect))
|
||||
}
|
||||
|
||||
// check if the related records exist
|
||||
@@ -374,31 +372,3 @@ func (validator *RecordDataValidator) checkRelationValue(field *schema.SchemaFie
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (validator *RecordDataValidator) checkUserValue(field *schema.SchemaField, value any) error {
|
||||
ids := list.ToUniqueStringSlice(value)
|
||||
if len(ids) == 0 {
|
||||
if field.Required {
|
||||
return requiredErr
|
||||
}
|
||||
return nil // nothing to check
|
||||
}
|
||||
|
||||
options, _ := field.Options.(*schema.UserOptions)
|
||||
|
||||
if len(ids) > options.MaxSelect {
|
||||
return validation.NewError("validation_too_many_values", fmt.Sprintf("Select no more than %d", options.MaxSelect))
|
||||
}
|
||||
|
||||
// check if the related users exist
|
||||
var total int
|
||||
validator.dao.UserQuery().
|
||||
Select("count(*)").
|
||||
AndWhere(dbx.In("id", list.ToInterfaceSlice(ids)...)).
|
||||
Row(&total)
|
||||
if total != len(ids) {
|
||||
return validation.NewError("validation_missing_users", "Failed to fetch all users with the provided ids")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
type testDataFieldScenario struct {
|
||||
name string
|
||||
data map[string]any
|
||||
files []*rest.UploadedFile
|
||||
files map[string][]*rest.UploadedFile
|
||||
expectedErrors []string
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func TestRecordDataValidatorEmptyAndUnknown(t *testing.T) {
|
||||
app, _ := tests.NewTestApp()
|
||||
defer app.Cleanup()
|
||||
|
||||
collection, _ := app.Dao().FindCollectionByNameOrId("demo")
|
||||
collection, _ := app.Dao().FindCollectionByNameOrId("demo2")
|
||||
record := models.NewRecord(collection)
|
||||
validator := validators.NewRecordDataValidator(app.Dao(), record, nil)
|
||||
|
||||
@@ -80,9 +80,9 @@ func TestRecordDataValidatorValidateText(t *testing.T) {
|
||||
|
||||
// create dummy record (used for the unique check)
|
||||
dummy := models.NewRecord(collection)
|
||||
dummy.SetDataValue("field1", "test")
|
||||
dummy.SetDataValue("field2", "test")
|
||||
dummy.SetDataValue("field3", "test")
|
||||
dummy.Set("field1", "test")
|
||||
dummy.Set("field2", "test")
|
||||
dummy.Set("field3", "test")
|
||||
if err := app.Dao().SaveRecord(dummy); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -196,9 +196,9 @@ func TestRecordDataValidatorValidateNumber(t *testing.T) {
|
||||
|
||||
// create dummy record (used for the unique check)
|
||||
dummy := models.NewRecord(collection)
|
||||
dummy.SetDataValue("field1", 123)
|
||||
dummy.SetDataValue("field2", 123)
|
||||
dummy.SetDataValue("field3", 123)
|
||||
dummy.Set("field1", 123)
|
||||
dummy.Set("field2", 123)
|
||||
dummy.Set("field3", 123)
|
||||
if err := app.Dao().SaveRecord(dummy); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -307,9 +307,9 @@ func TestRecordDataValidatorValidateBool(t *testing.T) {
|
||||
|
||||
// create dummy record (used for the unique check)
|
||||
dummy := models.NewRecord(collection)
|
||||
dummy.SetDataValue("field1", false)
|
||||
dummy.SetDataValue("field2", true)
|
||||
dummy.SetDataValue("field3", true)
|
||||
dummy.Set("field1", false)
|
||||
dummy.Set("field2", true)
|
||||
dummy.Set("field3", true)
|
||||
if err := app.Dao().SaveRecord(dummy); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -403,9 +403,9 @@ func TestRecordDataValidatorValidateEmail(t *testing.T) {
|
||||
|
||||
// create dummy record (used for the unique check)
|
||||
dummy := models.NewRecord(collection)
|
||||
dummy.SetDataValue("field1", "test@demo.com")
|
||||
dummy.SetDataValue("field2", "test@test.com")
|
||||
dummy.SetDataValue("field3", "test@example.com")
|
||||
dummy.Set("field1", "test@demo.com")
|
||||
dummy.Set("field2", "test@test.com")
|
||||
dummy.Set("field3", "test@example.com")
|
||||
if err := app.Dao().SaveRecord(dummy); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -519,9 +519,9 @@ func TestRecordDataValidatorValidateUrl(t *testing.T) {
|
||||
|
||||
// create dummy record (used for the unique check)
|
||||
dummy := models.NewRecord(collection)
|
||||
dummy.SetDataValue("field1", "http://demo.com")
|
||||
dummy.SetDataValue("field2", "http://test.com")
|
||||
dummy.SetDataValue("field3", "http://example.com")
|
||||
dummy.Set("field1", "http://demo.com")
|
||||
dummy.Set("field2", "http://test.com")
|
||||
dummy.Set("field3", "http://example.com")
|
||||
if err := app.Dao().SaveRecord(dummy); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -647,9 +647,9 @@ func TestRecordDataValidatorValidateDate(t *testing.T) {
|
||||
|
||||
// create dummy record (used for the unique check)
|
||||
dummy := models.NewRecord(collection)
|
||||
dummy.SetDataValue("field1", "2022-01-01 01:01:01")
|
||||
dummy.SetDataValue("field2", "2029-01-01 01:01:01.123")
|
||||
dummy.SetDataValue("field3", "2029-01-01 01:01:01.123")
|
||||
dummy.Set("field1", "2022-01-01 01:01:01")
|
||||
dummy.Set("field2", "2029-01-01 01:01:01.123")
|
||||
dummy.Set("field3", "2029-01-01 01:01:01.123")
|
||||
if err := app.Dao().SaveRecord(dummy); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -779,9 +779,9 @@ func TestRecordDataValidatorValidateSelect(t *testing.T) {
|
||||
|
||||
// create dummy record (used for the unique check)
|
||||
dummy := models.NewRecord(collection)
|
||||
dummy.SetDataValue("field1", "a")
|
||||
dummy.SetDataValue("field2", []string{"a", "b"})
|
||||
dummy.SetDataValue("field3", []string{"a", "b", "c"})
|
||||
dummy.Set("field1", "a")
|
||||
dummy.Set("field2", []string{"a", "b"})
|
||||
dummy.Set("field3", []string{"a", "b", "c"})
|
||||
if err := app.Dao().SaveRecord(dummy); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -909,9 +909,9 @@ func TestRecordDataValidatorValidateJson(t *testing.T) {
|
||||
|
||||
// create dummy record (used for the unique check)
|
||||
dummy := models.NewRecord(collection)
|
||||
dummy.SetDataValue("field1", `{"test":123}`)
|
||||
dummy.SetDataValue("field2", `{"test":123}`)
|
||||
dummy.SetDataValue("field3", `{"test":123}`)
|
||||
dummy.Set("field1", `{"test":123}`)
|
||||
dummy.Set("field2", `{"test":123}`)
|
||||
dummy.Set("field3", `{"test":123}`)
|
||||
if err := app.Dao().SaveRecord(dummy); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -1080,7 +1080,9 @@ func TestRecordDataValidatorValidateFile(t *testing.T) {
|
||||
"field2": []string{"test1", testFiles[0].Name(), testFiles[3].Name()},
|
||||
"field3": []string{"test1", "test2", "test3", "test4"},
|
||||
},
|
||||
[]*rest.UploadedFile{testFiles[0], testFiles[1], testFiles[2]},
|
||||
map[string][]*rest.UploadedFile{
|
||||
"field2": {testFiles[0], testFiles[3]},
|
||||
},
|
||||
[]string{"field2", "field3"},
|
||||
},
|
||||
{
|
||||
@@ -1090,7 +1092,10 @@ func TestRecordDataValidatorValidateFile(t *testing.T) {
|
||||
"field2": []string{"test1", testFiles[0].Name()},
|
||||
"field3": []string{"test1", "test2", "test3"},
|
||||
},
|
||||
[]*rest.UploadedFile{testFiles[0], testFiles[1], testFiles[2]},
|
||||
map[string][]*rest.UploadedFile{
|
||||
"field1": {testFiles[0]},
|
||||
"field2": {testFiles[0]},
|
||||
},
|
||||
[]string{"field1"},
|
||||
},
|
||||
{
|
||||
@@ -1100,7 +1105,10 @@ func TestRecordDataValidatorValidateFile(t *testing.T) {
|
||||
"field2": []string{"test1", testFiles[0].Name()},
|
||||
"field3": []string{testFiles[1].Name(), testFiles[2].Name()},
|
||||
},
|
||||
[]*rest.UploadedFile{testFiles[0], testFiles[1], testFiles[2]},
|
||||
map[string][]*rest.UploadedFile{
|
||||
"field2": {testFiles[0], testFiles[1], testFiles[2]},
|
||||
"field3": {testFiles[1], testFiles[2]},
|
||||
},
|
||||
[]string{"field3"},
|
||||
},
|
||||
{
|
||||
@@ -1120,7 +1128,9 @@ func TestRecordDataValidatorValidateFile(t *testing.T) {
|
||||
"field2": []string{testFiles[0].Name(), testFiles[1].Name()},
|
||||
"field3": nil,
|
||||
},
|
||||
[]*rest.UploadedFile{testFiles[0], testFiles[1], testFiles[2]},
|
||||
map[string][]*rest.UploadedFile{
|
||||
"field2": {testFiles[0], testFiles[1]},
|
||||
},
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
@@ -1130,7 +1140,9 @@ func TestRecordDataValidatorValidateFile(t *testing.T) {
|
||||
"field2": []string{"test1", testFiles[0].Name()},
|
||||
"field3": "test1", // will be casted
|
||||
},
|
||||
[]*rest.UploadedFile{testFiles[0], testFiles[1], testFiles[2]},
|
||||
map[string][]*rest.UploadedFile{
|
||||
"field2": {testFiles[0], testFiles[1], testFiles[2]},
|
||||
},
|
||||
[]string{},
|
||||
},
|
||||
}
|
||||
@@ -1142,17 +1154,17 @@ func TestRecordDataValidatorValidateRelation(t *testing.T) {
|
||||
app, _ := tests.NewTestApp()
|
||||
defer app.Cleanup()
|
||||
|
||||
demo, _ := app.Dao().FindCollectionByNameOrId("demo4")
|
||||
demo, _ := app.Dao().FindCollectionByNameOrId("demo3")
|
||||
|
||||
// demo4 rel ids
|
||||
relId1 := "b8ba58f9-e2d7-42a0-b0e7-a11efd98236b"
|
||||
relId2 := "df55c8ff-45ef-4c82-8aed-6e2183fe1125"
|
||||
relId3 := "b84cd893-7119-43c9-8505-3c4e22da28a9"
|
||||
relId4 := "054f9f24-0a0a-4e09-87b1-bc7ff2b336a2"
|
||||
// demo3 rel ids
|
||||
relId1 := "mk5fmymtx4wsprk"
|
||||
relId2 := "7nwo8tuiatetxdm"
|
||||
relId3 := "lcl9d87w22ml6jy"
|
||||
relId4 := "1tmknxy2868d869"
|
||||
|
||||
// record rel ids from different collections
|
||||
diffRelId1 := "63c2ab80-84ab-4057-a592-4604a731f78f"
|
||||
diffRelId2 := "2c542824-9de1-42fe-8924-e57c86267760"
|
||||
diffRelId1 := "0yxhwia2amd8gec"
|
||||
diffRelId2 := "llvuca81nly1qls"
|
||||
|
||||
// create new test collection
|
||||
collection := &models.Collection{}
|
||||
@@ -1162,7 +1174,7 @@ func TestRecordDataValidatorValidateRelation(t *testing.T) {
|
||||
Name: "field1",
|
||||
Type: schema.FieldTypeRelation,
|
||||
Options: &schema.RelationOptions{
|
||||
MaxSelect: 1,
|
||||
MaxSelect: types.Pointer(1),
|
||||
CollectionId: demo.Id,
|
||||
},
|
||||
},
|
||||
@@ -1171,7 +1183,7 @@ func TestRecordDataValidatorValidateRelation(t *testing.T) {
|
||||
Required: true,
|
||||
Type: schema.FieldTypeRelation,
|
||||
Options: &schema.RelationOptions{
|
||||
MaxSelect: 2,
|
||||
MaxSelect: types.Pointer(2),
|
||||
CollectionId: demo.Id,
|
||||
},
|
||||
},
|
||||
@@ -1180,7 +1192,6 @@ func TestRecordDataValidatorValidateRelation(t *testing.T) {
|
||||
Unique: true,
|
||||
Type: schema.FieldTypeRelation,
|
||||
Options: &schema.RelationOptions{
|
||||
MaxSelect: 3,
|
||||
CollectionId: demo.Id,
|
||||
},
|
||||
},
|
||||
@@ -1188,7 +1199,7 @@ func TestRecordDataValidatorValidateRelation(t *testing.T) {
|
||||
Name: "field4",
|
||||
Type: schema.FieldTypeRelation,
|
||||
Options: &schema.RelationOptions{
|
||||
MaxSelect: 3,
|
||||
MaxSelect: types.Pointer(3),
|
||||
CollectionId: "", // missing or non-existing collection id
|
||||
},
|
||||
},
|
||||
@@ -1199,9 +1210,9 @@ func TestRecordDataValidatorValidateRelation(t *testing.T) {
|
||||
|
||||
// create dummy record (used for the unique check)
|
||||
dummy := models.NewRecord(collection)
|
||||
dummy.SetDataValue("field1", relId1)
|
||||
dummy.SetDataValue("field2", []string{relId1, relId2})
|
||||
dummy.SetDataValue("field3", []string{relId1, relId2, relId3})
|
||||
dummy.Set("field1", relId1)
|
||||
dummy.Set("field2", []string{relId1, relId2})
|
||||
dummy.Set("field3", []string{relId1, relId2, relId3})
|
||||
if err := app.Dao().SaveRecord(dummy); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -1254,7 +1265,7 @@ func TestRecordDataValidatorValidateRelation(t *testing.T) {
|
||||
"field3": []string{relId1, relId2, relId3, relId4},
|
||||
},
|
||||
nil,
|
||||
[]string{"field2", "field3"},
|
||||
[]string{"field2"},
|
||||
},
|
||||
{
|
||||
"check with ids from different collections",
|
||||
@@ -1289,130 +1300,6 @@ func TestRecordDataValidatorValidateRelation(t *testing.T) {
|
||||
checkValidatorErrors(t, app.Dao(), models.NewRecord(collection), scenarios)
|
||||
}
|
||||
|
||||
func TestRecordDataValidatorValidateUser(t *testing.T) {
|
||||
app, _ := tests.NewTestApp()
|
||||
defer app.Cleanup()
|
||||
|
||||
userId1 := "97cc3d3d-6ba2-383f-b42a-7bc84d27410c"
|
||||
userId2 := "7bc84d27-6ba2-b42a-383f-4197cc3d3d0c"
|
||||
userId3 := "4d0197cc-2b4a-3f83-a26b-d77bc8423d3c"
|
||||
missingUserId := "00000000-84ab-4057-a592-4604a731f78f"
|
||||
|
||||
// create new test collection
|
||||
collection := &models.Collection{}
|
||||
collection.Name = "validate_test"
|
||||
collection.Schema = schema.NewSchema(
|
||||
&schema.SchemaField{
|
||||
Name: "field1",
|
||||
Type: schema.FieldTypeUser,
|
||||
Options: &schema.UserOptions{
|
||||
MaxSelect: 1,
|
||||
},
|
||||
},
|
||||
&schema.SchemaField{
|
||||
Name: "field2",
|
||||
Required: true,
|
||||
Type: schema.FieldTypeUser,
|
||||
Options: &schema.UserOptions{
|
||||
MaxSelect: 2,
|
||||
},
|
||||
},
|
||||
&schema.SchemaField{
|
||||
Name: "field3",
|
||||
Unique: true,
|
||||
Type: schema.FieldTypeUser,
|
||||
Options: &schema.UserOptions{
|
||||
MaxSelect: 3,
|
||||
},
|
||||
},
|
||||
)
|
||||
if err := app.Dao().SaveCollection(collection); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// create dummy record (used for the unique check)
|
||||
dummy := models.NewRecord(collection)
|
||||
dummy.SetDataValue("field1", userId1)
|
||||
dummy.SetDataValue("field2", []string{userId1, userId2})
|
||||
dummy.SetDataValue("field3", []string{userId1, userId2, userId3})
|
||||
if err := app.Dao().SaveRecord(dummy); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
scenarios := []testDataFieldScenario{
|
||||
{
|
||||
"check required constraint - nil",
|
||||
map[string]any{
|
||||
"field1": nil,
|
||||
"field2": nil,
|
||||
"field3": nil,
|
||||
},
|
||||
nil,
|
||||
[]string{"field2"},
|
||||
},
|
||||
{
|
||||
"check required constraint - zero id",
|
||||
map[string]any{
|
||||
"field1": "",
|
||||
"field2": "",
|
||||
"field3": "",
|
||||
},
|
||||
nil,
|
||||
[]string{"field2"},
|
||||
},
|
||||
{
|
||||
"check unique constraint",
|
||||
map[string]any{
|
||||
"field1": nil,
|
||||
"field2": userId1,
|
||||
"field3": []string{userId1, userId2, userId3, userId3}, // repeating values are collapsed
|
||||
},
|
||||
nil,
|
||||
[]string{"field3"},
|
||||
},
|
||||
{
|
||||
"check MaxSelect constraint",
|
||||
map[string]any{
|
||||
"field1": []string{userId1, userId2}, // maxSelect is 1 and will be normalized to userId1 only
|
||||
"field2": []string{userId1, userId2, userId3},
|
||||
"field3": []string{userId1, userId3, userId2},
|
||||
},
|
||||
nil,
|
||||
[]string{"field2"},
|
||||
},
|
||||
{
|
||||
"check with mixed existing and nonexisting user ids",
|
||||
map[string]any{
|
||||
"field1": missingUserId,
|
||||
"field2": []string{missingUserId, userId1},
|
||||
"field3": []string{userId1, missingUserId},
|
||||
},
|
||||
nil,
|
||||
[]string{"field1", "field2", "field3"},
|
||||
},
|
||||
{
|
||||
"valid data - only required fields",
|
||||
map[string]any{
|
||||
"field2": []string{userId1, userId2},
|
||||
},
|
||||
nil,
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
"valid data - all fields with normalization",
|
||||
map[string]any{
|
||||
"field1": []string{userId1, userId2},
|
||||
"field2": userId2,
|
||||
"field3": []string{userId3, userId2, userId1}, // unique is not triggered because the order is different
|
||||
},
|
||||
nil,
|
||||
[]string{},
|
||||
},
|
||||
}
|
||||
|
||||
checkValidatorErrors(t, app.Dao(), models.NewRecord(collection), scenarios)
|
||||
}
|
||||
|
||||
func checkValidatorErrors(t *testing.T, dao *daos.Dao, record *models.Record, scenarios []testDataFieldScenario) {
|
||||
for i, s := range scenarios {
|
||||
validator := validators.NewRecordDataValidator(dao, record, s.files)
|
||||
|
||||
Reference in New Issue
Block a user