Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ab116d3
ユーザー登録をトランザクション化しE2Eテストを追加
Jun 3, 2026
287990a
E2Eをフロント本体の型チェック対象から除外
Jun 3, 2026
74375ef
サインアップの機密情報をリクエストボディで送信
Jun 3, 2026
f41e776
リポジトリの認証関連クエリをgoqu化
Jun 4, 2026
53fb33e
認証リポジトリのデバッグ出力を削除
Jun 4, 2026
4fdcb10
E2EをCIで実行するワークフローを追加
Jun 4, 2026
3c022b2
レビュー指摘に基づきsignup検証とエラー伝播を追加
Jun 4, 2026
2b75be9
CI向けにE2Eのenvファイル依存を緩和
Jun 4, 2026
33638ce
ユーザー削除でTransactionRepositoryを使用
Jun 5, 2026
59b14cf
Potential fix for pull request finding
hikahana Jun 9, 2026
943f7ea
Potential fix for pull request finding
hikahana Jun 9, 2026
b8890ee
Merge branch 'develop' into codex/transactional-signup-e2e
Jun 9, 2026
14e404a
サインアップE2Eのレビュー指摘を反映
Jun 11, 2026
4d01a48
E2E用ESLint設定を追加
Jun 14, 2026
f8a8adb
signup入力検証をOpenAPIに委譲
Jun 14, 2026
930957a
OpenAPIバリデーションをsignupに限定
Jun 14, 2026
a1048b9
OpenAPIバリデーション限定理由をコメント化
Jun 14, 2026
56573b3
レビュー指摘に基づきE2Eと削除処理を改善
Jun 14, 2026
4efc548
mail_authのメール無効化メソッド名を改善
Jun 15, 2026
6c44dad
mail_authのクエリ生成を共通化
Jun 26, 2026
f456ce5
repositoryのクエリ生成を共通化
Jun 26, 2026
c739eee
テストケースの説明コメントを追加
Jun 26, 2026
7ff0ab6
レビュー指摘事項を修正
Jun 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: E2E

on:
workflow_dispatch:
pull_request:
branches:
- develop
paths:
- ".github/workflows/e2e.yml"
- "Makefile"
- "compose.e2e.yml"
- "api/**"
- "mysql/**"
- "openapi/**"
- "view/**"
- "my.cnf"

concurrency:
group: e2e-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
signup-e2e:
runs-on: ubuntu-latest
timeout-minutes: 20

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Run E2E
run: make run-e2e

- name: Cleanup E2E containers
if: always()
run: docker compose -f compose.e2e.yml down --volumes --remove-orphans
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# アプリコンテナ=view,api、DBコンテナ=db,minio
include finansu.env
-include finansu.env

# 配色
SHELL := /bin/bash
Expand Down Expand Up @@ -216,6 +216,12 @@ run-test: ## APIテスト実行
run-eslint: ## ESLint実行
docker compose exec view pnpm run lint

run-e2e: ## E2Eテスト実行 (DB/API/View/PlaywrightをDocker内で完結)
docker compose -f compose.e2e.yml down --volumes --remove-orphans
docker compose -f compose.e2e.yml up --build -d db minio migrate seed api view
docker compose -f compose.e2e.yml run --rm --no-deps e2e
docker compose -f compose.e2e.yml down --volumes --remove-orphans

##@ クリーンアップ
del-vol: ## アプリコンテナボリューム削除
docker compose down -v
Expand Down
16 changes: 15 additions & 1 deletion api/drivers/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/NUTFes/FinanSu/api/generated"
echo "github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
oapimiddleware "github.com/oapi-codegen/echo-middleware"
)

func RunServer(server *handler.Handler) *echo.Echo {
Expand All @@ -28,10 +29,23 @@ func RunServer(server *handler.Handler) *echo.Echo {

// CORS対策
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"http://localhost:3000", "127.0.0.1:3000", "http://localhost:3001", "127.0.0.1:3001", "http://localhost:8000", "127.0.0.1:8000", "https://finansu.nutfes.net", "https://stg-finansu.nutfes.net"}, // ドメイン
AllowOrigins: []string{"http://localhost:3000", "127.0.0.1:3000", "http://view:3000", "http://localhost:3001", "127.0.0.1:3001", "http://localhost:8000", "127.0.0.1:8000", "https://finansu.nutfes.net", "https://stg-finansu.nutfes.net"}, // ドメイン
AllowMethods: []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete},
}))

swagger, err := generated.GetSwagger()
if err != nil {
panic(err)
}
swagger.Servers = nil
e.Use(oapimiddleware.OapiRequestValidatorWithOptions(swagger, &oapimiddleware.Options{
Skipper: func(c echo.Context) bool {
// TODO: OpenAPI定義と既存APIの実装差分を解消し、全体にvalidatorを適用する。
// 現状は全APIへ適用すると既存エンドポイントに影響が出るため、signupだけを検証対象にしている。
return c.Path() != "/mail_auth/signup" || c.Request().Method != http.MethodPost
},
}))

// ルーティング

generated.RegisterHandlers(e, server)
Expand Down
17 changes: 12 additions & 5 deletions api/externals/handler/mail_auth_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,18 @@ func (h *Handler) DeleteMailAuthSignout(c echo.Context, params generated.DeleteM
}

// router.POST(baseURL+"/mail_auth/signup", wrapper.PostMailAuthSignup)
func (h *Handler) PostMailAuthSignup(c echo.Context, params generated.PostMailAuthSignupParams) error {
email := params.Email
password := params.Password
userID := strconv.Itoa(params.UserId)
token, err := h.mailAuthUseCase.SignUp(c.Request().Context(), email, password, userID)
func (h *Handler) PostMailAuthSignup(c echo.Context) error {
var request generated.PostMailAuthSignupJSONRequestBody
if err := c.Bind(&request); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "invalid request body")
}

email := request.Email
password := request.Password
name := request.Name
bureauID := strconv.Itoa(request.BureauId)
roleID := strconv.Itoa(request.RoleId)
token, err := h.mailAuthUseCase.SignUp(c.Request().Context(), email, password, name, bureauID, roleID)
Comment thread
hikahana marked this conversation as resolved.
if err != nil {
return err
}
Expand Down
122 changes: 106 additions & 16 deletions api/externals/repository/mail_auth_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ package repository
import (
"context"
"database/sql"
"fmt"

"github.com/NUTFes/FinanSu/api/drivers/db"
"github.com/NUTFes/FinanSu/api/externals/repository/abstract"
goqu "github.com/doug-martin/goqu/v9"
"github.com/doug-martin/goqu/v9/exp"
)

type mailAuthRepository struct {
Expand All @@ -16,9 +17,12 @@ type mailAuthRepository struct {

type MailAuthRepository interface {
CreateMailAuth(context.Context, string, string, string) (int64, error)
FindMailAuthByEmail(context.Context, string) *sql.Row
FindMailAuthByID(context.Context, string) *sql.Row
CreateMailAuthWithTx(context.Context, *sql.Tx, string, string, string) (int64, error)
FindMailAuthByEmail(context.Context, string) (*sql.Row, error)
FindMailAuthByID(context.Context, string) (*sql.Row, error)
ChangePasswordByUserID(context.Context, string, string) error
InvalidateEmailByUserIDWithTx(context.Context, *sql.Tx, string) error
InvalidateEmailByUserIDsWithTx(context.Context, *sql.Tx, []int) error
}

func NewMailAuthRepository(client db.Client, crud abstract.Crud) MailAuthRepository {
Expand All @@ -27,32 +31,118 @@ func NewMailAuthRepository(client db.Client, crud abstract.Crud) MailAuthReposit

// 作成
func (r *mailAuthRepository) CreateMailAuth(c context.Context, email string, password string, userID string) (int64, error) {
result, err := r.client.DB().ExecContext(c, "insert into mail_auth (email, password, user_id) values ('"+email+"','"+password+"',"+userID+")")
query, args, err := createMailAuthQuery(email, password, userID)
if err != nil {
return 0, err
}

result, err := r.client.DB().ExecContext(c, query, args...)
if err != nil {
return 0, err
}
lastInsertID, err := result.LastInsertId()
return lastInsertID, err
}

func (r *mailAuthRepository) CreateMailAuthWithTx(c context.Context, tx *sql.Tx, email string, password string, userID string) (int64, error) {
query, args, err := createMailAuthQuery(email, password, userID)
if err != nil {
return 0, err
}

result, err := tx.ExecContext(c, query, args...)
if err != nil {
return 0, err
}
return result.LastInsertId()
}

// メールアドレスからmail_authを探してくる
func (r *mailAuthRepository) FindMailAuthByEmail(c context.Context, email string) *sql.Row {
query := "select * from mail_auth where email= '" + email + "'"
row := r.client.DB().QueryRowContext(c, query)
fmt.Printf("\x1b[36m%s\n", query)
return row
func (r *mailAuthRepository) FindMailAuthByEmail(c context.Context, email string) (*sql.Row, error) {
query, args, err := dialect.From("mail_auth").
Prepared(true).
Where(goqu.Ex{"email": email}).
ToSQL()
if err != nil {
return nil, err
}

return r.client.DB().QueryRowContext(c, query, args...), nil
}

// mail_auth_idからmail_authを探してくる
func (r *mailAuthRepository) FindMailAuthByID(c context.Context, id string) *sql.Row {
query := "select * from mail_auth where id= " + id
row := r.client.DB().QueryRowContext(c, query)
fmt.Printf("\x1b[36m%s\n", query)
return row
func (r *mailAuthRepository) FindMailAuthByID(c context.Context, id string) (*sql.Row, error) {
query, args, err := dialect.From("mail_auth").
Prepared(true).
Where(goqu.Ex{"id": id}).
ToSQL()
if err != nil {
return nil, err
}

return r.client.DB().QueryRowContext(c, query, args...), nil
}

// パスワードの変更
func (r *mailAuthRepository) ChangePasswordByUserID(c context.Context, userID string, password string) error {
query := "UPDATE mail_auth SET password = '" + password + "' WHERE user_id = " + userID
return r.crud.UpdateDB(c, query)
query, args, err := updateMailAuthQuery(
goqu.Record{"password": password},
goqu.Ex{"user_id": userID},
)
if err != nil {
return err
}

_, err = r.client.DB().ExecContext(c, query, args...)
return err
}

func (r *mailAuthRepository) InvalidateEmailByUserIDWithTx(c context.Context, tx *sql.Tx, userID string) error {
query, args, err := updateMailAuthQuery(
goqu.Record{"email": nil},
goqu.Ex{"user_id": userID},
)
if err != nil {
return err
}

_, err = tx.ExecContext(c, query, args...)
return err
}

func (r *mailAuthRepository) InvalidateEmailByUserIDsWithTx(c context.Context, tx *sql.Tx, userIDs []int) error {
if len(userIDs) == 0 {
return nil
}

query, args, err := updateMailAuthQuery(
goqu.Record{"email": nil},
goqu.I("user_id").In(userIDs),
)
if err != nil {
return err
}

_, err = tx.ExecContext(c, query, args...)
return err
}

func createMailAuthQuery(email string, password string, userID string) (string, []any, error) {
return dialect.Insert("mail_auth").
Prepared(true).
Rows(goqu.Record{
"email": email,
"password": password,
"user_id": userID,
}).
ToSQL()
}

func updateMailAuthQuery(record goqu.Record, first exp.Expression, rest ...exp.Expression) (string, []any, error) {
where := append([]exp.Expression{first}, rest...)
return dialect.Update("mail_auth").
Prepared(true).
Set(record).
Where(where...).
ToSQL()
}
Loading
Loading