diff --git a/filter_test.go b/filter_test.go index 37f4276..117f037 100644 --- a/filter_test.go +++ b/filter_test.go @@ -7,11 +7,10 @@ import ( "gorm.io/gorm" "gorm.io/gorm/clause" "gorm.io/gorm/schema" - "gorm.io/gorm/utils/tests" ) func TestFilterWhere(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) filter := &Filter{Field: "name", Args: []string{"val1"}} db = filter.Where(db, "name = ?", "val1") expected := map[string]clause.Clause{ @@ -28,7 +27,7 @@ func TestFilterWhere(t *testing.T) { } func TestFilterWhereOr(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) filter := &Filter{Field: "name", Args: []string{"val1"}, Or: true} db = filter.Where(db, "name = ?", "val1") expected := map[string]clause.Clause{ @@ -49,9 +48,10 @@ func TestFilterWhereOr(t *testing.T) { } func TestFilterScope(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) filter := &Filter{Field: "notacolumn", Args: []string{"val1"}, Operator: Operators["$eq"]} schema := &schema.Schema{ + DBNames: []string{"name"}, FieldsByDBName: map[string]*schema.Field{ "name": {Name: "Name", DBName: "name"}, }, @@ -64,7 +64,8 @@ func TestFilterScope(t *testing.T) { filter.Field = "name" - db = db.Scopes(filter.Scope(&Settings{}, schema)).Find(nil) + results := []map[string]interface{}{} + db = db.Scopes(filter.Scope(&Settings{}, schema)).Find(results) expected := map[string]clause.Clause{ "WHERE": { Name: "WHERE", @@ -81,6 +82,7 @@ func TestFilterScope(t *testing.T) { func TestFilterScopeBlacklisted(t *testing.T) { filter := &Filter{Field: "name", Args: []string{"val1"}, Operator: Operators["$eq"]} schema := &schema.Schema{ + DBNames: []string{"name"}, FieldsByDBName: map[string]*schema.Field{ "name": {Name: "Name"}, }, @@ -111,7 +113,7 @@ type FilterTestModel struct { } func TestFilterScopeWithJoin(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) filter := &Filter{Field: "Relation.name", Args: []string{"val1"}, Operator: Operators["$eq"]} results := []*FilterTestModel{} @@ -120,6 +122,7 @@ func TestFilterScopeWithJoin(t *testing.T) { return } + db.DryRun = true db = db.Model(&results).Scopes(filter.Scope(&Settings{}, schema)).Find(&results) expected := map[string]clause.Clause{ "WHERE": { @@ -158,12 +161,22 @@ func TestFilterScopeWithJoin(t *testing.T) { }, }, }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Table: "filter_test_models", Name: "name"}, + {Table: "filter_test_models", Name: "id"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) + assert.Nil(t, db.Error) } func TestFilterScopeWithJoinBlacklistedRelation(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) filter := &Filter{Field: "Relation.name", Args: []string{"val1"}, Operator: Operators["$eq"]} results := []*FilterTestModel{} @@ -184,7 +197,7 @@ func TestFilterScopeWithJoinBlacklistedRelation(t *testing.T) { } func TestFilterScopeWithJoinHasMany(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) filter := &Filter{Field: "Relation.name", Args: []string{"val1"}, Operator: Operators["$eq"]} results := []*FilterTestModel{} @@ -200,7 +213,7 @@ func TestFilterScopeWithJoinHasMany(t *testing.T) { } func TestFilterScopeWithJoinInvalidModel(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) filter := &Filter{Field: "Relation.name", Args: []string{"val1"}, Operator: Operators["$eq"]} results := []*FilterTestModel{} @@ -214,7 +227,7 @@ func TestFilterScopeWithJoinInvalidModel(t *testing.T) { } func TestFilterScopeWithJoinNestedRelation(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) filter := &Filter{Field: "Relation.NestedRelation.field", Args: []string{"val1"}, Operator: Operators["$eq"]} results := []*FilterTestModel{} @@ -236,6 +249,14 @@ func TestFilterScopeWithJoinNestedRelation(t *testing.T) { }, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{}, + }, } assert.Equal(t, expected, conditionTx.Statement.Clauses) @@ -290,12 +311,21 @@ func TestFilterScopeWithJoinNestedRelation(t *testing.T) { }, }, }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Table: "filter_test_models", Name: "name"}, + {Table: "filter_test_models", Name: "id"}, + }, + }, + }, } assert.Equal(t, expected, joinTx.Statement.Clauses) } func TestFilterScopeWithJoinDontDuplicate(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) settings := &Settings{} filter := &Filter{Field: "Relation.name", Args: []string{"val1"}, Operator: Operators["$eq"]} filter2 := &Filter{Field: "Relation.id", Args: []string{"0"}, Operator: Operators["$gt"]} @@ -348,12 +378,21 @@ func TestFilterScopeWithJoinDontDuplicate(t *testing.T) { }, }, }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Table: "filter_test_models", Name: "name"}, + {Table: "filter_test_models", Name: "id"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) } func TestFilterScopeWithAlreadyExistingJoin(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) filter := &Filter{Field: "Relation.name", Args: []string{"val1"}, Operator: Operators["$eq"]} results := []*FilterTestModel{} @@ -409,13 +448,23 @@ func TestFilterScopeWithAlreadyExistingJoin(t *testing.T) { }, }, }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Raw: true, Name: "`Relation`.`name` AS `Relation__name`"}, + {Raw: true, Name: "`Relation`.`id` AS `Relation__id`"}, + {Raw: true, Name: "`Relation`.`parent_id` AS `Relation__parent_id`"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) assert.Empty(t, db.Statement.Joins) } func TestFilterScopeWithAlreadyExistingRawJoin(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) filter := &Filter{Field: "Relation.name", Args: []string{"val1"}, Operator: Operators["$eq"]} results := []*FilterTestModel{} @@ -441,12 +490,28 @@ func TestFilterScopeWithAlreadyExistingRawJoin(t *testing.T) { "FROM": { Name: "FROM", Expression: clause.From{ - Joins: []clause.Join{}, // The join is in Statement.Joins + Joins: []clause.Join{ + { + Expression: clause.NamedExpr{ + SQL: `LEFT JOIN filter_test_relations AS "Relation" ON id > ?`, + Vars: []interface{}{0}, + }, + }, + }, + }, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Table: "filter_test_models", Name: "name"}, + {Table: "filter_test_models", Name: "id"}, + }, }, }, } assert.Equal(t, expected, db.Statement.Clauses) - assert.NotEmpty(t, db.Statement.Joins) + assert.Empty(t, db.Statement.Joins) } type FilterTestModelComputedRelation struct { @@ -464,7 +529,7 @@ type FilterTestModelComputed struct { } func TestFilterScopeComputed(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) filter := &Filter{Field: "computed", Args: []string{"val1"}, Operator: Operators["$eq"]} results := []*FilterTestModelComputed{} @@ -483,12 +548,20 @@ func TestFilterScopeComputed(t *testing.T) { }, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{}, + }, } assert.Equal(t, expected, db.Statement.Clauses) } func TestFilterScopeComputedRelation(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) filter := &Filter{Field: "Relation.computed", Args: []string{"val1"}, Operator: Operators["$eq"]} results := []*FilterTestModelComputed{} @@ -535,6 +608,16 @@ func TestFilterScopeComputedRelation(t *testing.T) { }, }, }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Table: "filter_test_model_computeds", Name: "name"}, + {Table: "filter_test_model_computeds", Name: "computed"}, // Should not be problematic that it is added automatically by Gorm since we force only selectable fields all he time. + {Table: "filter_test_model_computeds", Name: "id"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) } diff --git a/go.mod b/go.mod index d252258..5a857c7 100644 --- a/go.mod +++ b/go.mod @@ -3,18 +3,20 @@ module goyave.dev/filter go 1.17 require ( - github.com/stretchr/testify v1.7.0 - gorm.io/gorm v1.23.10 - goyave.dev/goyave/v4 v4.2.0 + github.com/stretchr/testify v1.8.1 + gorm.io/driver/sqlite v1.4.3 + gorm.io/gorm v1.24.1 + goyave.dev/goyave/v4 v4.4.5 ) require ( github.com/Code-Hex/uniseg v0.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/imdario/mergo v0.3.12 // indirect + github.com/imdario/mergo v0.3.13 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect + github.com/mattn/go-sqlite3 v1.14.16 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index b55c4ce..31268a1 100644 --- a/go.sum +++ b/go.sum @@ -1,216 +1,41 @@ -github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= -github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Code-Hex/uniseg v0.2.0 h1:QB/2UJFvEuRLSZqe+Sb1XQBTWjqGVbZoC6oSWzQRKws= github.com/Code-Hex/uniseg v0.2.0/go.mod h1:/ndS2tP+X1lk2HUOcXWGtVTxVq0lWilwgMa4CbzdRsg= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU= -github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= -github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= -github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= -github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= -github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= -github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= -github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.11.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= -github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= -github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= -github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= -github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= -github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= -github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= -github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.9.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= -github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= -github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= -github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= -github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= -github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.14.1/go.mod h1:RgDuE4Z34o7XE92RpLsvFiOEfrAUT0Xt2KxvX73W06M= -github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw= -github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= -github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/mysql v1.3.2 h1:QJryWiqQ91EvZ0jZL48NOpdlPdMjdip1hQ8bTgo4H7I= -gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U= -gorm.io/driver/postgres v1.3.1/go.mod h1:WwvWOuR9unCLpGWCL6Y3JOeBWvbKi6JLhayiVclSZZU= -gorm.io/driver/sqlite v1.3.1/go.mod h1:wJx0hJspfycZ6myN38x1O/AqLtNS6c5o9TndewFbELg= -gorm.io/driver/sqlserver v1.3.1/go.mod h1:w25Vrx2BG+CJNUu/xKbFhaKlGxT/nzRkhWCCoptX8tQ= -gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= -gorm.io/gorm v1.23.2/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= -gorm.io/gorm v1.23.10 h1:4Ne9ZbzID9GUxRkllxN4WjJKpsHx8YbKvekVdgyWh24= -gorm.io/gorm v1.23.10/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= -goyave.dev/goyave/v4 v4.2.0 h1:M0ZRtP3OXSda0+8dBdYRRCdbG3Wxg630FxS6rqdwLBw= -goyave.dev/goyave/v4 v4.2.0/go.mod h1:AHpLpWiV3Qf+5onrNkkd9GF7LWm+i0lcNXjOcNp25nk= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.4.3 h1:/JhWJhO2v17d8hjApTltKNADm7K7YI2ogkR7avJUL3k= +gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU= +gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI= +gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= +gorm.io/gorm v1.24.1 h1:CgvzRniUdG67hBAzsxDGOAuq4Te1osVMYsa1eQbd4fs= +gorm.io/gorm v1.24.1/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= +goyave.dev/goyave/v4 v4.4.5 h1:pqNQTMMx4XImjpDZ890Yz4nTW0t5o3zpmdPfmXuXSF4= +goyave.dev/goyave/v4 v4.4.5/go.mod h1:lZCUx5ln6k+CWNop2vLU9295LNaOm7FUqpJbk6hqGVU= diff --git a/join.go b/join.go index 3f6fbf6..342656b 100644 --- a/join.go +++ b/join.go @@ -84,7 +84,7 @@ func (j *Join) applyRelation(schema *schema.Schema, blacklist *Blacklist, relati func joinScope(relationName string, rel *schema.Relationship, fields []string, blacklist *Blacklist) func(*gorm.DB) *gorm.DB { var columns []*schema.Field if fields == nil { - columns = getSelectableFields(blacklist, rel.FieldSchema.FieldsByDBName) + columns = getSelectableFields(blacklist, rel.FieldSchema) } else { var b []string if blacklist != nil { diff --git a/join_test.go b/join_test.go index bf0e840..dc48f17 100644 --- a/join_test.go +++ b/join_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/assert" "gorm.io/gorm" - "gorm.io/gorm/utils/tests" ) type JoinTestModel struct { @@ -20,6 +19,7 @@ func (m *JoinTestModel) TableName() string { } type JoinRelationModel struct { + C string `gorm:"->;-:migration" computed:"UPPER(~~~ct~~~.b)"` B string A int `gorm:"primaryKey"` } @@ -29,7 +29,7 @@ func (m *JoinRelationModel) TableName() string { } func TestJoinScope(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) join := &Join{Relation: "notarelation", Fields: []string{"a", "b", "notacolumn"}} join.selectCache = map[string][]string{} @@ -40,8 +40,7 @@ func TestJoinScope(t *testing.T) { assert.Nil(t, join.Scopes(&Settings{}, schema)) join.Relation = "Relation" - results := map[string]interface{}{} - db = db.Scopes(join.Scopes(&Settings{}, schema)...).Table("table").Find(&results) + db = db.Model(&JoinTestModel{}).Scopes(join.Scopes(&Settings{}, schema)...).Find(nil) if assert.Contains(t, db.Statement.Preloads, "Relation") { tx := db.Scopes(db.Statement.Preloads["Relation"][0].(func(*gorm.DB) *gorm.DB)).Find(nil) assert.Equal(t, []string{"`relation`.`a`", "`relation`.`b`"}, tx.Statement.Selects) @@ -50,7 +49,7 @@ func TestJoinScope(t *testing.T) { } func TestJoinScopeAutoSelectFieldsNoBlacklist(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) join := &Join{Relation: "Relation", Fields: nil} join.selectCache = map[string][]string{} schema, err := parseModel(db, &JoinTestModel{}) @@ -58,17 +57,16 @@ func TestJoinScopeAutoSelectFieldsNoBlacklist(t *testing.T) { return } - results := map[string]interface{}{} - db = db.Scopes(join.Scopes(&Settings{}, schema)...).Table("table").Find(&results) + db = db.Model(&JoinTestModel{}).Scopes(join.Scopes(&Settings{}, schema)...).Find(nil) if assert.Contains(t, db.Statement.Preloads, "Relation") { tx := db.Scopes(db.Statement.Preloads["Relation"][0].(func(*gorm.DB) *gorm.DB)).Find(nil) - assert.ElementsMatch(t, []string{"`relation`.`a`", "`relation`.`b`"}, tx.Statement.Selects) + assert.Equal(t, []string{"(UPPER(`relation`.b)) `c`", "`relation`.`b`", "`relation`.`a`"}, tx.Statement.Selects) } assert.Nil(t, join.selectCache["Relation"]) } func TestJoinScopeAutoSelectFieldsWithBlacklist(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) join := &Join{Relation: "Relation", Fields: nil} join.selectCache = map[string][]string{} schema, err := parseModel(db, &JoinTestModel{}) @@ -84,17 +82,16 @@ func TestJoinScopeAutoSelectFieldsWithBlacklist(t *testing.T) { }, } - results := map[string]interface{}{} - db = db.Scopes(join.Scopes(&Settings{Blacklist: blacklist}, schema)...).Table("table").Find(&results) + db = db.Model(&JoinTestModel{}).Scopes(join.Scopes(&Settings{Blacklist: blacklist}, schema)...).Find(nil) if assert.Contains(t, db.Statement.Preloads, "Relation") { tx := db.Scopes(db.Statement.Preloads["Relation"][0].(func(*gorm.DB) *gorm.DB)).Find(nil) - assert.ElementsMatch(t, []string{"`relation`.`a`"}, tx.Statement.Selects) + assert.Equal(t, []string{"(UPPER(`relation`.b)) `c`", "`relation`.`a`"}, tx.Statement.Selects) } assert.Nil(t, join.selectCache["Relation"]) } func TestJoinScopeAnonymousRelation(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) join := &Join{Relation: "notarelation", Fields: []string{"a", "b", "notacolumn"}} join.selectCache = map[string][]string{} @@ -115,8 +112,7 @@ func TestJoinScopeAnonymousRelation(t *testing.T) { assert.Nil(t, join.Scopes(&Settings{}, schema)) join.Relation = "Relation" - results := map[string]interface{}{} - db = db.Scopes(join.Scopes(&Settings{}, schema)...).Table("table").Find(&results) + db = db.Model(&JoinTestModel{}).Scopes(join.Scopes(&Settings{}, schema)...).Find(nil) assert.Empty(t, db.Statement.Preloads) assert.Empty(t, db.Statement.Selects) assert.Equal(t, "Relation \"Relation\" is anonymous, could not get table name", db.Error.Error()) @@ -124,7 +120,7 @@ func TestJoinScopeAnonymousRelation(t *testing.T) { } func TestJoinScopeBlacklisted(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) join := &Join{Relation: "Relation", Fields: []string{"a", "b", "notacolumn"}} schema, err := parseModel(db, &JoinTestModel{}) @@ -170,7 +166,7 @@ func (m *JoinHopManyTestChildModel) TableName() string { } func TestJoinScopeBlacklistedRelationHop(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) join := &Join{Relation: "Relation.Parent.Relation", Fields: []string{"name", "id"}} join.selectCache = map[string][]string{} @@ -193,17 +189,15 @@ func TestJoinScopeBlacklistedRelationHop(t *testing.T) { } func TestJoinScopePrimaryKeyNotSelected(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) join := &Join{Relation: "Relation", Fields: []string{"b"}} join.selectCache = map[string][]string{} schema, err := parseModel(db, &JoinHopTestModel{}) if !assert.Nil(t, err) { return } - schema.Table = "table" - results := map[string]interface{}{} - db = db.Scopes(join.Scopes(&Settings{}, schema)...).Table("table").Find(&results) + db = db.Model(&JoinHopTestModel{}).Scopes(join.Scopes(&Settings{}, schema)...).Find(nil) if assert.Contains(t, db.Statement.Preloads, "Relation") { tx := db.Scopes(db.Statement.Preloads["Relation"][0].(func(*gorm.DB) *gorm.DB)).Find(nil) assert.Equal(t, []string{"`relation`.`b`", "`relation`.`a`", "`relation`.`parent_id`"}, tx.Statement.Selects) @@ -220,7 +214,8 @@ func TestJoinScopePrimaryKeyNotSelected(t *testing.T) { }, }, } - db = db.Scopes(join.Scopes(settings, schema)...).Table("table").Find(&results) + db = openDryRunDB(t) + db = db.Model(&JoinHopTestModel{}).Scopes(join.Scopes(settings, schema)...).Find(nil) if assert.Contains(t, db.Statement.Preloads, "Relation") { tx := db.Scopes(db.Statement.Preloads["Relation"][0].(func(*gorm.DB) *gorm.DB)).Find(nil) assert.Equal(t, []string{"`relation`.`b`", "`relation`.`parent_id`"}, tx.Statement.Selects) @@ -228,17 +223,15 @@ func TestJoinScopePrimaryKeyNotSelected(t *testing.T) { } func TestJoinScopeHasMany(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) join := &Join{Relation: "Relation", Fields: []string{"a", "b"}} join.selectCache = map[string][]string{} schema, err := parseModel(db, &JoinHopManyTestModel{}) if !assert.Nil(t, err) { return } - schema.Table = "table" - results := map[string]interface{}{} - db = db.Scopes(join.Scopes(&Settings{}, schema)...).Table("table").Find(&results) + db = db.Model(&JoinHopManyTestModel{}).Scopes(join.Scopes(&Settings{}, schema)...).Find(nil) if assert.Contains(t, db.Statement.Preloads, "Relation") { tx := db.Scopes(db.Statement.Preloads["Relation"][0].(func(*gorm.DB) *gorm.DB)).Find(nil) assert.Equal(t, []string{"`relation`.`a`", "`relation`.`b`", "`relation`.`parent_id`"}, tx.Statement.Selects) @@ -255,7 +248,8 @@ func TestJoinScopeHasMany(t *testing.T) { }, }, } - db = db.Scopes(join.Scopes(settings, schema)...).Table("table").Find(&results) + db = openDryRunDB(t) + db = db.Model(&JoinHopManyTestModel{}).Scopes(join.Scopes(settings, schema)...).Find(nil) if assert.Contains(t, db.Statement.Preloads, "Relation") { tx := db.Scopes(db.Statement.Preloads["Relation"][0].(func(*gorm.DB) *gorm.DB)).Find(nil) assert.Equal(t, []string{"`relation`.`a`", "`relation`.`b`"}, tx.Statement.Selects) @@ -263,7 +257,7 @@ func TestJoinScopeHasMany(t *testing.T) { } func TestJoinScopeNestedRelations(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) join := &Join{Relation: "Relation.Parent", Fields: []string{"id", "relation_id"}} join.selectCache = map[string][]string{} schema, err := parseModel(db, &JoinHopManyTestModel{}) @@ -288,11 +282,10 @@ func TestJoinScopeNestedRelations(t *testing.T) { }, } - results := map[string]interface{}{} - db = db.Scopes(join.Scopes(settings, schema)...).Table("table").Find(&results) + db = db.Model(&JoinHopManyTestModel{}).Scopes(join.Scopes(settings, schema)...).Find(nil) if assert.Contains(t, db.Statement.Preloads, "Relation.Parent") { tx := db.Session(&gorm.Session{}).Scopes(db.Statement.Preloads["Relation.Parent"][0].(func(*gorm.DB) *gorm.DB)).Find(nil) - assert.Equal(t, []string{"`table`.`id`"}, tx.Statement.Selects) + assert.Equal(t, []string{"`join_hop_many_test_models`.`id`"}, tx.Statement.Selects) } if assert.Contains(t, db.Statement.Preloads, "Relation") { tx := db.Session(&gorm.Session{}).Scopes(db.Statement.Preloads["Relation"][0].(func(*gorm.DB) *gorm.DB)).Find(nil) @@ -303,7 +296,7 @@ func TestJoinScopeNestedRelations(t *testing.T) { } func TestJoinScopeFinal(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) join := &Join{Relation: "Relation", Fields: []string{"a", "b"}} join.selectCache = map[string][]string{} schema, err := parseModel(db, &JoinHopManyTestModel{}) @@ -316,7 +309,7 @@ func TestJoinScopeFinal(t *testing.T) { } func TestJoinNestedRelationsWithSelect(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) join := &Join{Relation: "Relation", Fields: []string{"b"}} join.selectCache = map[string][]string{} join2 := &Join{Relation: "Relation.Parent", Fields: []string{"id", "relation_id"}} @@ -341,11 +334,10 @@ func TestJoinNestedRelationsWithSelect(t *testing.T) { }, } - results := map[string]interface{}{} - db = db.Scopes(join.Scopes(settings, schema)...).Scopes(join2.Scopes(settings, schema)...).Table("table").Find(&results) + db = db.Model(&JoinHopManyTestModel{}).Scopes(join.Scopes(settings, schema)...).Scopes(join2.Scopes(settings, schema)...).Find(nil) if assert.Contains(t, db.Statement.Preloads, "Relation.Parent") { tx := db.Session(&gorm.Session{}).Scopes(db.Statement.Preloads["Relation.Parent"][0].(func(*gorm.DB) *gorm.DB)).Find(nil) - assert.Equal(t, []string{"`table`.`id`"}, tx.Statement.Selects) + assert.Equal(t, []string{"`join_hop_many_test_models`.`id`"}, tx.Statement.Selects) } if assert.Contains(t, db.Statement.Preloads, "Relation") { tx := db.Session(&gorm.Session{}).Scopes(db.Statement.Preloads["Relation"][0].(func(*gorm.DB) *gorm.DB)).Find(nil) @@ -356,7 +348,7 @@ func TestJoinNestedRelationsWithSelect(t *testing.T) { } func TestJoinScopeInvalidSyntax(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) join := &Join{Relation: "Relation.", Fields: []string{"a", "b"}} // A dot at the end of the relation name is invalid join.selectCache = map[string][]string{} schema, err := parseModel(db, &JoinHopManyTestModel{}) @@ -367,7 +359,7 @@ func TestJoinScopeInvalidSyntax(t *testing.T) { } func TestJoinScopeNonExistingRelation(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) join := &Join{Relation: "Relation.NotARelation.Parent", Fields: []string{"a", "b"}} join.selectCache = map[string][]string{} schema, err := parseModel(db, &JoinHopManyTestModel{}) @@ -376,3 +368,34 @@ func TestJoinScopeNonExistingRelation(t *testing.T) { } assert.Nil(t, join.Scopes(&Settings{}, schema)) } + +type JoinTestModelComputed struct { + Relation *JoinRelationModelComputed + Name string + ID int `gorm:"primaryKey"` + RelID int `gorm:"column:relation_id"` +} + +type JoinRelationModelComputed struct { + C string `gorm:"->;-:migration" computed:"UPPER(~~~ct~~~.b)"` + B string + A int `gorm:"primaryKey"` +} + +func TestJoinScopeComputedField(t *testing.T) { + db := openDryRunDB(t) + join := &Join{Relation: "Relation", Fields: []string{"a", "b", "c"}} + join.selectCache = map[string][]string{} + + schema, err := parseModel(db, &JoinTestModelComputed{}) + if !assert.Nil(t, err) { + return + } + + db = db.Model(&JoinTestModelComputed{}).Scopes(join.Scopes(&Settings{}, schema)...).Find(nil) + if assert.Contains(t, db.Statement.Preloads, "Relation") { + tx := db.Scopes(db.Statement.Preloads["Relation"][0].(func(*gorm.DB) *gorm.DB)).Find(nil) + assert.Equal(t, []string{"`join_relation_model_computeds`.`a`", "`join_relation_model_computeds`.`b`", "(UPPER(`join_relation_model_computeds`.b)) `c`"}, tx.Statement.Selects) + } + assert.Equal(t, []string{"a", "b", "c"}, join.selectCache["Relation"]) +} diff --git a/operator_test.go b/operator_test.go index 1a69256..5a5bca6 100644 --- a/operator_test.go +++ b/operator_test.go @@ -4,14 +4,12 @@ import ( "testing" "github.com/stretchr/testify/assert" - "gorm.io/gorm" "gorm.io/gorm/clause" "gorm.io/gorm/schema" - "gorm.io/gorm/utils/tests" ) func TestEquals(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) db = Operators["$eq"].Function(db, &Filter{Field: "name", Args: []string{"test"}}, "`test_models`.`name`", schema.String) expected := map[string]clause.Clause{ @@ -28,7 +26,7 @@ func TestEquals(t *testing.T) { } func TestNotEquals(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) db = Operators["$ne"].Function(db, &Filter{Field: "name", Args: []string{"test"}}, "`test_models`.`name`", schema.String) expected := map[string]clause.Clause{ @@ -45,7 +43,7 @@ func TestNotEquals(t *testing.T) { } func TestGreaterThan(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) db = Operators["$gt"].Function(db, &Filter{Field: "age", Args: []string{"18"}}, "`test_models`.`age`", schema.String) expected := map[string]clause.Clause{ @@ -62,7 +60,7 @@ func TestGreaterThan(t *testing.T) { } func TestLowerThan(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) db = Operators["$lt"].Function(db, &Filter{Field: "age", Args: []string{"18"}}, "`test_models`.`age`", schema.String) expected := map[string]clause.Clause{ @@ -79,7 +77,7 @@ func TestLowerThan(t *testing.T) { } func TestGreaterThanEqual(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) db = Operators["$gte"].Function(db, &Filter{Field: "age", Args: []string{"18"}}, "`test_models`.`age`", schema.String) expected := map[string]clause.Clause{ @@ -96,7 +94,7 @@ func TestGreaterThanEqual(t *testing.T) { } func TestLowerThanEqual(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) db = Operators["$lte"].Function(db, &Filter{Field: "age", Args: []string{"18"}}, "`test_models`.`age`", schema.String) expected := map[string]clause.Clause{ @@ -113,7 +111,7 @@ func TestLowerThanEqual(t *testing.T) { } func TestStarts(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) db = Operators["$starts"].Function(db, &Filter{Field: "name", Args: []string{"test"}}, "`test_models`.`name`", schema.String) expected := map[string]clause.Clause{ @@ -130,7 +128,7 @@ func TestStarts(t *testing.T) { } func TestEnds(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) db = Operators["$ends"].Function(db, &Filter{Field: "name", Args: []string{"test"}}, "`test_models`.`name`", schema.String) expected := map[string]clause.Clause{ @@ -147,7 +145,7 @@ func TestEnds(t *testing.T) { } func TestContains(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) db = Operators["$cont"].Function(db, &Filter{Field: "name", Args: []string{"test"}}, "`test_models`.`name`", schema.String) expected := map[string]clause.Clause{ @@ -164,7 +162,7 @@ func TestContains(t *testing.T) { } func TestNotContains(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) db = Operators["$excl"].Function(db, &Filter{Field: "name", Args: []string{"test"}}, "`test_models`.`name`", schema.String) expected := map[string]clause.Clause{ @@ -181,7 +179,7 @@ func TestNotContains(t *testing.T) { } func TestIn(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) db = Operators["$in"].Function(db, &Filter{Field: "name", Args: []string{"val1", "val2"}}, "`test_models`.`name`", schema.String) expected := map[string]clause.Clause{ @@ -198,7 +196,7 @@ func TestIn(t *testing.T) { } func TestNotIn(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) db = Operators["$notin"].Function(db, &Filter{Field: "name", Args: []string{"val1", "val2"}}, "`test_models`.`name`", schema.String) expected := map[string]clause.Clause{ @@ -215,7 +213,7 @@ func TestNotIn(t *testing.T) { } func TestIsNull(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) db = Operators["$isnull"].Function(db, &Filter{Field: "name"}, "`test_models`.`name`", schema.String) expected := map[string]clause.Clause{ @@ -232,7 +230,7 @@ func TestIsNull(t *testing.T) { } func TestNotNull(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) db = Operators["$notnull"].Function(db, &Filter{Field: "name"}, "`test_models`.`name`", schema.String) expected := map[string]clause.Clause{ @@ -249,7 +247,7 @@ func TestNotNull(t *testing.T) { } func TestBetween(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) db = Operators["$between"].Function(db, &Filter{Field: "age", Args: []string{"18", "25"}}, "`test_models`.`age`", schema.Uint) expected := map[string]clause.Clause{ @@ -266,7 +264,7 @@ func TestBetween(t *testing.T) { } func TestIsTrue(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) db = Operators["$istrue"].Function(db, &Filter{Field: "isActive"}, "`test_models`.`is_active`", schema.Bool) expected := map[string]clause.Clause{ @@ -281,13 +279,13 @@ func TestIsTrue(t *testing.T) { } assert.Equal(t, expected, db.Statement.Clauses) - db, _ = gorm.Open(&tests.DummyDialector{}, nil) + db = openDryRunDB(t) db = Operators["$istrue"].Function(db, &Filter{Field: "isActive"}, "`test_models`.`is_active`", schema.String) // Unsupported type assert.Empty(t, db.Statement.Clauses) } func TestIsFalse(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) db = Operators["$isfalse"].Function(db, &Filter{Field: "isActive"}, "`test_models`.`is_active`", schema.Bool) expected := map[string]clause.Clause{ @@ -302,7 +300,7 @@ func TestIsFalse(t *testing.T) { } assert.Equal(t, expected, db.Statement.Clauses) - db, _ = gorm.Open(&tests.DummyDialector{}, nil) + db = openDryRunDB(t) db = Operators["$isfalse"].Function(db, &Filter{Field: "isActive"}, "`test_models`.`is_active`", schema.String) // Unsupported type assert.Empty(t, db.Statement.Clauses) } diff --git a/search_test.go b/search_test.go index fed5dc3..1ad0f4a 100644 --- a/search_test.go +++ b/search_test.go @@ -8,11 +8,10 @@ import ( "gorm.io/gorm" "gorm.io/gorm/clause" "gorm.io/gorm/schema" - "gorm.io/gorm/utils/tests" ) func TestSearchScope(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) search := &Search{ Fields: []string{"name", "email"}, Query: "My Query", @@ -64,6 +63,14 @@ func TestSearchScope(t *testing.T) { }, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{}, + }, } assert.Equal(t, expected, db.Statement.Clauses) } @@ -111,7 +118,7 @@ type SearchTestModel struct { } func TestSeachScopeWithJoin(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) search := &Search{ Fields: []string{"name", "Relation.name"}, Query: "My Query", @@ -188,12 +195,21 @@ func TestSeachScopeWithJoin(t *testing.T) { }, }, }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Table: "search_test_models", Name: "name"}, + {Table: "search_test_models", Name: "id"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) } func TestSeachScopeWithJoinInvalidModel(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) search := &Search{ Fields: []string{"name", "Relation.name"}, Query: "My Query", @@ -211,7 +227,7 @@ func TestSeachScopeWithJoinInvalidModel(t *testing.T) { } func TestSeachScopeWithJoinNestedRelation(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) search := &Search{ Fields: []string{"name", "Relation.NestedRelation.field"}, Query: "My Query", @@ -309,6 +325,15 @@ func TestSeachScopeWithJoinNestedRelation(t *testing.T) { }, }, }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Table: "search_test_models", Name: "name"}, + {Table: "search_test_models", Name: "id"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) } @@ -328,7 +353,7 @@ type SearchTestModelComputed struct { } func TestSearchScopeComputed(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) search := &Search{ Fields: []string{"computed", "Relation.computed"}, Query: "My Query", @@ -400,6 +425,16 @@ func TestSearchScopeComputed(t *testing.T) { }, }, }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Table: "search_test_model_computeds", Name: "name"}, + {Table: "search_test_model_computeds", Name: "computed"}, // Should not be problematic that it is added automatically by Gorm since we force only selectable fields all he time. + {Table: "search_test_model_computeds", Name: "id"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) } diff --git a/settings.go b/settings.go index dad915a..2bdd56f 100644 --- a/settings.go +++ b/settings.go @@ -166,7 +166,7 @@ func (s *Settings) scopeFields(db *gorm.DB, request *goyave.Request, schema *sch } return db.Scopes(selectScope(schema.Table, cleanColumns(schema, fields, s.FieldsBlacklist), false)) } - return db.Scopes(selectScope(schema.Table, getSelectableFields(&s.Blacklist, schema.FieldsByDBName), false)) + return db.Scopes(selectScope(schema.Table, getSelectableFields(&s.Blacklist, schema), false)) } func (s *Settings) scopeSort(db *gorm.DB, request *goyave.Request, schema *schema.Schema) *gorm.DB { @@ -258,7 +258,7 @@ func (s *Settings) applySearch(request *goyave.Request, schema *schema.Schema) * if ok { fields := s.FieldsSearch if fields == nil { - for _, f := range getSelectableFields(&s.Blacklist, schema.FieldsByDBName) { + for _, f := range getSelectableFields(&s.Blacklist, schema) { fields = append(fields, f.DBName) } } @@ -280,15 +280,15 @@ func (s *Settings) applySearch(request *goyave.Request, schema *schema.Schema) * return nil } -func getSelectableFields(blacklist *Blacklist, fields map[string]*schema.Field) []*schema.Field { +func getSelectableFields(blacklist *Blacklist, sch *schema.Schema) []*schema.Field { b := []string{} if blacklist != nil && blacklist.FieldsBlacklist != nil { b = blacklist.FieldsBlacklist } - columns := make([]*schema.Field, 0, len(fields)) - for k, f := range fields { - if !sliceutil.ContainsStr(b, k) { - columns = append(columns, f) + columns := make([]*schema.Field, 0, len(sch.DBNames)) + for _, f := range sch.DBNames { + if !sliceutil.ContainsStr(b, f) { + columns = append(columns, sch.FieldsByDBName[f]) } } diff --git a/settings_test.go b/settings_test.go index 2fc0997..2805033 100644 --- a/settings_test.go +++ b/settings_test.go @@ -6,14 +6,16 @@ import ( "testing" "github.com/stretchr/testify/assert" + "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/clause" "gorm.io/gorm/schema" - "gorm.io/gorm/utils/tests" "goyave.dev/goyave/v4" "goyave.dev/goyave/v4/database" ) +var fifteen = 15 + type TestScopeRelation struct { A string B string @@ -23,6 +25,7 @@ type TestScopeModel struct { Relation *TestScopeRelation Name string Email string + Computed string `gorm:"->;-:migration" computed:"UPPER(~~~ct~~~.name)"` ID uint RelationID uint } @@ -32,7 +35,16 @@ type TestScopeModelNoPrimaryKey struct { RelationID uint } -func prepareTestScope(settings *Settings) (*database.Paginator, *gorm.DB) { +func openDryRunDB(t *testing.T) *gorm.DB { + db, err := gorm.Open(sqlite.Open(":memory:?mode=memory"), nil) + if err != nil { + assert.FailNow(t, "Could not open dry run DB", err) + } + db.DryRun = true + return db +} + +func prepareTestScope(t *testing.T, settings *Settings) (*database.Paginator, *gorm.DB) { request := &goyave.Request{ Data: map[string]interface{}{ "filter": []*Filter{ @@ -46,12 +58,12 @@ func prepareTestScope(settings *Settings) (*database.Paginator, *gorm.DB) { "join": []*Join{{Relation: "Relation", Fields: []string{"a", "b"}}}, "page": 2, "per_page": 15, - "fields": "id,name,email", + "fields": "id,name,email,computed", "search": "val", }, Lang: "en-US", } - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) results := []*TestScopeModel{} if settings == nil { @@ -61,7 +73,7 @@ func prepareTestScope(settings *Settings) (*database.Paginator, *gorm.DB) { return settings.Scope(db, request, results) } -func prepareTestScopeUnpaginated(settings *Settings) ([]*TestScopeModel, *gorm.DB) { +func prepareTestScopeUnpaginated(t *testing.T, settings *Settings) ([]*TestScopeModel, *gorm.DB) { request := &goyave.Request{ Data: map[string]interface{}{ "filter": []*Filter{ @@ -75,12 +87,12 @@ func prepareTestScopeUnpaginated(settings *Settings) ([]*TestScopeModel, *gorm.D "join": []*Join{{Relation: "Relation", Fields: []string{"a", "b"}}}, "page": 2, // Those two should be ignored since we are not paginating "per_page": 15, - "fields": "id,name,email", + "fields": "id,name,email,computed", "search": "val", }, Lang: "en-US", } - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) results := []*TestScopeModel{} if settings == nil { @@ -91,7 +103,7 @@ func prepareTestScopeUnpaginated(settings *Settings) ([]*TestScopeModel, *gorm.D } func TestScope(t *testing.T) { - paginator, db := prepareTestScope(&Settings{ + paginator, db := prepareTestScope(t, &Settings{ FieldsSearch: []string{"email"}, SearchOperator: &Operator{ Function: func(tx *gorm.DB, filter *Filter, column string, dataType schema.DataType) *gorm.DB { @@ -157,18 +169,34 @@ func TestScope(t *testing.T) { }, "LIMIT": { Expression: clause.Limit{ - Limit: 15, + Limit: &fifteen, Offset: 15, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Raw: true, Name: "`test_scope_models`.`id`"}, + {Raw: true, Name: "`test_scope_models`.`name`"}, + {Raw: true, Name: "`test_scope_models`.`email`"}, + {Raw: true, Name: "(UPPER(`test_scope_models`.name)) `computed`"}, + {Raw: true, Name: "`test_scope_models`.`relation_id`"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) assert.Contains(t, db.Statement.Preloads, "Relation") - assert.Equal(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "`test_scope_models`.`relation_id`"}, db.Statement.Selects) + assert.Equal(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "(UPPER(`test_scope_models`.name)) `computed`", "`test_scope_models`.`relation_id`"}, db.Statement.Selects) } func TestScopeUnpaginated(t *testing.T) { - results, db := prepareTestScopeUnpaginated(&Settings{ + results, db := prepareTestScopeUnpaginated(t, &Settings{ FieldsSearch: []string{"email"}, SearchOperator: &Operator{ Function: func(tx *gorm.DB, filter *Filter, column string, dataType schema.DataType) *gorm.DB { @@ -232,14 +260,30 @@ func TestScopeUnpaginated(t *testing.T) { }, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Raw: true, Name: "`test_scope_models`.`id`"}, + {Raw: true, Name: "`test_scope_models`.`name`"}, + {Raw: true, Name: "`test_scope_models`.`email`"}, + {Raw: true, Name: "(UPPER(`test_scope_models`.name)) `computed`"}, + {Raw: true, Name: "`test_scope_models`.`relation_id`"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) assert.Contains(t, db.Statement.Preloads, "Relation") - assert.Equal(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "`test_scope_models`.`relation_id`"}, db.Statement.Selects) + assert.ElementsMatch(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "`test_scope_models`.`relation_id`", "(UPPER(`test_scope_models`.name)) `computed`"}, db.Statement.Selects) } func TestScopeDisableFields(t *testing.T) { - paginator, db := prepareTestScope(&Settings{DisableFields: true, FieldsSearch: []string{"email"}}) + paginator, db := prepareTestScope(t, &Settings{DisableFields: true, FieldsSearch: []string{"email"}}) assert.NotNil(t, paginator) expected := map[string]clause.Clause{ @@ -297,17 +341,33 @@ func TestScopeDisableFields(t *testing.T) { }, "LIMIT": { Expression: clause.Limit{ - Limit: 15, + Limit: &fifteen, Offset: 15, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Raw: true, Name: "`test_scope_models`.`name`"}, + {Raw: true, Name: "`test_scope_models`.`email`"}, + {Raw: true, Name: "(UPPER(`test_scope_models`.name)) `computed`"}, + {Raw: true, Name: "`test_scope_models`.`id`"}, + {Raw: true, Name: "`test_scope_models`.`relation_id`"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) - assert.ElementsMatch(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`relation_id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`"}, db.Statement.Selects) + assert.ElementsMatch(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`relation_id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "(UPPER(`test_scope_models`.name)) `computed`"}, db.Statement.Selects) } func TestScopeUnpaginatedDisableFields(t *testing.T) { - results, db := prepareTestScopeUnpaginated(&Settings{DisableFields: true, FieldsSearch: []string{"email"}}) + results, db := prepareTestScopeUnpaginated(t, &Settings{DisableFields: true, FieldsSearch: []string{"email"}}) assert.NotNil(t, results) expected := map[string]clause.Clause{ @@ -363,13 +423,29 @@ func TestScopeUnpaginatedDisableFields(t *testing.T) { }, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Raw: true, Name: "`test_scope_models`.`name`"}, + {Raw: true, Name: "`test_scope_models`.`email`"}, + {Raw: true, Name: "(UPPER(`test_scope_models`.name)) `computed`"}, + {Raw: true, Name: "`test_scope_models`.`id`"}, + {Raw: true, Name: "`test_scope_models`.`relation_id`"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) - assert.ElementsMatch(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`relation_id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`"}, db.Statement.Selects) + assert.ElementsMatch(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`relation_id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "(UPPER(`test_scope_models`.name)) `computed`"}, db.Statement.Selects) } func TestScopeDisableFilter(t *testing.T) { - paginator, db := prepareTestScope(&Settings{DisableFilter: true, FieldsSearch: []string{"email"}}) + paginator, db := prepareTestScope(t, &Settings{DisableFilter: true, FieldsSearch: []string{"email"}}) assert.NotNil(t, paginator) expected := map[string]clause.Clause{ @@ -404,17 +480,33 @@ func TestScopeDisableFilter(t *testing.T) { }, "LIMIT": { Expression: clause.Limit{ - Limit: 15, + Limit: &fifteen, Offset: 15, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Raw: true, Name: "`test_scope_models`.`id`"}, + {Raw: true, Name: "`test_scope_models`.`name`"}, + {Raw: true, Name: "`test_scope_models`.`email`"}, + {Raw: true, Name: "(UPPER(`test_scope_models`.name)) `computed`"}, + {Raw: true, Name: "`test_scope_models`.`relation_id`"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) - assert.Equal(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "`test_scope_models`.`relation_id`"}, db.Statement.Selects) + assert.ElementsMatch(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "`test_scope_models`.`relation_id`", "(UPPER(`test_scope_models`.name)) `computed`"}, db.Statement.Selects) } func TestScopeUnpaginatedDisableFilter(t *testing.T) { - results, db := prepareTestScopeUnpaginated(&Settings{DisableFilter: true, FieldsSearch: []string{"email"}}) + results, db := prepareTestScopeUnpaginated(t, &Settings{DisableFilter: true, FieldsSearch: []string{"email"}}) assert.NotNil(t, results) expected := map[string]clause.Clause{ @@ -447,13 +539,29 @@ func TestScopeUnpaginatedDisableFilter(t *testing.T) { }, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Raw: true, Name: "`test_scope_models`.`id`"}, + {Raw: true, Name: "`test_scope_models`.`name`"}, + {Raw: true, Name: "`test_scope_models`.`email`"}, + {Raw: true, Name: "(UPPER(`test_scope_models`.name)) `computed`"}, + {Raw: true, Name: "`test_scope_models`.`relation_id`"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) - assert.Equal(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "`test_scope_models`.`relation_id`"}, db.Statement.Selects) + assert.ElementsMatch(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "`test_scope_models`.`relation_id`", "(UPPER(`test_scope_models`.name)) `computed`"}, db.Statement.Selects) } func TestScopeDisableSort(t *testing.T) { - paginator, db := prepareTestScope(&Settings{DisableSort: true, FieldsSearch: []string{"email"}}) + paginator, db := prepareTestScope(t, &Settings{DisableSort: true, FieldsSearch: []string{"email"}}) assert.NotNil(t, paginator) expected := map[string]clause.Clause{ @@ -498,17 +606,33 @@ func TestScopeDisableSort(t *testing.T) { }, "LIMIT": { Expression: clause.Limit{ - Limit: 15, + Limit: &fifteen, Offset: 15, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Raw: true, Name: "`test_scope_models`.`id`"}, + {Raw: true, Name: "`test_scope_models`.`name`"}, + {Raw: true, Name: "`test_scope_models`.`email`"}, + {Raw: true, Name: "(UPPER(`test_scope_models`.name)) `computed`"}, + {Raw: true, Name: "`test_scope_models`.`relation_id`"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) - assert.Equal(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "`test_scope_models`.`relation_id`"}, db.Statement.Selects) + assert.ElementsMatch(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "`test_scope_models`.`relation_id`", "(UPPER(`test_scope_models`.name)) `computed`"}, db.Statement.Selects) } func TestScopeUnpaginatedDisableSort(t *testing.T) { - results, db := prepareTestScopeUnpaginated(&Settings{DisableSort: true, FieldsSearch: []string{"email"}}) + results, db := prepareTestScopeUnpaginated(t, &Settings{DisableSort: true, FieldsSearch: []string{"email"}}) assert.NotNil(t, results) expected := map[string]clause.Clause{ @@ -551,13 +675,29 @@ func TestScopeUnpaginatedDisableSort(t *testing.T) { }, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Raw: true, Name: "`test_scope_models`.`id`"}, + {Raw: true, Name: "`test_scope_models`.`name`"}, + {Raw: true, Name: "`test_scope_models`.`email`"}, + {Raw: true, Name: "(UPPER(`test_scope_models`.name)) `computed`"}, + {Raw: true, Name: "`test_scope_models`.`relation_id`"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) - assert.Equal(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "`test_scope_models`.`relation_id`"}, db.Statement.Selects) + assert.ElementsMatch(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "`test_scope_models`.`relation_id`", "(UPPER(`test_scope_models`.name)) `computed`"}, db.Statement.Selects) } func TestScopeDisableJoin(t *testing.T) { - paginator, db := prepareTestScope(&Settings{DisableJoin: true, FieldsSearch: []string{"email"}}) + paginator, db := prepareTestScope(t, &Settings{DisableJoin: true, FieldsSearch: []string{"email"}}) assert.NotNil(t, paginator) expected := map[string]clause.Clause{ @@ -615,18 +755,33 @@ func TestScopeDisableJoin(t *testing.T) { }, "LIMIT": { Expression: clause.Limit{ - Limit: 15, + Limit: &fifteen, Offset: 15, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Raw: true, Name: "`test_scope_models`.`id`"}, + {Raw: true, Name: "`test_scope_models`.`name`"}, + {Raw: true, Name: "`test_scope_models`.`email`"}, + {Raw: true, Name: "(UPPER(`test_scope_models`.name)) `computed`"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) assert.Empty(t, db.Statement.Preloads) - assert.Equal(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`"}, db.Statement.Selects) + assert.ElementsMatch(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "(UPPER(`test_scope_models`.name)) `computed`"}, db.Statement.Selects) } func TestScopeUnpaginatedDisableJoin(t *testing.T) { - results, db := prepareTestScopeUnpaginated(&Settings{DisableJoin: true, FieldsSearch: []string{"email"}}) + results, db := prepareTestScopeUnpaginated(t, &Settings{DisableJoin: true, FieldsSearch: []string{"email"}}) assert.NotNil(t, results) expected := map[string]clause.Clause{ @@ -682,14 +837,29 @@ func TestScopeUnpaginatedDisableJoin(t *testing.T) { }, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Raw: true, Name: "`test_scope_models`.`id`"}, + {Raw: true, Name: "`test_scope_models`.`name`"}, + {Raw: true, Name: "`test_scope_models`.`email`"}, + {Raw: true, Name: "(UPPER(`test_scope_models`.name)) `computed`"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) assert.Empty(t, db.Statement.Preloads) - assert.Equal(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`"}, db.Statement.Selects) + assert.ElementsMatch(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "(UPPER(`test_scope_models`.name)) `computed`"}, db.Statement.Selects) } func TestScopeDisableSearch(t *testing.T) { - paginator, db := prepareTestScope(&Settings{DisableSearch: true, FieldsSearch: []string{"name"}}) + paginator, db := prepareTestScope(t, &Settings{DisableSearch: true, FieldsSearch: []string{"name"}}) assert.NotNil(t, paginator) expected := map[string]clause.Clause{ @@ -738,18 +908,34 @@ func TestScopeDisableSearch(t *testing.T) { }, "LIMIT": { Expression: clause.Limit{ - Limit: 15, + Limit: &fifteen, Offset: 15, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Raw: true, Name: "`test_scope_models`.`id`"}, + {Raw: true, Name: "`test_scope_models`.`name`"}, + {Raw: true, Name: "`test_scope_models`.`email`"}, + {Raw: true, Name: "(UPPER(`test_scope_models`.name)) `computed`"}, + {Raw: true, Name: "`test_scope_models`.`relation_id`"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) - assert.Equal(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "`test_scope_models`.`relation_id`"}, db.Statement.Selects) + assert.ElementsMatch(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "`test_scope_models`.`relation_id`", "(UPPER(`test_scope_models`.name)) `computed`"}, db.Statement.Selects) } func TestScopeUnpaginatedDisableSearch(t *testing.T) { - results, db := prepareTestScopeUnpaginated(&Settings{DisableSearch: true, FieldsSearch: []string{"name"}}) + results, db := prepareTestScopeUnpaginated(t, &Settings{DisableSearch: true, FieldsSearch: []string{"name"}}) assert.NotNil(t, results) expected := map[string]clause.Clause{ @@ -796,10 +982,26 @@ func TestScopeUnpaginatedDisableSearch(t *testing.T) { }, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Raw: true, Name: "`test_scope_models`.`id`"}, + {Raw: true, Name: "`test_scope_models`.`name`"}, + {Raw: true, Name: "`test_scope_models`.`email`"}, + {Raw: true, Name: "(UPPER(`test_scope_models`.name)) `computed`"}, + {Raw: true, Name: "`test_scope_models`.`relation_id`"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) - assert.Equal(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "`test_scope_models`.`relation_id`"}, db.Statement.Selects) + assert.ElementsMatch(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`name`", "`test_scope_models`.`email`", "`test_scope_models`.`relation_id`", "(UPPER(`test_scope_models`.name)) `computed`"}, db.Statement.Selects) } func TestScopeNoPrimaryKey(t *testing.T) { @@ -810,7 +1012,7 @@ func TestScopeNoPrimaryKey(t *testing.T) { }, Lang: "en-US", } - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) results := []*TestScopeModelNoPrimaryKey{} paginator, db := Scope(db, request, results) @@ -826,7 +1028,7 @@ func TestScopeUnpaginatedNoPrimaryKey(t *testing.T) { }, Lang: "en-US", } - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) var results []*TestScopeModelNoPrimaryKey db = ScopeUnpaginated(db, request, &results) @@ -839,7 +1041,7 @@ func TestScopeWithFieldsBlacklist(t *testing.T) { Data: map[string]interface{}{}, Lang: "en-US", } - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) settings := &Settings{ Blacklist: Blacklist{ FieldsBlacklist: []string{"name"}, @@ -848,7 +1050,7 @@ func TestScopeWithFieldsBlacklist(t *testing.T) { results := []*TestScopeModel{} paginator, db := settings.Scope(db, request, results) assert.NotNil(t, paginator) - assert.ElementsMatch(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`relation_id`", "`test_scope_models`.`email`"}, db.Statement.Selects) + assert.ElementsMatch(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`relation_id`", "`test_scope_models`.`email`", "(UPPER(`test_scope_models`.name)) `computed`"}, db.Statement.Selects) } func TestScopeUnpaginatedWithFieldsBlacklist(t *testing.T) { @@ -856,7 +1058,7 @@ func TestScopeUnpaginatedWithFieldsBlacklist(t *testing.T) { Data: map[string]interface{}{}, Lang: "en-US", } - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) settings := &Settings{ Blacklist: Blacklist{ FieldsBlacklist: []string{"name"}, @@ -865,7 +1067,7 @@ func TestScopeUnpaginatedWithFieldsBlacklist(t *testing.T) { var results []*TestScopeModel db = settings.ScopeUnpaginated(db, request, &results) assert.Nil(t, db.Error) - assert.ElementsMatch(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`relation_id`", "`test_scope_models`.`email`"}, db.Statement.Selects) + assert.ElementsMatch(t, []string{"`test_scope_models`.`id`", "`test_scope_models`.`relation_id`", "`test_scope_models`.`email`", "(UPPER(`test_scope_models`.name)) `computed`"}, db.Statement.Selects) } func TestScopeInvalidModel(t *testing.T) { @@ -873,7 +1075,7 @@ func TestScopeInvalidModel(t *testing.T) { Data: map[string]interface{}{}, Lang: "en-US", } - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) model := []string{} assert.Panics(t, func() { Scope(db, request, model) @@ -885,7 +1087,7 @@ func TestScopeUnpaginatedInvalidModel(t *testing.T) { Data: map[string]interface{}{}, Lang: "en-US", } - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) model := []string{} assert.Panics(t, func() { ScopeUnpaginated(db, request, model) @@ -902,8 +1104,13 @@ func TestBlacklistGetSelectableFields(t *testing.T) { "email": {}, } - assert.ElementsMatch(t, []*schema.Field{fields["id"], fields["email"]}, getSelectableFields(blacklist, fields)) - assert.ElementsMatch(t, []*schema.Field{fields["id"], fields["email"], fields["name"]}, getSelectableFields(nil, fields)) + sch := &schema.Schema{ + DBNames: []string{"id", "name", "email"}, + FieldsByDBName: fields, + } + + assert.ElementsMatch(t, []*schema.Field{fields["id"], fields["email"]}, getSelectableFields(blacklist, sch)) + assert.ElementsMatch(t, []*schema.Field{fields["id"], fields["email"], fields["name"]}, getSelectableFields(nil, sch)) } type TestFilterScopeModel struct { @@ -925,7 +1132,7 @@ func TestApplyFiltersAnd(t *testing.T) { }, Lang: "en-US", } - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) schema, err := parseModel(db, &TestFilterScopeModel{}) if !assert.Nil(t, err) { return @@ -950,6 +1157,14 @@ func TestApplyFiltersAnd(t *testing.T) { }, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{}, + }, } assert.Equal(t, expected, db.Statement.Clauses) @@ -965,7 +1180,7 @@ func TestApplyFiltersOr(t *testing.T) { }, Lang: "en-US", } - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) schema, err := parseModel(db, &TestFilterScopeModel{}) if !assert.Nil(t, err) { return @@ -998,6 +1213,14 @@ func TestApplyFiltersOr(t *testing.T) { }, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{}, + }, } assert.Equal(t, expected, db.Statement.Clauses) @@ -1017,7 +1240,7 @@ func TestApplyFiltersMixed(t *testing.T) { }, Lang: "en-US", } - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) schema, err := parseModel(db, &TestFilterScopeModel{}) if !assert.Nil(t, err) { return @@ -1056,6 +1279,14 @@ func TestApplyFiltersMixed(t *testing.T) { }, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{}, + }, } assert.Equal(t, expected, db.Statement.Clauses) @@ -1070,7 +1301,7 @@ func TestApplyFiltersWithJoin(t *testing.T) { }, Lang: "en-US", } - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) schema, err := parseModel(db, &FilterTestModel{}) if !assert.Nil(t, err) { return @@ -1121,6 +1352,15 @@ func TestApplyFiltersWithJoin(t *testing.T) { }, }, }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Table: "filter_test_models", Name: "name"}, + {Table: "filter_test_models", Name: "id"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) @@ -1133,7 +1373,7 @@ func TestApplySearch(t *testing.T) { }, Lang: "en-US", } - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) schema, err := parseModel(db, &TestFilterScopeModel{}) if !assert.Nil(t, err) { return @@ -1155,39 +1395,53 @@ func TestApplySearchNoQuery(t *testing.T) { } func TestSelectScope(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) db = db.Scopes(selectScope("", nil, false)).Find(nil) assert.Empty(t, db.Statement.Selects) + assert.Empty(t, db.Statement.Clauses["SELECT"].Expression.(clause.Select).Columns) - db, _ = gorm.Open(&tests.DummyDialector{}, nil) + db = openDryRunDB(t) db = db.Scopes(selectScope("", nil, true)).Find(nil) assert.Empty(t, db.Statement.Selects) + assert.Empty(t, db.Statement.Clauses["SELECT"].Expression.(clause.Select).Columns) - sch := &schema.Schema{Table: "test_models"} + sch := &schema.Schema{ + Table: "test_models", + } - db, _ = gorm.Open(&tests.DummyDialector{}, nil) + db = openDryRunDB(t) db = db.Scopes(selectScope(sch.Table, []*schema.Field{{DBName: "a"}, {DBName: "b"}}, false)).Find(nil) assert.Equal(t, []string{"`test_models`.`a`", "`test_models`.`b`"}, db.Statement.Selects) + assert.Equal(t, []clause.Column{{Raw: true, Name: "`test_models`.`a`"}, {Raw: true, Name: "`test_models`.`b`"}}, db.Statement.Clauses["SELECT"].Expression.(clause.Select).Columns) - db, _ = gorm.Open(&tests.DummyDialector{}, nil) + db = openDryRunDB(t) db = db.Scopes(selectScope(sch.Table, []*schema.Field{{DBName: "a"}, {DBName: "b"}}, true)).Find(nil) assert.Equal(t, []string{"`test_models`.`a`", "`test_models`.`b`"}, db.Statement.Selects) + assert.Equal(t, []clause.Column{{Raw: true, Name: "`test_models`.`a`"}, {Raw: true, Name: "`test_models`.`b`"}}, db.Statement.Clauses["SELECT"].Expression.(clause.Select).Columns) - db, _ = gorm.Open(&tests.DummyDialector{}, nil) + db = openDryRunDB(t) db = db.Scopes(selectScope(sch.Table, []*schema.Field{{DBName: "a"}, {DBName: "b"}}, false)).Select("1 + 1 AS count").Find(nil) assert.Equal(t, []string{"1 + 1 AS count", "`test_models`.`a`", "`test_models`.`b`"}, db.Statement.Selects) + assert.Equal(t, []clause.Column{{Raw: true, Name: "1 + 1 AS count"}, {Raw: true, Name: "`test_models`.`a`"}, {Raw: true, Name: "`test_models`.`b`"}}, db.Statement.Clauses["SELECT"].Expression.(clause.Select).Columns) - db, _ = gorm.Open(&tests.DummyDialector{}, nil) + db = openDryRunDB(t) db = db.Scopes(selectScope(sch.Table, []*schema.Field{}, true)).Select("*, 1 + 1 AS count").Find(nil) assert.Equal(t, []string{"1"}, db.Statement.Selects) + assert.Equal(t, []clause.Column{{Raw: true, Name: "1"}}, db.Statement.Clauses["SELECT"].Expression.(clause.Select).Columns) - db, _ = gorm.Open(&tests.DummyDialector{}, nil) + db = openDryRunDB(t) db = db.Scopes(selectScope(sch.Table, []*schema.Field{{DBName: "c", StructField: reflect.StructField{Tag: `computed:"a+b"`}}}, true)).Select("*, 1 + 1 AS count").Find(nil) assert.Equal(t, []string{"(a+b) `c`"}, db.Statement.Selects) + assert.Equal(t, []clause.Column{{Raw: true, Name: "(a+b) `c`"}}, db.Statement.Clauses["SELECT"].Expression.(clause.Select).Columns) + + db = openDryRunDB(t) + db = db.Joins("relation").Scopes(selectScope(sch.Table, []*schema.Field{{DBName: "c", StructField: reflect.StructField{Tag: `computed:"a+b"`}}}, true)).Select("*, 1 + 1 AS count").Find(nil) + assert.Equal(t, []string{"(a+b) `c`"}, db.Statement.Selects) + assert.Equal(t, []clause.Column{{Raw: true, Name: "(a+b) `c`"}}, db.Statement.Clauses["SELECT"].Expression.(clause.Select).Columns) } func TestGetFieldFinalRelation(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) schema, err := parseModel(db, &FilterTestModel{}) if !assert.Nil(t, err) { return @@ -1209,3 +1463,177 @@ func TestGetFieldFinalRelation(t *testing.T) { assert.Nil(t, sch) assert.Empty(t, joinName) } + +func TestSettingsComputedFieldWithAutoFields(t *testing.T) { + request := &goyave.Request{ + Data: map[string]interface{}{ + "filter": []*Filter{ + {Field: "Relation.a", Args: []string{"val1"}, Operator: Operators["$cont"]}, + }, + "per_page": 15, + }, + Lang: "en-US", + } + db := openDryRunDB(t) + + results := []*TestScopeModel{} + paginator, db := Scope(db, request, results) + + assert.NotNil(t, paginator) + + expected := map[string]clause.Clause{ + "WHERE": { + Name: "WHERE", + Expression: clause.Where{ + Exprs: []clause.Expression{ + clause.AndConditions{ + Exprs: []clause.Expression{ + clause.Expr{SQL: "`Relation`.`a` LIKE ?", Vars: []interface{}{"%val1%"}}, + }, + }, + }, + }, + }, + "LIMIT": { + Expression: clause.Limit{ + Limit: &fifteen, + Offset: 0, + }, + }, + "FROM": { + Name: "FROM", + Expression: clause.From{ + Joins: []clause.Join{ + { + Type: clause.LeftJoin, + Table: clause.Table{ + Name: "test_scope_relations", + Alias: "Relation", + }, + ON: clause.Where{ + Exprs: []clause.Expression{ + clause.Eq{ + Column: clause.Column{ + Table: "test_scope_models", + Name: "relation_id", + }, + Value: clause.Column{ + Table: "Relation", + Name: "id", + }, + }, + }, + }, + }, + }, + }, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Raw: true, Name: "`test_scope_models`.`name`"}, + {Raw: true, Name: "`test_scope_models`.`email`"}, + {Raw: true, Name: "(UPPER(`test_scope_models`.name)) `computed`"}, + {Raw: true, Name: "`test_scope_models`.`id`"}, + {Raw: true, Name: "`test_scope_models`.`relation_id`"}, + }, + }, + }, + } + assert.Equal(t, expected, db.Statement.Clauses) + assert.ElementsMatch(t, []string{"`test_scope_models`.`name`", "`test_scope_models`.`email`", "(UPPER(`test_scope_models`.name)) `computed`", "`test_scope_models`.`id`", "`test_scope_models`.`relation_id`"}, db.Statement.Selects) +} + +func TestSettingsSelectWithExistingJoin(t *testing.T) { + request := &goyave.Request{ + Data: map[string]interface{}{ + "filter": []*Filter{ + {Field: "Relation.a", Args: []string{"val1"}, Operator: Operators["$cont"]}, + }, + "per_page": 15, + }, + Lang: "en-US", + } + db := openDryRunDB(t) + + // We manually join a relation with a condition. + // We expect this join to not be removed nor duplicated, with the condition kept and the fields selected. + db = db.Joins("Relation", db.Session(&gorm.Session{NewDB: true}).Where("Relation.id > ?", 0)) + + results := []*TestScopeModel{} + paginator, db := Scope(db, request, results) + + assert.NotNil(t, paginator) + + expected := map[string]clause.Clause{ + "WHERE": { + Name: "WHERE", + Expression: clause.Where{ + Exprs: []clause.Expression{ + clause.AndConditions{ + Exprs: []clause.Expression{ + clause.Expr{SQL: "`Relation`.`a` LIKE ?", Vars: []interface{}{"%val1%"}}, + }, + }, + }, + }, + }, + "FROM": { + Name: "FROM", + Expression: clause.From{ + Joins: []clause.Join{ + { + Type: clause.LeftJoin, + Table: clause.Table{ + Name: "test_scope_relations", + Alias: "Relation", + }, + ON: clause.Where{ + Exprs: []clause.Expression{ + clause.Eq{ + Column: clause.Column{ + Table: "test_scope_models", + Name: "relation_id", + }, + Value: clause.Column{ + Table: "Relation", + Name: "id", + }, + }, + clause.Where{ + Exprs: []clause.Expression{ + clause.Expr{SQL: "Relation.id > ?", Vars: []interface{}{0}}, + }, + }, + }, + }, + }, + }, + }, + }, + "LIMIT": { + Expression: clause.Limit{ + Limit: &fifteen, + Offset: 0, + }, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Raw: true, Name: "`Relation`.`a` AS `Relation__a`"}, + {Raw: true, Name: "`Relation`.`b` AS `Relation__b`"}, + {Raw: true, Name: "`Relation`.`id` AS `Relation__id`"}, + {Raw: true, Name: "`test_scope_models`.`name`"}, + {Raw: true, Name: "`test_scope_models`.`email`"}, + {Raw: true, Name: "(UPPER(`test_scope_models`.name)) `computed`"}, + {Raw: true, Name: "`test_scope_models`.`id`"}, + {Raw: true, Name: "`test_scope_models`.`relation_id`"}, + }, + }, + }, + } + assert.Equal(t, expected, db.Statement.Clauses) + assert.Empty(t, db.Statement.Joins) +} diff --git a/sort_test.go b/sort_test.go index 3791604..ddeb29c 100644 --- a/sort_test.go +++ b/sort_test.go @@ -4,14 +4,12 @@ import ( "testing" "github.com/stretchr/testify/assert" - "gorm.io/gorm" "gorm.io/gorm/clause" "gorm.io/gorm/schema" - "gorm.io/gorm/utils/tests" ) func TestSortScope(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) sort := &Sort{Field: "notacolumn", Order: SortAscending} schema := &schema.Schema{ FieldsByDBName: map[string]*schema.Field{ @@ -24,7 +22,8 @@ func TestSortScope(t *testing.T) { sort.Field = "name" - db = db.Scopes(sort.Scope(&Settings{}, schema)).Table("table").Find(nil) + results := []map[string]interface{}{} + db = db.Scopes(sort.Scope(&Settings{}, schema)).Table("table").Find(&results) expected := map[string]clause.Clause{ "ORDER BY": { Name: "ORDER BY", @@ -39,12 +38,22 @@ func TestSortScope(t *testing.T) { }, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{}, + }, } assert.Equal(t, expected, db.Statement.Clauses) sort.Order = SortDescending - db, _ = gorm.Open(&tests.DummyDialector{}, nil) - db = db.Scopes(sort.Scope(&Settings{}, schema)).Table("table").Find(nil) + db = openDryRunDB(t) + + results = []map[string]interface{}{} + db = db.Scopes(sort.Scope(&Settings{}, schema)).Table("table").Find(&results) expected["ORDER BY"].Expression.(clause.OrderBy).Columns[0].Desc = true assert.Equal(t, expected, db.Statement.Clauses) } @@ -80,7 +89,7 @@ type SortTestModel struct { } func TestSortScopeWithJoin(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) sort := &Sort{Field: "Relation.name", Order: SortAscending} results := []*SortTestModel{} @@ -132,12 +141,21 @@ func TestSortScopeWithJoin(t *testing.T) { }, }, }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Table: "sort_test_models", Name: "name"}, + {Table: "sort_test_models", Name: "id"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) } func TestSortScopeWithJoinInvalidModel(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) sort := &Sort{Field: "Relation.name", Order: SortDescending} results := []*SortTestModel{} @@ -151,7 +169,7 @@ func TestSortScopeWithJoinInvalidModel(t *testing.T) { } func TestSortScopeWithJoinNestedRelation(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) sort := &Sort{Field: "Relation.NestedRelation.field", Order: SortAscending} results := []*SortTestModel{} @@ -224,13 +242,22 @@ func TestSortScopeWithJoinNestedRelation(t *testing.T) { }, }, }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Table: "sort_test_models", Name: "name"}, + {Table: "sort_test_models", Name: "id"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) } type SortTestModelComputedRelation struct { Name string - Computed string `computed:"~~~ct~~~.computedcolumnrelation"` + Computed string `gorm:"->;-:migration" computed:"~~~ct~~~.computedcolumnrelation"` ID uint ParentID uint } @@ -238,12 +265,12 @@ type SortTestModelComputedRelation struct { type SortTestModelComputed struct { Relation *SortTestModelComputedRelation `gorm:"foreignKey:ParentID"` Name string - Computed string `computed:"~~~ct~~~.computedcolumn"` + Computed string `gorm:"->;-:migration" computed:"~~~ct~~~.computedcolumn"` ID uint } func TestSortScopeComputed(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) sort := &Sort{Field: "computed", Order: SortAscending} results := []*SortTestModelComputed{} @@ -267,12 +294,20 @@ func TestSortScopeComputed(t *testing.T) { }, }, }, + "FROM": { + Name: "FROM", + Expression: clause.From{}, + }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{}, + }, } assert.Equal(t, expected, db.Statement.Clauses) } func TestSortScopeComputedWithJoin(t *testing.T) { - db, _ := gorm.Open(&tests.DummyDialector{}, nil) + db := openDryRunDB(t) sort := &Sort{Field: "Relation.computed", Order: SortAscending} results := []*SortTestModelComputed{} @@ -324,6 +359,16 @@ func TestSortScopeComputedWithJoin(t *testing.T) { }, }, }, + "SELECT": { + Name: "SELECT", + Expression: clause.Select{ + Columns: []clause.Column{ + {Table: "sort_test_model_computeds", Name: "name"}, + {Table: "sort_test_model_computeds", Name: "computed"}, // Should not be problematic that it is added automatically by Gorm since we force only selectable fields all he time. + {Table: "sort_test_model_computeds", Name: "id"}, + }, + }, + }, } assert.Equal(t, expected, db.Statement.Clauses) }