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