Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: custom database connection for model #254

Merged
merged 24 commits into from
Aug 26, 2023
Merged

feat: custom database connection for model #254

merged 24 commits into from
Aug 26, 2023

Conversation

kkumar-gcc
Copy link
Member

@kkumar-gcc kkumar-gcc commented Jul 29, 2023

Closes: #182

📑 Description

This PR introduces a new Model method to the ORM facade, allowing users to specify custom database connections for specific models.

Example Usage:

// models/user.go

...

func (u *User) Connection() string {
	return "postgresql"
}

...
// routes/web.go
...

func Web() {
       facades.Route().Get("/", func(ctx http.Context) {
		var count int64
                facades.Orm().Query().Model(&models.User{}).Count(&count)
		ctx.Response().Json(http.StatusOK, http.Json{
			"Hello": "Goravel",
		})
	})

        ...
}

✅ Checks

  • My pull request adheres to the code style of this project
  • My code requires changes to the documentation
  • I have updated the documentation as required
  • All the tests have passed

@kkumar-gcc kkumar-gcc requested a review from hwbrzzl July 29, 2023 15:13
@kkumar-gcc kkumar-gcc requested a review from hwbrzzl August 5, 2023 06:00
Copy link
Contributor

@hwbrzzl hwbrzzl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good first step, great work, Bro!

contracts/database/orm/orm.go Outdated Show resolved Hide resolved
database/gorm/query.go Outdated Show resolved Hide resolved
database/gorm/query.go Show resolved Hide resolved
@codecov
Copy link

codecov bot commented Aug 7, 2023

Codecov Report

Patch coverage: 76.62% and project coverage change: +0.19% 🎉

Comparison is base (ab51feb) 65.46% compared to head (54dad30) 65.66%.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #254      +/-   ##
==========================================
+ Coverage   65.46%   65.66%   +0.19%     
==========================================
  Files         137      137              
  Lines        8475     8702     +227     
==========================================
+ Hits         5548     5714     +166     
- Misses       2539     2583      +44     
- Partials      388      405      +17     
Files Changed Coverage Δ
database/gorm/query.go 59.81% <58.92%> (-1.89%) ⬇️
database/gorm/test_utils.go 83.30% <89.65%> (+1.04%) ⬆️
database/gorm/event.go 93.91% <100.00%> (ø)
database/gorm/transaction.go 100.00% <100.00%> (ø)
database/orm.go 43.75% <100.00%> (ø)

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@hwbrzzl hwbrzzl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, you did it, Bro.

database/gorm/transaction.go Outdated Show resolved Hide resolved
database/gorm/query.go Show resolved Hide resolved
@kkumar-gcc
Copy link
Member Author

Hello @hwbrzzl,
I am attempting to create test cases for this functionality. I created a Product model and table, as well as a 'Connection' function and test function for it.

...

// Model with custom connection
type Product struct {
	orm.Model
	orm.SoftDeletes
	Name  string
	Price float64
}

func (p *Product) Connection() string {
	return "postgresql"
}

...

func (s *QueryTestSuite) TestFirst() {
	for _, query := range s.queries {
		tests := []struct {
			name  string
			setup func()
		}{
			{
				name: "success",
				setup: func() {
					// NOTE: this will throw error when create
					product := Product{Name: "create_product", Price: 200}
					s.Nil(query.Create(&product))
					s.True(product.ID > 0)

					var product1 Product
					s.Nil(query.Where("name", "create_product").First(&product1))
					s.True(product1.ID > 0)
	                     },
			},
		}
		for _, test := range tests {
			s.Run(test.name, func() {
				test.setup()
			})
		}
	}
}

But this is giving me below error:

2023/08/19 21:36:03 /home/krish/goravel/framework/database/gorm/query_test.go:1606
[1.545ms] [rows:1] SELECT * FROM `users` WHERE `name` = 'first_user' AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT 1
mysql postgresql
        --- FAIL: TestQueryTestSuite/TestFirst/success (0.06s)
panic: 

mock: Unexpected Method Call
-----------------------------

Get(string)
		0: "database.connections.postgresql.read"

The closest call I have is: 

Get(string)
		0: "database.connections.mysql.read"


Diff: 0: FAIL:  (string=database.connections.postgresql.read) != (string=database.connections.mysql.read) [recovered]
	panic: 

mock: Unexpected Method Call
-----------------------------

Get(string)
		0: "database.connections.postgresql.read"

The closest call I have is: 

Get(string)
		0: "database.connections.mysql.read"


Diff: 0: FAIL:  (string=database.connections.postgresql.read) != (string=database.connections.mysql.read)

goroutine 268 [running]:
testing.tRunner.func1.2({0x12948e0, 0xc000205140})
	/usr/local/go/src/testing/testing.go:1396 +0x24e
testing.tRunner.func1()
	/usr/local/go/src/testing/testing.go:1399 +0x39f
panic({0x12948e0, 0xc000205140})
	/usr/local/go/src/runtime/panic.go:884 +0x212
github.com/stretchr/testify/mock.(*Mock).fail(0xc000219310, {0x144fef6?, 0xc00007af80?}, {0xc0004d83c0?, 0x1?, 0x1?})
	/home/krish/go/pkg/mod/github.com/stretchr/testify@v1.8.4/mock/mock.go:332 +0x145
github.com/stretchr/testify/mock.(*Mock).MethodCalled(0xc000219310, {0x1888a35, 0x3}, {0xc000204f30, 0x1, 0x1})
	/home/krish/go/pkg/mod/github.com/stretchr/testify@v1.8.4/mock/mock.go:492 +0x572
github.com/stretchr/testify/mock.(*Mock).Called(0x12bf700?, {0xc000204f30, 0x1, 0x1})
	/home/krish/go/pkg/mod/github.com/stretchr/testify@v1.8.4/mock/mock.go:464 +0x148
github.com/goravel/framework/contracts/config/mocks.(*Config).Get(0xc000219310, {0xc00015fb00, 0x24}, {0x0?, 0x0, 0x0})
	/home/krish/goravel/framework/contracts/config/mocks/Config.go:41 +0x185
github.com/goravel/framework/database/db.(*ConfigImpl).Reads(0xc00024dd20)
	/home/krish/goravel/framework/database/db/config.go:34 +0x8e
github.com/goravel/framework/database/gorm.(*GormImpl).Make(0xc0008caf00)
	/home/krish/goravel/framework/database/gorm/gorm.go:57 +0x3f
github.com/goravel/framework/database/gorm.NewQueryImpl({0x15d85d8?, 0xc00021f770}, {0x15d9cf0?, 0xc000219310}, {0x15cf6c0?, 0xc0008caf00?})
	/home/krish/goravel/framework/database/gorm/query.go:37 +0x82
github.com/goravel/framework/database/gorm.InitializeQuery({0x15d85d8, 0xc00021f770}, {0x15d9cf0?, 0xc000219310}, {0x1416386, 0xa})
	/home/krish/goravel/framework/database/gorm/wire_gen.go:30 +0x2c5
github.com/goravel/framework/database/gorm.(*QueryImpl).refreshConnection(0xc0004fea50, {0x12c3c40?, 0xc000182840?})
	/home/krish/goravel/framework/database/gorm/query.go:668 +0x1bf
github.com/goravel/framework/database/gorm.(*QueryImpl).Create(0xc0004fea50, {0x12c3c40, 0xc000182840})
	/home/krish/goravel/framework/database/gorm/query.go:75 +0x2c
github.com/goravel/framework/database/gorm.(*QueryTestSuite).TestFirst.func1()
	/home/krish/goravel/framework/database/gorm/query_test.go:1611 +0x1d3
github.com/goravel/framework/database/gorm.(*QueryTestSuite).TestFirst.func2()
	/home/krish/goravel/framework/database/gorm/query_test.go:1622 +0x1f
github.com/stretchr/testify/suite.(*Suite).Run.func2(0x1?)
	/home/krish/go/pkg/mod/github.com/stretchr/testify@v1.8.4/suite/suite.go:112 +0x36
testing.tRunner(0xc0004e9d40, 0xc0009890e0)
	/usr/local/go/src/testing/testing.go:1446 +0x10b
created by testing.(*T).Run
	/usr/local/go/src/testing/testing.go:1493 +0x35f

@hwbrzzl
Copy link
Contributor

hwbrzzl commented Aug 20, 2023

@kkumar-gcc I think you don't need to add a new test case for QueryTestSuite, because we don't need to test all queries for this feature, you can create a new test function: TestConnection, and only test two scenes:

  1. The default query is mysql, the connection of Product model is Postgresql;
  2. The default query is postgresql, the connection of Product model is postgresql;

About the error, it's caused by the default query is mysql, and you don't to mock the postgresql driver. You can mock it like below:

mysqlDocker := NewMysqlDocker()
mysqlDocker.MockConfig.On("Get", "database.connections.postgresql.read").Return(nil)
mysqlDocker.MockConfig.On("Get", "database.connections.postgresql.write").Return(nil)
mysqlDocker.MockConfig.On("GetString", "database.connections.postgresql.host").Return("localhost")
mysqlDocker.MockConfig.On("GetString", "database.connections.postgresql.username").Return(DbUser)
mysqlDocker.MockConfig.On("GetString", "database.connections.postgresql.password").Return(DbPassword)
mysqlDocker.MockConfig.On("GetInt", "database.connections.postgresql.port").Return(r.Port)

@kkumar-gcc
Copy link
Member Author

Hello @hwbrzzl,

I've encountered an issue while attempting to execute a separate test case for the custom connection. Strangely, I'm receiving a MySQL error, even though the connection has been properly switched to PostgreSQL. This error seems unexpected given that the connection has been intentionally refreshed to use PostgreSQL.

023/08/20 16:09:07 /home/krish/goravel/framework/database/gorm/query.go:88 Error 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"goravel_product" ("created_at","updated_at","deleted_at","name","price") VALUES' at line 1
[1.115ms] [rows:0] INSERT INTO "goravel_product" ("created_at","updated_at","deleted_at","name","price") VALUES ('2023-08-20 10:39:07.516','2023-08-20 10:39:07.516',NULL,'create_product',200) RETURNING "id"
    query_test.go:393: 
        	Error Trace:	/home/krish/goravel/framework/database/gorm/query_test.go:393
        	Error:      	Expected nil, but got: &mysql.MySQLError{Number:0x428, SQLState:[5]uint8{0x34, 0x32, 0x30, 0x30, 0x30}, Message:"You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\"goravel_product\" (\"created_at\",\"updated_at\",\"deleted_at\",\"name\",\"price\") VALUES' at line 1"}
        	Test:       	TestCustomConnection
{{0 {2023-08-20 16:09:07 2023-08-20 16:09:07}} {{0001-01-01 00:00:00 +0000 UTC false}} create_product 200}
    query_test.go:395: 
        	Error Trace:	/home/krish/goravel/framework/database/gorm/query_test.go:395
        	Error:      	Should be true
        	Test:       	TestCustomConnection

@hwbrzzl
Copy link
Contributor

hwbrzzl commented Aug 21, 2023

I tested it locally, but haven't found the best way to implement this, I think maybe we need to choose another way: https://gorm.io/docs/dbresolver.html, it's supported by the Gorm official.

Copy link
Contributor

@hwbrzzl hwbrzzl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job!

database/gorm/query.go Outdated Show resolved Hide resolved
database/gorm/query.go Outdated Show resolved Hide resolved
database/gorm/query.go Outdated Show resolved Hide resolved
database/gorm/query.go Outdated Show resolved Hide resolved
database/gorm/query.go Outdated Show resolved Hide resolved
database/gorm/query_test.go Show resolved Hide resolved
database/gorm/query.go Outdated Show resolved Hide resolved
database/gorm/query.go Outdated Show resolved Hide resolved
database/gorm/query.go Outdated Show resolved Hide resolved
Copy link
Contributor

@hwbrzzl hwbrzzl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@hwbrzzl hwbrzzl merged commit f408251 into master Aug 26, 2023
11 checks passed
@hwbrzzl hwbrzzl deleted the kkumar-gcc/#182 branch August 26, 2023 02:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

✨ [Feature] 模型自定义数据库连接
2 participants