func getAllFields(model interface{}) map[field.Name]reflect.Value { fields := map[field.Name]reflect.Value{} // Value of model modelValue := reflect.ValueOf(model) if modelValue.Kind() == reflect.Ptr { modelValue = modelValue.Elem() } // Type of Model modelType := reflect.TypeOf(model) if modelType.Kind() == reflect.Ptr { modelType = modelType.Elem() } for i := 0; i < modelType.NumField(); i++ { v := modelValue.Field(i) // Straight up struct field of type field.Field if v.CanAddr() == true && v.Addr().Type().Implements(fieldType) == true { fieldName := field.Name(modelType.Field(i).Name) fields[fieldName] = modelValue.Field(i) } else if v.Kind() == reflect.Struct { //t := modelType.Field(i) // Embedded Struct with potential field.Field fields fieldName := field.Name(modelType.Field(i).Name) fields[fieldName] = modelValue.Field(i) } } return fields }
func modelFields(model interface{}) field.Names { fields := make(field.Names, 0) // Value of model ifv := reflect.ValueOf(model) if ifv.Kind() == reflect.Ptr { ifv = ifv.Elem() } // Type of Model itf := reflect.TypeOf(model) if itf.Kind() == reflect.Ptr { itf = itf.Elem() } for i := 0; i < itf.NumField(); i++ { v := ifv.Field(i) // Straight up struct field of type field.Field if v.CanAddr() == true && v.Addr().Type().Implements(fieldType) == true { fields = append(fields, field.Name(itf.Field(i).Name)) } else { t := itf.Field(i) // Embedded Struct with potential field.Field fields if t.Anonymous == true && v.CanAddr() == true && v.Kind() == reflect.Struct { fields = append(fields, modelFields(v.Addr().Interface())...) } else if t.Anonymous == true && v.CanAddr() == true && v.Kind() == reflect.Interface { // Embedded Model interface fields = append(fields, modelFields(v.Elem().Interface())...) } } } return fields }
func (*MockModelCustomPrimaryKey) PrimaryKey() PrimaryKeyer { return NewCustomPrimaryKey(field.Names{"Id"}, func(pk PrimaryKeyer, model Model) (field.Names, error) { f, _ := ModelGetField(model, field.Name("Id")) f.Scan("abc-123-xyz-789") return field.Names{"Id"}, nil }) }
func TestPrimaryKeyer(t *testing.T) { Convey("PrimaryKeyer", t, func() { model := &MockModel{} Convey("SinglePrimaryKey", func() { pk := NewSinglePrimaryKey(field.Name("Id")) So(pk.Fields(), ShouldResemble, field.Names{field.Name("Id")}) fields, err := model.PrimaryKey().Generator(model) So(len(fields), ShouldEqual, 0) So(err, ShouldBeNil) v, _ := model.Id.Value() So(v, ShouldBeNil) }) Convey("MultiplePrimaryKey", func() { pk := NewMultiplePrimaryKey(field.Names{"Id", "Org"}) So(pk.Fields(), ShouldResemble, field.Names{"Id", "Org"}) fields, err := model.PrimaryKey().Generator(model) So(len(fields), ShouldEqual, 0) So(err, ShouldBeNil) v, _ := model.Id.Value() So(v, ShouldBeNil) v, _ = model.Org.Value() So(v, ShouldBeNil) }) Convey("CustomPrimaryKey", func() { pk := NewCustomPrimaryKey(field.Names{"Id", "Org"}, func(pk PrimaryKeyer, model Model) (field.Names, error) { f, _ := ModelGetField(model, "Id") f.Scan("abc-123") return field.Names{"Id"}, nil }) fields, err := pk.Generator(model) So(len(fields), ShouldEqual, 1) So(err, ShouldBeNil) v, _ := model.Id.Value() So(v, ShouldEqual, "abc-123") }) }) }
func ExampleModelGetField_() { user := &User{} user.Id.Scan(1234) modelField, err := norm.ModelGetField(user, field.Name("Id")) if err != nil { fmt.Println(err.Error()) } outputJson(modelField) // Output: // 1234 }
func (u *User) PrimaryKey() norm.PrimaryKeyer { return norm.NewSinglePrimaryKey(field.Name("Id")) }
func (u *User) PrimaryKeyFieldName() field.Name { return field.Name("Id") }
func TestValidator(t *testing.T) { Convey("ValidatorCache", t, func() { var ( cache ValidatorCache = make(ValidatorCache, 1) validators []FieldValidator ) Convey("Get", func() { Convey("Not set", func() { So(cache.Get(&MockModel{}), ShouldBeEmpty) }) Convey("When Set", func() { mv := &MockValidator{} validators = append(validators, mv) cache.Set(&MockModel{}, validators) So(cache.Get(&MockModel{}), ShouldContain, mv) }) }) Convey("Set", func() { validators = append(validators, &MockValidator{}) cache.Set(&MockModel{}, validators) So(cache.Get(&MockModel{}), ShouldResemble, validators) }) Convey("Del", func() { validators = append(validators, &MockValidator{}) cache.Set(&MockModel{}, validators) cache.Del(&MockModel{}) So(len(cache.Get(&MockModel{})), ShouldEqual, 0) }) Convey("Clone", func() { validators = append(validators, &MockValidator{}) cache.Set(&MockModel{}, validators) So(cache.Clone(), ShouldResemble, cache) }) Convey("Validate", func() { var ( fv1, fv2 FieldValidator m *MockModel ) fv1 = NewFieldValidator(field.Name("FirstName"), "value_match", MockFieldValidatorFunc, "test") fv2 = NewFieldValidator(field.Name("Org"), "value_match", MockFieldValidatorFunc, "picatic") m = &MockModel{} cache.Set(m, []FieldValidator{fv1, fv2}) Convey("Single Error via Names", func() { err := cache.Validate(nil, m, field.Names{"FirstName"}) So(err, ShouldNotBeNil) So(len(err.Errors), ShouldEqual, 1) }) Convey("Multiple Errors", func() { err := cache.Validate(nil, m, field.Names{"FirstName", "Org"}) So(err, ShouldNotBeNil) So(len(err.Errors), ShouldEqual, 2) }) Convey("No errors", func() { m.FirstName.Scan("test") m.Org.Scan("picatic") err := cache.Validate(nil, m, field.Names{"FirstName", "Org"}) So(err, ShouldBeNil) }) }) }) Convey("ModelValidator", t, func() { normConn := NewConnection(nil, "picatic", nil) Convey("nil error follows through", func() { m := &MockModel{} m.FirstName.Scan("Pete") err := ModelValidate(normConn.NewSession(nil), m, nil) So(err, ShouldBeNil) So(err == nil, ShouldBeTrue) }) Convey("error passed", func() { m := &MockModel{} m.FirstName.Scan("Not Pete") err := ModelValidate(normConn.NewSession(nil), m, nil) So(err, ShouldNotBeNil) }) }) Convey("FieldValidator", t, func() { var ( fv FieldValidator m *MockModel ) fv = NewFieldValidator(field.Name("FirstName"), "value_match", MockFieldValidatorFunc, "test") m = &MockModel{} Convey("Pass", func() { m.FirstName.Scan("test") err := fv.Validate(nil, m) So(err, ShouldBeNil) }) Convey("Fail", func() { m.FirstName.Scan("duck") err := fv.Validate(nil, m) So(err, ShouldNotBeNil) So(err, ShouldHaveSameTypeAs, &FieldValidationError{}) So(fmt.Sprintf("%s", err), ShouldEqual, "Value [duck] did not equal first argument [test]") }) }) Convey("ValidationError", t, func() { var ( ve *ValidationError ) Convey("New", func() { ve = NewValidationError("id", "alias", "invalid") So(ve.Field, ShouldEqual, "id") So(ve.Message, ShouldEqual, "invalid") So(ve.Alias, ShouldEqual, "alias") So(ve.Error(), ShouldEqual, "Field: [id] Alias: [alias] Message: invalid") }) }) Convey("ValidationErrors", t, func() { var ( ves *ValidationErrors ) ves = &ValidationErrors{} Convey("Empty", func() { So(ves.Error(), ShouldEqual, "Empty errors") }) Convey("Only ValidationError", func() { ves.Add(NewValidationError(field.Name("id"), "alias", "mega message")) So(ves.Error(), ShouldEqual, "Field: [id] Alias: [alias] Message: mega message") }) Convey("Not ValidationError", func() { ves.Add(fmt.Errorf("not a ValidationError")) So(ves.Error(), ShouldEqual, "not a ValidationError") }) }) }
func TestModel(t *testing.T) { Convey("Model", t, func() { db, mock, _ := sqlmock.New() conn := NewConnection(db, "mock_db", nil) model := &MockModel{} model.Id.Scan("1") model.FirstName.Scan("Mock") modelWithEmbedded := &MockModelEmbedded{} Convey("ModelFields", func() { Convey("On Ptr to Struct", func() { fields := ModelFields(model) So(fields, ShouldContain, field.Name("Id")) So(fields, ShouldContain, field.Name("FirstName")) So(fields, ShouldContain, field.Name("Org")) So(len(fields), ShouldEqual, 3) }) Convey("With embedded struct", func() { fields := ModelFields(modelWithEmbedded) So(fields, ShouldContain, field.Name("Id")) So(fields, ShouldContain, field.Name("FirstName")) So(fields, ShouldContain, field.Name("Org")) So(fields, ShouldContain, field.Name("Created")) So(fields, ShouldContain, field.Name("Modified")) So(len(fields), ShouldEqual, 5) }) Convey("With embedded Model interface", func() { m := &MockModelInterfaceEmbedded{model} fields := ModelFields(m) So(fields, ShouldContain, field.Name("Id")) So(fields, ShouldContain, field.Name("FirstName")) So(fields, ShouldContain, field.Name("Org")) So(len(fields), ShouldEqual, 3) }) }) Convey("ModelGetField", func() { Convey("When field exists", func() { rawModelField, err := ModelGetField(model, "Id") So(err, ShouldBeNil) f, ok := rawModelField.(*field.NullString) So(ok, ShouldBeTrue) So(f.String, ShouldEqual, "1") }) Convey("When field does not exist", func() { rawModelField, err := ModelGetField(model, "NotAField") So(rawModelField, ShouldBeNil) So(err, ShouldNotBeNil) }) Convey("Field from Embedded struct", func() { modelWithEmbedded.Id.Scan("12") idField, err := ModelGetField(modelWithEmbedded, "Id") So(err, ShouldBeNil) f, ok := idField.(*field.NullString) So(ok, ShouldBeTrue) So(f.String, ShouldEqual, "12") }) Convey("Fields from embedded Model interface", func() { m := &MockModelInterfaceEmbedded{Model: model} rawModelField, err := ModelGetField(m, "Id") So(err, ShouldBeNil) f, ok := rawModelField.(*field.NullString) So(ok, ShouldBeTrue) So(f.String, ShouldEqual, "1") }) }) Convey("ModelTableName", func() { Convey("Builds table name with connection database prepended", func() { sess := conn.NewSession(nil) So(ModelTableName(sess, model), ShouldEqual, "mock_db.mocks") }) }) Convey("NewSelect", func() { Convey("Without fields", func() { mock.ExpectQuery("SELECT `id`, `first_name`, `org` FROM mock_db\\.mocks").WillReturnRows(sqlmock.NewRows([]string{"id", "first_name"}).FromCSVString("2,mocker")) err := NewSelect(conn.NewSession(nil), model, nil).LoadStruct(model) So(err, ShouldBeNil) }) Convey("With fields", func() { mock.ExpectQuery("SELECT `id` FROM mock_db\\.mocks").WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("2")) err := NewSelect(conn.NewSession(nil), model, field.Names{"Id"}).LoadStruct(model) So(err, ShouldBeNil) So(model.Id.String, ShouldEqual, "2") }) }) Convey("NewInsert", func() { Convey("Without fields", func() { mock.ExpectExec("INSERT INTO `mock_db`\\.`mocks` \\(`first_name`,`org`\\) VALUES \\('Mock',NULL\\)").WillReturnResult(sqlmock.NewResult(2, 1)) _, err := NewInsert(conn.NewSession(nil), model, nil).Record(model).Exec() So(err, ShouldBeNil) }) Convey("With fields", func() { mock.ExpectExec("INSERT INTO `mock_db`\\.`mocks` \\(`first_name`\\) VALUES \\('Mock'\\)").WillReturnResult(sqlmock.NewResult(3, 1)) _, err := NewInsert(conn.NewSession(nil), model, field.Names{"FirstName"}).Record(model).Exec() So(err, ShouldBeNil) }) Convey("With Custom Primary Key", func() { modelCust := &MockModelCustomPrimaryKey{} modelCust.FirstName.Scan("Custom Key") mock.ExpectExec("INSERT INTO `mock_db`\\.`mocks` \\((`id`|,|`first_name`)+\\) VALUES \\(('abc-123-xyz-789'|,|'Custom Key')+\\)").WillReturnResult(sqlmock.NewResult(4, 1)) _, err := NewInsert(conn.NewSession(nil), modelCust, field.Names{"FirstName"}).Record(modelCust).Exec() So(err, ShouldBeNil) }) }) Convey("NewUpdate", func() { Convey("Without fields", func() { mock.ExpectExec("UPDATE `mock_db`\\.`mocks` SET (`first_name` = 'Mock'|, |`org` = NULL)+ WHERE \\(id = '1'\\)").WillReturnResult(sqlmock.NewResult(0, 1)) _, err := NewUpdate(conn.NewSession(nil), model, nil).Where("id = ?", model.Id.String).Exec() So(err, ShouldBeNil) }) Convey("With fields", func() { mock.ExpectExec("UPDATE `mock_db`\\.`mocks` SET `first_name` = 'Mock' WHERE \\(id = '1'\\)").WillReturnResult(sqlmock.NewResult(0, 1)) _, err := NewUpdate(conn.NewSession(nil), model, field.Names{"FirstName"}).Where("id = ?", model.Id.String).Exec() So(err, ShouldBeNil) }) }) Convey("ModelLoadMap", func() { dataMap := map[string]interface{}{ "id": "1234", "first_name": "James", } ModelLoadMap(model, dataMap) So(model.Id.String, ShouldEqual, "1234") So(model.FirstName.String, ShouldEqual, "James") }) Convey("ModelChangedFields", func() { model := &MockModel{} model.Id.Scan("1") model.FirstName.Scan("James James James") Convey("No changed fields", func() { f, err := ModelDirtyFields(model) So(len(f), ShouldEqual, 0) So(err, ShouldBeNil) }) Convey("Changed", func() { model.FirstName.Scan("Santa") f, err := ModelDirtyFields(model) So(len(f), ShouldEqual, 1) So(err, ShouldBeNil) }) }) Convey("ModelGetSetFields", func() { model := &MockModel{} Convey("No set fields", func() { f, err := ModelGetSetFields(model) So(len(f), ShouldEqual, 0) So(err, ShouldBeNil) }) Convey("Changed", func() { model.Id.Scan("1") model.FirstName.Scan("James James James") f, err := ModelGetSetFields(model) So(len(f), ShouldEqual, 2) So(err, ShouldBeNil) }) }) Convey("ModelValidate", func() { Convey("With validation error", func() { err := ModelValidate(conn.NewSession(nil), model, nil) So(err, ShouldNotBeNil) So(err, ShouldHaveSameTypeAs, &ValidationErrors{}) So(err.Error(), ShouldEqual, "Field: [FirstName] Alias: [matches] Message: Value [Mock] did not equal first argument [Pete]") }) Convey("Without validation error", func() { model.FirstName.Scan("Pete") err := ModelValidate(conn.NewSession(nil), model, nil) So(err, ShouldBeNil) }) }) }) }
func (MockModel) Validators() []FieldValidator { validators := make([]FieldValidator, 1) validators[0] = NewFieldValidator(field.Name("FirstName"), "matches", MockFieldValidatorFunc, "Pete") return validators }
func (*MockModel) PrimaryKey() PrimaryKeyer { return NewSinglePrimaryKey(field.Name("Id")) }