Compare commits
6 Commits
4cdfe753c8
...
6f9ff9ab96
Author | SHA1 | Date | |
---|---|---|---|
|
6f9ff9ab96 | ||
|
7b8abf8e5c | ||
|
4546665461 | ||
|
2c1beb30f6 | ||
|
43a1d0509c | ||
|
b14b8788ab |
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,3 +25,4 @@ go.work.sum
|
|||||||
# Custom
|
# Custom
|
||||||
/_output
|
/_output
|
||||||
/deployment/db_data
|
/deployment/db_data
|
||||||
|
/tmp/**
|
||||||
|
5
Makefile
5
Makefile
@ -33,9 +33,12 @@ all: add-copyright format build
|
|||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
build: tidy # build.
|
build: tidy sqlc # build.
|
||||||
@go build -v -ldflags "$(GO_LDFLAGS)" -o $(OUTPUT_DIR)/howmuch $(ROOT_DIR)/cmd/howmuch/main.go 2>/dev/null
|
@go build -v -ldflags "$(GO_LDFLAGS)" -o $(OUTPUT_DIR)/howmuch $(ROOT_DIR)/cmd/howmuch/main.go 2>/dev/null
|
||||||
|
|
||||||
|
.PHONY: sqlc
|
||||||
|
@sqlc generate
|
||||||
|
|
||||||
.PHONY: format
|
.PHONY: format
|
||||||
format: # format code.
|
format: # format code.
|
||||||
@gofmt -s -w ./
|
@gofmt -s -w ./
|
||||||
|
39
README.md
39
README.md
@ -198,9 +198,46 @@ type User struct {
|
|||||||
|
|
||||||
Use Buffalo pop `Soda CLI` to create database migrations.
|
Use Buffalo pop `Soda CLI` to create database migrations.
|
||||||
|
|
||||||
### 2024/10/07
|
### 2024/10/06
|
||||||
|
|
||||||
Implement the architecture design for User entity.
|
Implement the architecture design for User entity.
|
||||||
|
|
||||||
Checked out OpenAPI, and found that it was not that simple at all. It needs
|
Checked out OpenAPI, and found that it was not that simple at all. It needs
|
||||||
a whole package of knowledge about the web development!
|
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).
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ tags:
|
|||||||
- name: user
|
- name: user
|
||||||
|
|
||||||
paths:
|
paths:
|
||||||
/signup:
|
/user/signup:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
- user
|
- user
|
||||||
|
7
docs/error_code.md
Normal file
7
docs/error_code.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Platform level error code design
|
||||||
|
|
||||||
|
- InternalError
|
||||||
|
- InvalidParameter
|
||||||
|
- AuthFailure
|
||||||
|
- ResourceNotFound
|
||||||
|
- FailedOperation
|
2
go.mod
2
go.mod
@ -35,7 +35,6 @@ require (
|
|||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
@ -61,7 +60,6 @@ require (
|
|||||||
golang.org/x/arch v0.8.0 // indirect
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
golang.org/x/crypto v0.27.0 // indirect
|
golang.org/x/crypto v0.27.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
|
||||||
golang.org/x/sys v0.25.0 // indirect
|
golang.org/x/sys v0.25.0 // indirect
|
||||||
golang.org/x/text v0.18.0 // indirect
|
golang.org/x/text v0.18.0 // indirect
|
||||||
google.golang.org/protobuf v1.34.1 // indirect
|
google.golang.org/protobuf v1.34.1 // indirect
|
||||||
|
@ -25,33 +25,66 @@ package controller
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/model"
|
||||||
|
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/usecase/usecase"
|
||||||
"git.vinchent.xyz/vinchent/howmuch/internal/pkg/core"
|
"git.vinchent.xyz/vinchent/howmuch/internal/pkg/core"
|
||||||
|
"git.vinchent.xyz/vinchent/howmuch/internal/pkg/errno"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// User is the user controller interface, it describes all the handlers
|
// User is the user controller interface, it describes all the handlers
|
||||||
// that need to be implemented for the /user endpoint
|
// that need to be implemented for the /user endpoint
|
||||||
type User interface {
|
type User interface {
|
||||||
Signup(core.Context)
|
Signup(core.Context)
|
||||||
UpdateInfo(core.Context)
|
UpdateInfo(*gin.Context)
|
||||||
Login(core.Context)
|
Login(*gin.Context)
|
||||||
Logout(core.Context)
|
Logout(*gin.Context)
|
||||||
ChangePassword(core.Context)
|
ChangePassword(*gin.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserController struct{}
|
type UserController struct {
|
||||||
|
userUsecase usecase.User
|
||||||
|
}
|
||||||
|
|
||||||
|
var UserParamsErr = &errno.Errno{
|
||||||
|
HTTP: http.StatusBadRequest,
|
||||||
|
Code: errno.ErrorCode(errno.InvalidParameterCode, "UserParamsErr"),
|
||||||
|
Message: "user info is not correct",
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserController(us usecase.User) User {
|
||||||
|
return &UserController{
|
||||||
|
userUsecase: us,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (uc *UserController) Signup(ctx core.Context) {
|
func (uc *UserController) Signup(ctx core.Context) {
|
||||||
ctx.JSON(http.StatusOK, "hello")
|
var params model.User
|
||||||
|
|
||||||
|
if err := ctx.Bind(¶ms); err != nil {
|
||||||
|
core.WriteResponse(ctx, UserParamsErr, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check params validity (govalidator)
|
||||||
|
|
||||||
|
_, err := uc.userUsecase.Create(ctx, ¶ms)
|
||||||
|
if err != nil {
|
||||||
|
core.WriteResponse(ctx, err, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
core.WriteResponse(ctx, errno.OK, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uc *UserController) UpdateInfo(ctx core.Context) {
|
func (uc *UserController) UpdateInfo(ctx *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uc *UserController) Login(ctx core.Context) {
|
func (uc *UserController) Login(ctx *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uc *UserController) Logout(ctx core.Context) {
|
func (uc *UserController) Logout(ctx *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uc *UserController) ChangePassword(ctx core.Context) {
|
func (uc *UserController) ChangePassword(ctx *gin.Context) {
|
||||||
}
|
}
|
||||||
|
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
|
||||||
|
}
|
88
internal/howmuch/adapter/repo/user.go
Normal file
88
internal/howmuch/adapter/repo/user.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
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.Tx)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("transaction is not a pgx.Tx")
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
@ -37,6 +37,7 @@ import (
|
|||||||
"git.vinchent.xyz/vinchent/howmuch/internal/pkg/log"
|
"git.vinchent.xyz/vinchent/howmuch/internal/pkg/log"
|
||||||
"git.vinchent.xyz/vinchent/howmuch/pkg/version/verflag"
|
"git.vinchent.xyz/vinchent/howmuch/pkg/version/verflag"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/jackc/pgx/v5"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
@ -105,24 +106,27 @@ func run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Init DB
|
// Init DB
|
||||||
dbPool, err := datastore.NewDB(
|
dbConfString := fmt.Sprintf(
|
||||||
fmt.Sprintf(
|
"host=%s port=%d dbname=%s user=%s password=%s sslmode=%s",
|
||||||
"host=%s port=%d dbname=%s user=%s password=%s sslmode=%s",
|
viper.GetString("db.host"),
|
||||||
viper.GetString("db.host"),
|
viper.GetInt("db.port"),
|
||||||
viper.GetInt("db.port"),
|
viper.GetString("db.database"),
|
||||||
viper.GetString("db.database"),
|
viper.GetString("db.username"),
|
||||||
viper.GetString("db.username"),
|
viper.GetString("db.password"),
|
||||||
viper.GetString("db.password"),
|
viper.GetString("db.sslmode"),
|
||||||
viper.GetString("db.sslmode"),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
dbConf, err := pgx.ParseConfig(dbConfString)
|
||||||
|
if err != nil {
|
||||||
|
log.FatalLog("DB connection config failure", "err", err, "cfg string", dbConfString)
|
||||||
|
}
|
||||||
|
dbConn, err := datastore.NewDB(dbConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.FatalLog("DB connection failure", "err", err)
|
log.FatalLog("DB connection failure", "err", err)
|
||||||
}
|
}
|
||||||
defer dbPool.Close()
|
defer dbConn.Close(context.Background())
|
||||||
|
|
||||||
// Register the core service
|
// Register the core service
|
||||||
r := registry.NewRegistry(dbPool)
|
r := registry.NewRegistry(dbConn)
|
||||||
|
|
||||||
engine := gin.Default()
|
engine := gin.Default()
|
||||||
|
|
||||||
|
@ -25,12 +25,12 @@ package datastore
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewDB creates a new database for the application
|
// NewDB creates a new database for the application
|
||||||
func NewDB(dsn string) (*pgxpool.Pool, error) {
|
func NewDB(connConfig *pgx.ConnConfig) (*pgx.Conn, error) {
|
||||||
conn, err := pgxpool.New(context.Background(), dsn)
|
conn, err := pgx.ConnectConfig(context.Background(), connConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,13 @@ func Routes(engine *gin.Engine, c controller.AppController) *gin.Engine {
|
|||||||
core.WriteResponse(ctx, errno.PageNotFoundErr, nil)
|
core.WriteResponse(ctx, errno.PageNotFoundErr, nil)
|
||||||
})
|
})
|
||||||
|
|
||||||
engine.POST("/signup", func(ctx *gin.Context) { c.User.Signup(ctx) })
|
v1 := engine.Group("/v1")
|
||||||
|
{
|
||||||
|
userV1 := v1.Group("/user")
|
||||||
|
{
|
||||||
|
userV1.POST("/signup", func(ctx *gin.Context) { c.User.Signup(ctx) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return engine
|
return engine
|
||||||
}
|
}
|
||||||
|
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 (
|
import (
|
||||||
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/adapter/controller"
|
"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.
|
// registry is an implementation of Registry interface.
|
||||||
@ -32,7 +32,7 @@ import (
|
|||||||
// It might holds other drivers when the projects grows. For example
|
// It might holds other drivers when the projects grows. For example
|
||||||
// the object needed to connect to Redis or Kafka.
|
// the object needed to connect to Redis or Kafka.
|
||||||
type registry struct {
|
type registry struct {
|
||||||
db *pgxpool.Pool
|
db *pgx.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registry returns a new app controller that will be used by main()/run()
|
// 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.
|
// NewRegistry returns a new Registry's implementation.
|
||||||
func NewRegistry(db *pgxpool.Pool) Registry {
|
func NewRegistry(db *pgx.Conn) Registry {
|
||||||
return ®istry{db: db}
|
return ®istry{db: db}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,9 +22,14 @@
|
|||||||
|
|
||||||
package registry
|
package registry
|
||||||
|
|
||||||
import "git.vinchent.xyz/vinchent/howmuch/internal/howmuch/adapter/controller"
|
import (
|
||||||
|
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/adapter/controller"
|
||||||
|
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/adapter/repo"
|
||||||
|
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/usecase/usecase"
|
||||||
|
)
|
||||||
|
|
||||||
// NewUserController returns a user controller's implementation
|
// NewUserController returns a user controller's implementation
|
||||||
func (r *registry) NewUserController() controller.User {
|
func (r *registry) NewUserController() controller.User {
|
||||||
return &controller.UserController{}
|
u := usecase.NewUserUsecase(repo.NewUserRepository(r.db), repo.NewDBRepository(r.db))
|
||||||
|
return controller.NewUserController(u)
|
||||||
}
|
}
|
||||||
|
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
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
user := data.(*model.User)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
@ -22,6 +22,18 @@
|
|||||||
|
|
||||||
package core
|
package core
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
type Context interface {
|
type Context interface {
|
||||||
|
// Context
|
||||||
|
Deadline() (deadline time.Time, ok bool)
|
||||||
|
Done() <-chan struct{}
|
||||||
|
Err() error
|
||||||
|
Value(key any) any
|
||||||
|
|
||||||
|
// Request
|
||||||
|
Bind(obj any) error
|
||||||
|
|
||||||
|
// Response
|
||||||
JSON(code int, obj any)
|
JSON(code int, obj any)
|
||||||
}
|
}
|
||||||
|
@ -24,18 +24,28 @@ package errno
|
|||||||
|
|
||||||
import "net/http"
|
import "net/http"
|
||||||
|
|
||||||
|
type PlatformLevelErrCode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
InternalErrorCode = "InternalError"
|
||||||
|
InvalidParameterCode = "InvalidParameter"
|
||||||
|
AuthFailureCode = "AuthFailure"
|
||||||
|
ResourceNotFoundCode = "ResourceNotFound"
|
||||||
|
FailedOperationCode = "FailedOperation"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
OK = &Errno{HTTP: http.StatusOK, Code: "", Message: ""}
|
OK = &Errno{HTTP: http.StatusOK, Code: "", Message: ""}
|
||||||
|
|
||||||
InternalServerErr = &Errno{
|
InternalServerErr = &Errno{
|
||||||
HTTP: http.StatusInternalServerError,
|
HTTP: http.StatusInternalServerError,
|
||||||
Code: "InternalError",
|
Code: InternalErrorCode,
|
||||||
Message: "Internal server error",
|
Message: "Internal server error",
|
||||||
}
|
}
|
||||||
|
|
||||||
PageNotFoundErr = &Errno{
|
PageNotFoundErr = &Errno{
|
||||||
HTTP: http.StatusNotFound,
|
HTTP: http.StatusNotFound,
|
||||||
Code: "ResourceNotFound.PageNotFound",
|
Code: ErrorCode(ResourceNotFoundCode, "PageNotFound"),
|
||||||
Message: "Page not found",
|
Message: "Page not found",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -28,6 +28,10 @@ type Errno struct {
|
|||||||
Message string
|
Message string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ErrorCode(platformErrCode string, resourceErrCode string) string {
|
||||||
|
return platformErrCode + "." + resourceErrCode
|
||||||
|
}
|
||||||
|
|
||||||
// Error implements Error() method in error interface
|
// Error implements Error() method in error interface
|
||||||
func (err *Errno) Error() string {
|
func (err *Errno) Error() string {
|
||||||
return err.Message
|
return err.Message
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE "admin"
|
||||||
|
ADD CONSTRAINT unique_email UNIQUE ("email");
|
4
sqlc.yml
4
sqlc.yml
@ -23,9 +23,9 @@
|
|||||||
version: "2"
|
version: "2"
|
||||||
sql:
|
sql:
|
||||||
- engine: "postgresql"
|
- engine: "postgresql"
|
||||||
queries: "internal/app/adapter/repo/sqlc"
|
queries: "internal/howmuch/adapter/repo/sqlc"
|
||||||
schema: "migrations"
|
schema: "migrations"
|
||||||
gen:
|
gen:
|
||||||
go:
|
go:
|
||||||
out: "internal/app/controller/repo/sqlc"
|
out: "internal/howmuch/adapter/repo/sqlc"
|
||||||
sql_package: "pgx/v5"
|
sql_package: "pgx/v5"
|
||||||
|
@ -1 +0,0 @@
|
|||||||
exit status 2exit status 2exit status 2exit status 2exit status 2
|
|
Loading…
x
Reference in New Issue
Block a user