feat: Create user usecase
This commit is contained in:
		
							
								
								
									
										39
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								README.md
									
									
									
									
									
								
							| @ -198,9 +198,46 @@ type User struct { | ||||
|  | ||||
| Use Buffalo pop `Soda CLI` to create database migrations. | ||||
|  | ||||
| ### 2024/10/07 | ||||
| ### 2024/10/06 | ||||
|  | ||||
| Implement the architecture design for User entity. | ||||
|  | ||||
| Checked out OpenAPI, and found that it was not that simple at all. It needs | ||||
| a whole package of knowledge about the web development! | ||||
|  | ||||
| For the test-driven part, | ||||
|  | ||||
| - model layer: just model designs, **nothing to test** | ||||
| - infra: routes and db connections, it works when it works. Nothing to test. | ||||
| - registry: Just return some structs, no logic. **Not worth testing** | ||||
| - adapter: | ||||
|     - input-port (controller) test: it is about testing parsing the input | ||||
|     value, and the output results writing. The unit test of controller is to | ||||
|     **make sure that they behave as defined in the API documentation**. To | ||||
|     test, we have to mock the **business service**. | ||||
|     - output-port (repo) test: it is about testing converting business model | ||||
|     to database model and the interaction with the database. If we are going | ||||
|     to test them, it's about simulating different type of database behaviour | ||||
|     (success, timeout, etc.). To test, we have to mock the | ||||
|     **database connection**. | ||||
| - usecase: This is the core part to test, it's about the core business. | ||||
| We provide the data input and we check the data output in a fake repository. | ||||
|  | ||||
| With this design, although it may seem overkill for this little project, fits | ||||
| perfectly well with the TDD method. | ||||
|  | ||||
| Concretely, I will do the TDD for my usecase level development, and for the | ||||
| rest, I just put unit tests aside for later. | ||||
|  | ||||
| #### Workflow | ||||
|  | ||||
| 1. OAS Definition | ||||
| 2. (Integration/Validation test) | ||||
| 3. Usecase unit test cases | ||||
| 4. Usecase development | ||||
| 5. Refactor (2-3-4) | ||||
| 6. Input-port/Output-port | ||||
|  | ||||
| That should be the correct workflow. But to save time, I will cut off the | ||||
| integration test part (the 2nd point). | ||||
|  | ||||
|  | ||||
							
								
								
									
										68
									
								
								internal/howmuch/adapter/repo/db.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								internal/howmuch/adapter/repo/db.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,68 @@ | ||||
| // MIT License | ||||
| // | ||||
| // Copyright (c) 2024 vinchent <vinchent@vinchent.xyz> | ||||
| // | ||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| // of this software and associated documentation files (the "Software"), to deal | ||||
| // in the Software without restriction, including without limitation the rights | ||||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| // copies of the Software, and to permit persons to whom the Software is | ||||
| // furnished to do so, subject to the following conditions: | ||||
| // | ||||
| // The above copyright notice and this permission notice shall be included in all | ||||
| // copies or substantial portions of the Software. | ||||
| // | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| // SOFTWARE. | ||||
|  | ||||
| package repo | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
|  | ||||
| 	"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/usecase/repo" | ||||
| 	"git.vinchent.xyz/vinchent/howmuch/internal/pkg/log" | ||||
| 	"github.com/jackc/pgx/v5" | ||||
| ) | ||||
|  | ||||
| type dbRepository struct { | ||||
| 	db *pgx.Conn | ||||
| } | ||||
|  | ||||
| func NewDBRepository(db *pgx.Conn) repo.DBRepository { | ||||
| 	return &dbRepository{ | ||||
| 		db: db, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // XXX: Do I need rollback? in which cases? | ||||
|  | ||||
| func (dr *dbRepository) Transaction( | ||||
| 	ctx context.Context, | ||||
| 	txFunc func(txCtx context.Context, tx interface{}) (interface{}, error), | ||||
| ) (interface{}, error) { | ||||
| 	tx, err := dr.db.Begin(ctx) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	defer func() { | ||||
| 		if p := recover(); p != nil { | ||||
| 			tx.Rollback(ctx) | ||||
| 			log.PanicLog("transaction panicked!") | ||||
| 		} else if err != nil { | ||||
| 			tx.Rollback(ctx) | ||||
| 			log.ErrorLog("transaction failed!", "err", err) | ||||
| 		} else { | ||||
| 			err = tx.Commit(ctx) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	data, err := txFunc(ctx, tx) | ||||
| 	return data, err | ||||
| } | ||||
							
								
								
									
										91
									
								
								internal/howmuch/adapter/repo/user.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								internal/howmuch/adapter/repo/user.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,91 @@ | ||||
| // MIT License | ||||
| // | ||||
| // Copyright (c) 2024 vinchent <vinchent@vinchent.xyz> | ||||
| // | ||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| // of this software and associated documentation files (the "Software"), to deal | ||||
| // in the Software without restriction, including without limitation the rights | ||||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| // copies of the Software, and to permit persons to whom the Software is | ||||
| // furnished to do so, subject to the following conditions: | ||||
| // | ||||
| // The above copyright notice and this permission notice shall be included in all | ||||
| // copies or substantial portions of the Software. | ||||
| // | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| // SOFTWARE. | ||||
|  | ||||
| package repo | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"time" | ||||
|  | ||||
| 	"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/adapter/repo/sqlc" | ||||
| 	"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/model" | ||||
| 	"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/usecase/repo" | ||||
| 	"github.com/jackc/pgx/v5" | ||||
| 	"github.com/jackc/pgx/v5/pgtype" | ||||
| ) | ||||
|  | ||||
| // Get business service model, convert to the DB model (generated by sqlc) | ||||
| // To test repo's methods, I have to mock a pgx.Conn | ||||
|  | ||||
| type userRepository struct { | ||||
| 	db *pgx.Conn | ||||
| } | ||||
|  | ||||
| const insertTimeout = 1 * time.Second | ||||
|  | ||||
| func NewUserRepository(db *pgx.Conn) repo.UserRepository { | ||||
| 	return &userRepository{ | ||||
| 		db: db, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Create | ||||
| func (ur *userRepository) Create( | ||||
| 	ctx context.Context, | ||||
| 	transaction interface{}, | ||||
| 	u *model.User, | ||||
| ) (*model.User, error) { | ||||
| 	timeoutCtx, cancel := context.WithTimeout(ctx, insertTimeout) | ||||
| 	defer cancel() | ||||
|  | ||||
| 	args := sqlc.InsertUserParams{ | ||||
| 		Email:     u.Email, | ||||
| 		FirstName: u.FirstName, | ||||
| 		LastName:  u.LastName, | ||||
| 		Password:  u.Password, | ||||
| 		CreatedAt: pgtype.Timestamp{Time: time.Now(), Valid: true}, | ||||
| 		UpdatedAt: pgtype.Timestamp{Time: time.Now(), Valid: true}, | ||||
| 	} | ||||
|  | ||||
| 	tx, ok := transaction.(*pgx.Conn) | ||||
| 	if !ok { | ||||
| 		return nil, errors.New("transaction is not a *pgx.Conn") | ||||
| 	} | ||||
|  | ||||
| 	queries := sqlc.New(tx) | ||||
|  | ||||
| 	userDB, err := queries.InsertUser(timeoutCtx, args) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return &model.User{ | ||||
| 		ID:        int(userDB.ID), | ||||
| 		Email:     userDB.Email, | ||||
| 		FirstName: userDB.FirstName, | ||||
| 		LastName:  userDB.LastName, | ||||
| 		Password:  userDB.Password, | ||||
| 		CreatedAt: userDB.CreatedAt.Time, | ||||
| 		UpdatedAt: userDB.CreatedAt.Time, | ||||
| 	}, nil | ||||
| } | ||||
							
								
								
									
										36
									
								
								internal/howmuch/model/user.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								internal/howmuch/model/user.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
| // MIT License | ||||
| // | ||||
| // Copyright (c) 2024 vinchent <vinchent@vinchent.xyz> | ||||
| // | ||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| // of this software and associated documentation files (the "Software"), to deal | ||||
| // in the Software without restriction, including without limitation the rights | ||||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| // copies of the Software, and to permit persons to whom the Software is | ||||
| // furnished to do so, subject to the following conditions: | ||||
| // | ||||
| // The above copyright notice and this permission notice shall be included in all | ||||
| // copies or substantial portions of the Software. | ||||
| // | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| // SOFTWARE. | ||||
|  | ||||
| package model | ||||
|  | ||||
| import "time" | ||||
|  | ||||
| // User model | ||||
| type User struct { | ||||
| 	ID        int       `json:"id"` | ||||
| 	Email     string    `json:"email"` | ||||
| 	FirstName string    `json:"first_name"` | ||||
| 	LastName  string    `json:"last_name"` | ||||
| 	Password  string    `json:"password"` | ||||
| 	CreatedAt time.Time `json:"created_at"` | ||||
| 	UpdatedAt time.Time `json:"updated_at"` | ||||
| } | ||||
| @ -24,7 +24,7 @@ package registry | ||||
|  | ||||
| import ( | ||||
| 	"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/adapter/controller" | ||||
| 	"github.com/jackc/pgx/v5/pgxpool" | ||||
| 	"github.com/jackc/pgx/v5" | ||||
| ) | ||||
|  | ||||
| // registry is an implementation of Registry interface. | ||||
| @ -32,7 +32,7 @@ import ( | ||||
| // It might holds other drivers when the projects grows. For example | ||||
| // the object needed to connect to Redis or Kafka. | ||||
| type registry struct { | ||||
| 	db *pgxpool.Pool | ||||
| 	db *pgx.Conn | ||||
| } | ||||
|  | ||||
| // Registry returns a new app controller that will be used by main()/run() | ||||
| @ -43,7 +43,7 @@ type Registry interface { | ||||
| } | ||||
|  | ||||
| // NewRegistry returns a new Registry's implementation. | ||||
| func NewRegistry(db *pgxpool.Pool) Registry { | ||||
| func NewRegistry(db *pgx.Conn) Registry { | ||||
| 	return ®istry{db: db} | ||||
| } | ||||
|  | ||||
|  | ||||
							
								
								
									
										32
									
								
								internal/howmuch/usecase/repo/db.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								internal/howmuch/usecase/repo/db.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | ||||
| // MIT License | ||||
| // | ||||
| // Copyright (c) 2024 vinchent <vinchent@vinchent.xyz> | ||||
| // | ||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| // of this software and associated documentation files (the "Software"), to deal | ||||
| // in the Software without restriction, including without limitation the rights | ||||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| // copies of the Software, and to permit persons to whom the Software is | ||||
| // furnished to do so, subject to the following conditions: | ||||
| // | ||||
| // The above copyright notice and this permission notice shall be included in all | ||||
| // copies or substantial portions of the Software. | ||||
| // | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| // SOFTWARE. | ||||
|  | ||||
| package repo | ||||
|  | ||||
| import "context" | ||||
|  | ||||
| type DBRepository interface { | ||||
| 	Transaction( | ||||
| 		ctx context.Context, | ||||
| 		txFunc func(txCtx context.Context, tx interface{}) (interface{}, error), | ||||
| 	) (interface{}, error) | ||||
| } | ||||
							
								
								
									
										34
									
								
								internal/howmuch/usecase/repo/testdb.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								internal/howmuch/usecase/repo/testdb.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| // MIT License | ||||
| // | ||||
| // Copyright (c) 2024 vinchent <vinchent@vinchent.xyz> | ||||
| // | ||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| // of this software and associated documentation files (the "Software"), to deal | ||||
| // in the Software without restriction, including without limitation the rights | ||||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| // copies of the Software, and to permit persons to whom the Software is | ||||
| // furnished to do so, subject to the following conditions: | ||||
| // | ||||
| // The above copyright notice and this permission notice shall be included in all | ||||
| // copies or substantial portions of the Software. | ||||
| // | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| // SOFTWARE. | ||||
|  | ||||
| package repo | ||||
|  | ||||
| import "context" | ||||
|  | ||||
| type TestDBRepository struct{} | ||||
|  | ||||
| func (tdr *TestDBRepository) Transaction( | ||||
| 	ctx context.Context, | ||||
| 	txFunc func(txCtx context.Context, tx interface{}) (interface{}, error), | ||||
| ) (interface{}, error) { | ||||
| 	return txFunc(ctx, nil) | ||||
| } | ||||
							
								
								
									
										42
									
								
								internal/howmuch/usecase/repo/testuser.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								internal/howmuch/usecase/repo/testuser.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | ||||
| // MIT License | ||||
| // | ||||
| // Copyright (c) 2024 vinchent <vinchent@vinchent.xyz> | ||||
| // | ||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| // of this software and associated documentation files (the "Software"), to deal | ||||
| // in the Software without restriction, including without limitation the rights | ||||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| // copies of the Software, and to permit persons to whom the Software is | ||||
| // furnished to do so, subject to the following conditions: | ||||
| // | ||||
| // The above copyright notice and this permission notice shall be included in all | ||||
| // copies or substantial portions of the Software. | ||||
| // | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| // SOFTWARE. | ||||
|  | ||||
| package repo | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
|  | ||||
| 	"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/model" | ||||
| ) | ||||
|  | ||||
| type TestUserRepository struct{} | ||||
|  | ||||
| func (tur *TestUserRepository) Create( | ||||
| 	ctx context.Context, | ||||
| 	transaction interface{}, | ||||
| 	u *model.User, | ||||
| ) (*model.User, error) { | ||||
| 	user := *u | ||||
|  | ||||
| 	user.ID = 123 | ||||
| 	return &user, nil | ||||
| } | ||||
							
								
								
									
										33
									
								
								internal/howmuch/usecase/repo/user.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								internal/howmuch/usecase/repo/user.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| // MIT License | ||||
| // | ||||
| // Copyright (c) 2024 vinchent <vinchent@vinchent.xyz> | ||||
| // | ||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| // of this software and associated documentation files (the "Software"), to deal | ||||
| // in the Software without restriction, including without limitation the rights | ||||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| // copies of the Software, and to permit persons to whom the Software is | ||||
| // furnished to do so, subject to the following conditions: | ||||
| // | ||||
| // The above copyright notice and this permission notice shall be included in all | ||||
| // copies or substantial portions of the Software. | ||||
| // | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| // SOFTWARE. | ||||
|  | ||||
| package repo | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
|  | ||||
| 	"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/model" | ||||
| ) | ||||
|  | ||||
| type UserRepository interface { | ||||
| 	Create(ctx context.Context, transaction interface{}, u *model.User) (*model.User, error) | ||||
| } | ||||
							
								
								
									
										78
									
								
								internal/howmuch/usecase/usecase/user.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								internal/howmuch/usecase/usecase/user.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,78 @@ | ||||
| // MIT License | ||||
| // | ||||
| // Copyright (c) 2024 vinchent <vinchent@vinchent.xyz> | ||||
| // | ||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| // of this software and associated documentation files (the "Software"), to deal | ||||
| // in the Software without restriction, including without limitation the rights | ||||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| // copies of the Software, and to permit persons to whom the Software is | ||||
| // furnished to do so, subject to the following conditions: | ||||
| // | ||||
| // The above copyright notice and this permission notice shall be included in all | ||||
| // copies or substantial portions of the Software. | ||||
| // | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| // SOFTWARE. | ||||
|  | ||||
| package usecase | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
|  | ||||
| 	"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/model" | ||||
| 	"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/usecase/repo" | ||||
| 	"git.vinchent.xyz/vinchent/howmuch/internal/pkg/log" | ||||
| ) | ||||
|  | ||||
| type userUsecase struct { | ||||
| 	userRepo repo.UserRepository | ||||
| 	dbRepo   repo.DBRepository | ||||
| } | ||||
|  | ||||
| type User interface { | ||||
| 	Create(ctx context.Context, u *model.User) (*model.User, error) | ||||
| } | ||||
|  | ||||
| func NewUserUsecase(r repo.UserRepository, d repo.DBRepository) User { | ||||
| 	return &userUsecase{ | ||||
| 		userRepo: r, | ||||
| 		dbRepo:   d, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (uuc *userUsecase) Create(ctx context.Context, u *model.User) (*model.User, error) { | ||||
| 	data, err := uuc.dbRepo.Transaction( | ||||
| 		ctx, | ||||
| 		func(txCtx context.Context, tx interface{}) (interface{}, error) { | ||||
| 			u, err := uuc.userRepo.Create(txCtx, tx, u) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
|  | ||||
| 			// TODO: App log, maybe can be sent to some third party service. | ||||
| 			log.InfoLog( | ||||
| 				"created new user", | ||||
| 				"email", | ||||
| 				u.Email, | ||||
| 				"name", | ||||
| 				fmt.Sprintf("%s %s", u.FirstName, u.LastName), | ||||
| 			) | ||||
|  | ||||
| 			return u, err | ||||
| 		}, | ||||
| 	) | ||||
| 	user := data.(*model.User) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return user, nil | ||||
| } | ||||
							
								
								
									
										49
									
								
								internal/howmuch/usecase/usecase/user_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								internal/howmuch/usecase/usecase/user_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | ||||
| // MIT License | ||||
| // | ||||
| // Copyright (c) 2024 vinchent <vinchent@vinchent.xyz> | ||||
| // | ||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| // of this software and associated documentation files (the "Software"), to deal | ||||
| // in the Software without restriction, including without limitation the rights | ||||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| // copies of the Software, and to permit persons to whom the Software is | ||||
| // furnished to do so, subject to the following conditions: | ||||
| // | ||||
| // The above copyright notice and this permission notice shall be included in all | ||||
| // copies or substantial portions of the Software. | ||||
| // | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| // SOFTWARE. | ||||
|  | ||||
| package usecase | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"testing" | ||||
|  | ||||
| 	"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/model" | ||||
| 	"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/usecase/repo" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestCreateUser(t *testing.T) { | ||||
| 	ctx := context.Background() | ||||
| 	userUsecase := NewUserUsecase(&repo.TestUserRepository{}, &repo.TestDBRepository{}) | ||||
| 	input := &model.User{ | ||||
| 		Email:     "a@b.c", | ||||
| 		FirstName: "James", | ||||
| 		LastName:  "Bond", | ||||
| 		Password:  "verystrong", | ||||
| 	} | ||||
| 	want := input | ||||
| 	want.ID = 123 | ||||
|  | ||||
| 	got, err := userUsecase.Create(ctx, input) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, want, got) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user