You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by kl...@apache.org on 2022/07/06 08:50:46 UTC
[incubator-devlake-website] 01/01: docs: add Dal document
This is an automated email from the ASF dual-hosted git repository.
klesh pushed a commit to branch kw-220630-dal
in repository https://gitbox.apache.org/repos/asf/incubator-devlake-website.git
commit e1776355fde5e81eb9d37441f7157343dab7e3fd
Author: Klesh Wong <zh...@merico.dev>
AuthorDate: Wed Jul 6 16:50:23 2022 +0800
docs: add Dal document
---
docs/DeveloperManuals/Dal.md | 173 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 173 insertions(+)
diff --git a/docs/DeveloperManuals/Dal.md b/docs/DeveloperManuals/Dal.md
new file mode 100644
index 0000000..da27a55
--- /dev/null
+++ b/docs/DeveloperManuals/Dal.md
@@ -0,0 +1,173 @@
+---
+title: "Dal"
+sidebar_position: 4
+description: >
+ The Dal (Data Access Layer) is designed to decouple the hard dependency on `gorm` in v0.12
+---
+
+## Summary
+
+The Dal (Data Access Layer) is designed to decouple the hard dependency on `gorm` in v0.12. The advantages of introducing this isolation are:
+
+ - Unit Test: Mocking an Interface is easier and more reliable than Patching a Pointer.
+ - Clean Code: DBS operations are more consistence than using `gorm ` directly.
+ - Replaceable: It would be easier to replace `gorm` in the future if needed.
+
+## The Dal Interface
+
+```go
+type Dal interface {
+ AutoMigrate(entity interface{}, clauses ...Clause) error
+ Exec(query string, params ...interface{}) error
+ RawCursor(query string, params ...interface{}) (*sql.Rows, error)
+ Cursor(clauses ...Clause) (*sql.Rows, error)
+ Fetch(cursor *sql.Rows, dst interface{}) error
+ All(dst interface{}, clauses ...Clause) error
+ First(dst interface{}, clauses ...Clause) error
+ Count(clauses ...Clause) (int64, error)
+ Pluck(column string, dest interface{}, clauses ...Clause) error
+ Create(entity interface{}, clauses ...Clause) error
+ Update(entity interface{}, clauses ...Clause) error
+ CreateOrUpdate(entity interface{}, clauses ...Clause) error
+ CreateIfNotExist(entity interface{}, clauses ...Clause) error
+ Delete(entity interface{}, clauses ...Clause) error
+ AllTables() ([]string, error)
+}
+```
+
+
+## How to use
+
+### Query
+```go
+// Get a database cursor
+user := &models.User{}
+cursor, err := db.Cursor(
+ dal.From(user),
+ dal.Where("department = ?", "R&D"),
+ dal.Orderby("id DESC"),
+)
+if err != nil {
+ return err
+}
+for cursor.Next() {
+ err = dal.Fetch(cursor, user) // fetch one record at a time
+ ...
+}
+
+// Get a database cursor by raw sql query
+cursor, err := db.Raw("SELECT * FROM users")
+
+// USE WITH CAUTIOUS: loading a big table at once is slow and dangerous
+// Load all records from database at once.
+users := make([]models.Users, 0)
+err := db.All(&users, dal.Where("department = ?", "R&D"))
+
+// Load a column as Scalar or Slice
+var email string
+err := db.Pluck("email", &username, dal.Where("id = ?", 1))
+var emails []string
+err := db.Pluck("email", &emails)
+
+// Execute query
+err := db.Exec("UPDATE users SET department = ? WHERE department = ?", "Research & Development", "R&D")
+```
+
+### Insert
+```go
+err := db.Create(&models.User{
+ Email: "hello@example.com", // assumming this the Primarykey
+ Name: "hello",
+ Department: "R&D",
+})
+```
+
+### Update
+```go
+err := db.Create(&models.User{
+ Email: "hello@example.com", // assumming this the Primarykey
+ Name: "hello",
+ Department: "R&D",
+})
+```
+### Insert or Update
+```go
+err := db.CreateOrUpdate(&models.User{
+ Email: "hello@example.com", // assuming this is the Primarykey
+ Name: "hello",
+ Department: "R&D",
+})
+```
+
+### Insert if record(by PrimaryKey) didn't exist
+```go
+err := db.CreateIfNotExist(&models.User{
+ Email: "hello@example.com", // assuming this is the Primarykey
+ Name: "hello",
+ Department: "R&D",
+})
+```
+
+### Delete
+```go
+err := db.CreateIfNotExist(&models.User{
+ Email: "hello@example.com", // assuming this is the Primary key
+})
+```
+
+### DDL and others
+```go
+// Returns all table names
+allTables, err := db.AllTables()
+
+// Automigrate: create/add missing table/columns
+// Note: it won't delete any existing columns, nor does it update the column definition
+err := db.AutoMigrate(&models.User{})
+```
+
+## How to do Unit Test
+First, run the command `make mock` to generate the Mocking Stubs, the generated source files should appear in `mocks` folder.
+```
+mocks
+├── ApiResourceHandler.go
+├── AsyncResponseHandler.go
+├── BasicRes.go
+├── CloseablePluginTask.go
+├── ConfigGetter.go
+├── Dal.go
+├── DataConvertHandler.go
+├── ExecContext.go
+├── InjectConfigGetter.go
+├── InjectLogger.go
+├── Iterator.go
+├── Logger.go
+├── Migratable.go
+├── PluginApi.go
+├── PluginBlueprintV100.go
+├── PluginInit.go
+├── PluginMeta.go
+├── PluginTask.go
+├── RateLimitedApiClient.go
+├── SubTaskContext.go
+├── SubTaskEntryPoint.go
+├── SubTask.go
+└── TaskContext.go
+```
+With these Mocking stubs, you may start writing your TestCases using the `mocks.Dal`.
+```go
+import "github.com/apache/incubator-devlake/mocks"
+
+func TestCreateUser(t *testing.T) {
+ mockDal := new(mocks.Dal)
+ mockDal.On("Create", mock.Anything, mock.Anything).Return(nil).Once()
+ userService := &services.UserService{
+ Dal: mockDal,
+ }
+ userService.Post(map[string]interface{}{
+ "email": "helle@example.com",
+ "name": "hello",
+ "department": "R&D",
+ })
+ mockDal.AssertExpectations(t)
+```
+