From 113f405548ae077a091a2e15ab09afd2c9fb2782 Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Tue, 14 Feb 2023 12:39:13 +0900 Subject: [PATCH 01/19] =?UTF-8?q?=E2=9C=A8=20(writer/event)UpdateName?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When updating name for event, you need to update not only name field in Events but also event_name field in Orgs --- svc/pkg/domain/command/event.go | 2 +- svc/pkg/infra/writer/event.go | 45 +++++++++++++++++++++++++++------ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/svc/pkg/domain/command/event.go b/svc/pkg/domain/command/event.go index e64a06a..24b8c10 100644 --- a/svc/pkg/domain/command/event.go +++ b/svc/pkg/domain/command/event.go @@ -7,6 +7,6 @@ import ( type Event interface { Create(context.Context, event.Event) error - UpdateAll(context.Context, *event.Event) error + UpdateName(context.Context, event.Event) error Delete(context.Context, *event.Event) error } diff --git a/svc/pkg/infra/writer/event.go b/svc/pkg/infra/writer/event.go index 82dbfab..1d5ff6d 100644 --- a/svc/pkg/infra/writer/event.go +++ b/svc/pkg/infra/writer/event.go @@ -3,20 +3,26 @@ package writer import ( "cloud.google.com/go/firestore" "context" + "fmt" "ynufes-mypage-backend/svc/pkg/domain/model/event" "ynufes-mypage-backend/svc/pkg/exception" entity "ynufes-mypage-backend/svc/pkg/infra/entity/event" + orgEntity "ynufes-mypage-backend/svc/pkg/infra/entity/org" ) type ( Event struct { - collection *firestore.CollectionRef + client *firestore.Client + collectionEvent *firestore.CollectionRef + collectionOrg *firestore.CollectionRef } ) func NewEvent(c *firestore.Client) Event { return Event{ - collection: c.Collection(entity.EventCollectionName), + client: c, + collectionEvent: c.Collection(entity.EventCollectionName), + collectionOrg: c.Collection(orgEntity.OrgCollectionName), } } @@ -27,7 +33,7 @@ func (eve Event) Create(ctx context.Context, model event.Event) error { e := &entity.Event{ Name: model.Name, } - _, err := eve.collection.Doc(model.ID.ExportID()). + _, err := eve.collectionEvent.Doc(model.ID.ExportID()). Create(ctx, e) if err != nil { return err @@ -35,14 +41,37 @@ func (eve Event) Create(ctx context.Context, model event.Event) error { return nil } -func (eve Event) UpdateAll(ctx context.Context, model *event.Event) error { - _, err := eve.collection.Doc(model.ID.ExportID()). - Set(ctx, model) - return err +// UpdateName updates name of event, including event_name in Orgs and name in Events +func (eve Event) UpdateName(ctx context.Context, model event.Event) error { + err := eve.client.RunTransaction(ctx, func(ctx context.Context, tx *firestore.Transaction) error { + orgsRef := eve.collectionOrg.Where("event_id", "==", model.ID.GetValue()) + orgs, err := tx.Documents(orgsRef).GetAll() + if err != nil { + return err + } + for _, org := range orgs { + err := tx.Update(org.Ref, []firestore.Update{ + {Path: "event_name", Value: model.Name}, + }) + if err != nil { + return err + } + } + if err := tx.Update(eve.collectionEvent.Doc(model.ID.ExportID()), []firestore.Update{ + {Path: "name", Value: model.Name}, + }); err != nil { + return err + } + return nil + }) + if err != nil { + return fmt.Errorf("failed to update event: %w", err) + } + return nil } func (eve Event) Delete(ctx context.Context, model *event.Event) error { - _, err := eve.collection.Doc(model.ID.ExportID()). + _, err := eve.collectionEvent.Doc(model.ID.ExportID()). Delete(ctx) if err != nil { return err From be393db8383edf6ff1c89dc08dd837cb748c8d7a Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Tue, 14 Feb 2023 14:16:50 +0900 Subject: [PATCH 02/19] =?UTF-8?q?=E2=9C=85=20(uc/line)Http=E3=83=99?= =?UTF-8?q?=E3=83=BC=E3=82=B9=E3=83=86=E3=82=B9=E3=83=88=E3=81=AE=E3=81=9F?= =?UTF-8?q?=E3=82=81=E3=81=AE=E3=82=AF=E3=82=A8=E3=83=AA=E3=81=8B=E3=82=89?= =?UTF-8?q?=E3=81=AE=E3=83=80=E3=83=9F=E3=83=BC=E3=83=87=E3=83=BC=E3=82=BF?= =?UTF-8?q?=E5=8F=96=E5=BE=97=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- svc/pkg/uc/line/auth_line.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/svc/pkg/uc/line/auth_line.go b/svc/pkg/uc/line/auth_line.go index 660fc44..86ae6ae 100644 --- a/svc/pkg/uc/line/auth_line.go +++ b/svc/pkg/uc/line/auth_line.go @@ -3,6 +3,7 @@ package line import ( "context" "fmt" + "github.com/gin-gonic/gin" "log" "ynufes-mypage-backend/pkg/identity" linePkg "ynufes-mypage-backend/pkg/line" @@ -62,13 +63,18 @@ func (uc AuthUseCase) Do(ipt AuthInput) (*AuthOutput, error) { return nil, err } } else { - aToken = user.NewEncryptedAccessToken("testAccessToken") - rToken = user.NewEncryptedRefreshToken("testRefreshToken") + // if line auth is disabled, return dummy data + // if the request has query, it will be used. + c := ipt.Ctx.(*gin.Context) + aToken = user.NewEncryptedAccessToken( + user.PlainAccessToken(c.DefaultQuery("accessToken", "testAccessToken"))) + rToken = user.NewEncryptedRefreshToken( + user.PlainRefreshToken(c.DefaultQuery("refreshToken", "testRefreshToken"))) profile = linePkg.ProfileResponse{ - UserID: "testUserLineID", - DisplayName: "testUserDisplayName", - PictureURL: "https://testUserPicture.com", - StatusMessage: "testUserStatusMessage", + UserID: c.DefaultQuery("userID", "testUserID"), + DisplayName: c.DefaultQuery("displayName", "testDisplayName"), + PictureURL: c.DefaultQuery("pictureURL", "https://testUserPicture.com"), + StatusMessage: c.DefaultQuery("statusMessage", "testStatusMessage"), } } lineServiceID := user.LineServiceID(profile.UserID) From 66d2baf9fd167564b2493d04a3ce6f840db6ba6c Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Tue, 14 Feb 2023 17:28:19 +0900 Subject: [PATCH 03/19] =?UTF-8?q?=E2=9C=85=20(testing/op)UserCreate/UserIn?= =?UTF-8?q?fo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- svc/testing/operation/user_create.go | 48 ++++++++++++++++++++++++++ svc/testing/operation/user_info.go | 51 ++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 svc/testing/operation/user_create.go create mode 100644 svc/testing/operation/user_info.go diff --git a/svc/testing/operation/user_create.go b/svc/testing/operation/user_create.go new file mode 100644 index 0000000..9949aa4 --- /dev/null +++ b/svc/testing/operation/user_create.go @@ -0,0 +1,48 @@ +package operation + +import ( + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +// UserCreateInput fields can be empty. Default Value will be set in pkg/svc/uc/line/auth_line.go +type UserCreateInput struct { + AccessToken string + RefreshToken string + LineServiceID string + DisplayName string + PictureURL string + StatusMessage string +} + +type UserCreateOutput struct { + NewUser bool + JWT string +} + +func UserCreate(t *testing.T, r *gin.Engine, input UserCreateInput) UserCreateOutput { + w := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/api/v1/auth/line/callback", nil) + r.ServeHTTP(w, req) + assert.Equal(t, 302, w.Code) + cookie := w.Header().Get("Set-Cookie") + cookie = cookie[:strings.Index(cookie, ";")] + assert.Equal(t, "Authorization=", cookie[:14]) + var newUser bool + switch w.Header().Get("Location") { + case "/welcome": + newUser = true + case "/": + newUser = false + default: + t.Fatal("Unexpected Location Header") + } + return UserCreateOutput{ + NewUser: newUser, + JWT: cookie[14:], + } +} diff --git a/svc/testing/operation/user_info.go b/svc/testing/operation/user_info.go new file mode 100644 index 0000000..95a65e7 --- /dev/null +++ b/svc/testing/operation/user_info.go @@ -0,0 +1,51 @@ +package operation + +import ( + "encoding/json" + "fmt" + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" + "net/http" + "net/http/httptest" + "testing" + "ynufes-mypage-backend/svc/pkg/schema/user" +) + +type UserInfoInput struct { + Authorization string +} + +type UserInfoOutput struct { + Code int + Response *user.InfoResponse + ErrResponse string +} + +func UserInfo(t *testing.T, r *gin.Engine, input UserInfoInput) UserInfoOutput { + w := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/api/v1/user", nil) + req.Header.Set("Authorization", fmt.Sprintf("Bearer: %s", input.Authorization)) + r.ServeHTTP(w, req) + if w.Code != 200 { + return UserInfoOutput{ + Code: w.Code, + Response: nil, + ErrResponse: w.Body.String(), + } + } + var info user.InfoResponse + err := json.Unmarshal(w.Body.Bytes(), &info) + assert.NoError(t, err) + return UserInfoOutput{ + Code: w.Code, + Response: &info, + } +} + +func (o UserInfoOutput) StrictValidate(t *testing.T, suppose user.InfoResponse) { + assert.Equal(t, suppose.NameFirst, o.Response.NameFirst) + assert.Equal(t, suppose.NameLast, o.Response.NameLast) + assert.Equal(t, suppose.Type, o.Response.Type) + assert.Equal(t, suppose.ProfileImageURL, o.Response.ProfileImageURL) + assert.Equal(t, suppose.Status, o.Response.Status) +} From 02e8842f47e5ea0833b854485e101dea88ab3319 Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Tue, 14 Feb 2023 17:57:11 +0900 Subject: [PATCH 04/19] =?UTF-8?q?=E2=9C=85=20(testing/sce)TestUserCreation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- svc/testing/scenario/user_creation.go | 103 ++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 svc/testing/scenario/user_creation.go diff --git a/svc/testing/scenario/user_creation.go b/svc/testing/scenario/user_creation.go new file mode 100644 index 0000000..49e304d --- /dev/null +++ b/svc/testing/scenario/user_creation.go @@ -0,0 +1,103 @@ +package scenario + +import ( + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" + "testing" + userModel "ynufes-mypage-backend/svc/pkg/domain/model/user" + "ynufes-mypage-backend/svc/pkg/schema/user" + "ynufes-mypage-backend/svc/testing/operation" +) + +// TestUserCreation simulates following steps +// 1. Create a new user +// 2. Get user info +// 3. Create a new user with same LineServiceID +// 4. Get user info +// 5. Create a new user with different LineServiceID +// 6. Get user info +func TestUserCreation(t *testing.T, r *gin.Engine) { + // 1. Create a new user + ipt1 := operation.UserCreateInput{ + AccessToken: "accessToken1", + RefreshToken: "refreshToken1", + LineServiceID: "TestLineServiceID1", + DisplayName: "displayName1", + PictureURL: "https://test.com/hehe.jpg", + StatusMessage: "StatusMessage1", + } + out1 := operation.UserCreate(t, r, ipt1) + assert.Equal(t, true, out1.NewUser) + + // 2. Get user info + infoOut1 := operation.UserInfo(t, r, operation.UserInfoInput{ + Authorization: out1.JWT, + }) + if !assert.Equal(t, 200, infoOut1.Code) { + t.Errorf("TestUserCreation Failed: IncorrectCode (%d ,%d)", 200, infoOut1.Code) + return + } + infoOut1.StrictValidate(t, user.InfoResponse{ + NameFirst: "", + NameLast: "", + Type: int(userModel.StatusRegistered), + ProfileImageURL: ipt1.PictureURL, + Status: int(userModel.StatusNew), + }) + + // 3. Create a new user with same LineServiceID + ipt2 := operation.UserCreateInput{ + AccessToken: "accessToken2", + RefreshToken: "refreshToken2", + LineServiceID: ipt1.LineServiceID, + DisplayName: "displayName2", + PictureURL: "https://test.com/hehe2.jpg", + StatusMessage: "StatusMessage2", + } + out2 := operation.UserCreate(t, r, ipt2) + assert.Equal(t, false, out2.NewUser) + + // 4. Get user info + infoOut2 := operation.UserInfo(t, r, operation.UserInfoInput{ + Authorization: out2.JWT, + }) + if !assert.Equal(t, 200, infoOut2.Code) { + t.Errorf("TestUserCreation Failed: IncorrectCode (%d ,%d)", 200, infoOut2.Code) + return + } + infoOut2.StrictValidate(t, user.InfoResponse{ + NameFirst: "", + NameLast: "", + Type: int(userModel.StatusRegistered), + ProfileImageURL: ipt2.PictureURL, + Status: int(userModel.StatusNew), + }) + + // 5. Create a new user with different LineServiceID + ipt3 := operation.UserCreateInput{ + AccessToken: "accessToken3", + RefreshToken: "refreshToken3", + LineServiceID: "TestLineServiceID3", + DisplayName: "displayName3", + PictureURL: "https://test.com/hehe3.jpg", + StatusMessage: "StatusMessage3", + } + out3 := operation.UserCreate(t, r, ipt3) + assert.Equal(t, true, out3.NewUser) + + // 6. Get user info + infoOut3 := operation.UserInfo(t, r, operation.UserInfoInput{ + Authorization: out3.JWT, + }) + if !assert.Equal(t, 200, infoOut3.Code) { + t.Errorf("TestUserCreation Failed: IncorrectCode (%d ,%d)", 200, infoOut3.Code) + return + } + infoOut3.StrictValidate(t, user.InfoResponse{ + NameFirst: "", + NameLast: "", + Type: int(userModel.StatusRegistered), + ProfileImageURL: ipt3.PictureURL, + Status: int(userModel.StatusNew), + }) +} From c88ebfdaaad6cfdf417125337d5ff3230fef0b36 Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Wed, 15 Feb 2023 20:45:56 +0900 Subject: [PATCH 05/19] =?UTF-8?q?=E2=9C=85=20(testing/sce)UserInfoUpdate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- svc/testing/operation/user_info_update.go | 35 +++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 svc/testing/operation/user_info_update.go diff --git a/svc/testing/operation/user_info_update.go b/svc/testing/operation/user_info_update.go new file mode 100644 index 0000000..eab8e51 --- /dev/null +++ b/svc/testing/operation/user_info_update.go @@ -0,0 +1,35 @@ +package operation + +import ( + "fmt" + "github.com/gin-gonic/gin" + "net/http" + "net/http/httptest" + "testing" +) + +type UserInfoUpdateInput struct { + Authorization string +} + +type UserInfoUpdateOutput struct { + Code int + ErrResponse string +} + +func UserInfoUpdate(t *testing.T, r *gin.Engine, input UserInfoUpdateInput) UserInfoUpdateOutput { + w := httptest.NewRecorder() + + req, _ := http.NewRequest("POST", "/api/v1/user", nil) + req.Header.Set("Authorization", fmt.Sprintf("Bearer: %s", input.Authorization)) + r.ServeHTTP(w, req) + if w.Code != 200 { + return UserInfoUpdateOutput{ + Code: w.Code, + ErrResponse: w.Body.String(), + } + } + return UserInfoUpdateOutput{ + Code: w.Code, + } +} From bbe5d9755e90648b273b88fec4e82042c00acfba Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Wed, 15 Feb 2023 23:12:02 +0900 Subject: [PATCH 06/19] =?UTF-8?q?=F0=9F=94=A7=20(CI)GitHubActionsCI?= =?UTF-8?q?=E3=81=AEON=E6=9D=A1=E4=BB=B6=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/go test.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/go test.yml b/.github/workflows/go test.yml index e93bad0..e5c2160 100644 --- a/.github/workflows/go test.yml +++ b/.github/workflows/go test.yml @@ -1,5 +1,12 @@ name: go test with firestore emulator -on: [ push ] +on: + push: + branches: + - main + - develop + pull_request: + paths: + - '**.go' jobs: go-test-with-firestore-emulator: runs-on: ubuntu-latest From f56effd26a2c49a56876f253c73819580a5e6a13 Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Thu, 16 Feb 2023 05:50:46 +0900 Subject: [PATCH 07/19] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Member=20->=20User,?= =?UTF-8?q?=20#21=E3=81=AB=E5=9F=BA=E3=81=A5=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes: #21 --- svc/pkg/domain/command/org.go | 2 +- svc/pkg/domain/model/org/aggregate.go | 10 ++++----- svc/pkg/infra/entity/org/aggregate.go | 14 ++++++------- svc/pkg/infra/reader/org.go | 2 +- svc/pkg/infra/writer/org.go | 30 +++++++++++++-------------- svc/pkg/uc/org/create.go | 10 ++++----- svc/pkg/uc/org/register.go | 12 +++++------ 7 files changed, 40 insertions(+), 40 deletions(-) diff --git a/svc/pkg/domain/command/org.go b/svc/pkg/domain/command/org.go index 2b31a56..5834dd2 100644 --- a/svc/pkg/domain/command/org.go +++ b/svc/pkg/domain/command/org.go @@ -8,6 +8,6 @@ import ( type Org interface { Create(context.Context, org.Org) error Set(context.Context, org.Org) error - UpdateMembers(context.Context, org.Org) error + UpdateUsers(context.Context, org.Org) error UpdateIsOpen(context.Context, org.Org) error } diff --git a/svc/pkg/domain/model/org/aggregate.go b/svc/pkg/domain/model/org/aggregate.go index b279a74..b241b90 100644 --- a/svc/pkg/domain/model/org/aggregate.go +++ b/svc/pkg/domain/model/org/aggregate.go @@ -8,11 +8,11 @@ import ( type ( Org struct { - ID ID - Event event.Event - Name string - Members []user.ID - IsOpen bool + ID ID + Event event.Event + Name string + Users []user.ID + IsOpen bool } ID util.ID ) diff --git a/svc/pkg/infra/entity/org/aggregate.go b/svc/pkg/infra/entity/org/aggregate.go index 24220b0..9977e19 100644 --- a/svc/pkg/infra/entity/org/aggregate.go +++ b/svc/pkg/infra/entity/org/aggregate.go @@ -14,14 +14,14 @@ type Org struct { EventID int64 `firestore:"event_id"` EventName string `firestore:"event_name"` Name string `firestore:"name"` - Members []int64 `firestore:"member_ids"` + Users []int64 `firestore:"user_ids"` IsOpen bool `firestore:"is_open"` } func (o Org) ToModel() (*org.Org, error) { - var members []user.ID - for i := range o.Members { - members = append(members, identity.NewID(o.Members[i])) + var users []user.ID + for i := range o.Users { + users = append(users, identity.NewID(o.Users[i])) } id, err := identity.ImportID(o.ID) if err != nil { @@ -33,8 +33,8 @@ func (o Org) ToModel() (*org.Org, error) { ID: identity.NewID(o.EventID), Name: o.EventName, }, - Name: o.Name, - Members: members, - IsOpen: o.IsOpen, + Name: o.Name, + Users: users, + IsOpen: o.IsOpen, }, nil } diff --git a/svc/pkg/infra/reader/org.go b/svc/pkg/infra/reader/org.go index 152bb31..26edc1b 100644 --- a/svc/pkg/infra/reader/org.go +++ b/svc/pkg/infra/reader/org.go @@ -37,7 +37,7 @@ func (o Org) GetByID(ctx context.Context, id org.ID) (*org.Org, error) { func (o Org) ListByGrantedUserID(ctx context.Context, id user.ID) ([]org.Org, error) { var orgs []org.Org uid := id.GetValue() - iter := o.collection.Where("member_ids", "array-contains", uid).Documents(ctx) + iter := o.collection.Where("user_ids", "array-contains", uid).Documents(ctx) for { var orgEntity entity.Org snap, err := iter.Next() diff --git a/svc/pkg/infra/writer/org.go b/svc/pkg/infra/writer/org.go index 867f949..65052fb 100644 --- a/svc/pkg/infra/writer/org.go +++ b/svc/pkg/infra/writer/org.go @@ -24,15 +24,15 @@ func (o Org) Create(ctx context.Context, org org.Org) error { if !org.ID.HasValue() { return exception.ErrIDNotAssigned } - memberE := make([]int64, len(org.Members)) - for i := range org.Members { - memberE[i] = org.Members[i].GetValue() + usersE := make([]int64, len(org.Users)) + for i := range org.Users { + usersE[i] = org.Users[i].GetValue() } e := entity.Org{ EventID: org.Event.ID.GetValue(), EventName: org.Event.Name, Name: org.Name, - Members: memberE, + Users: usersE, IsOpen: org.IsOpen, } if _, err := o.collection.Doc(org.ID.ExportID()).Create(ctx, e); err != nil { @@ -43,16 +43,16 @@ func (o Org) Create(ctx context.Context, org org.Org) error { } func (o Org) Set(ctx context.Context, org org.Org) error { - memberE := make([]int64, len(org.Members)) + usersE := make([]int64, len(org.Users)) e := entity.Org{ EventID: org.Event.ID.GetValue(), EventName: org.Event.Name, Name: org.Name, - Members: memberE, + Users: usersE, IsOpen: org.IsOpen, } - for i := range org.Members { - memberE[i] = org.Members[i].GetValue() + for i := range org.Users { + usersE[i] = org.Users[i].GetValue() } _, err := o.collection.Doc(org.ID.ExportID()).Set(ctx, e) if err != nil { @@ -62,18 +62,18 @@ func (o Org) Set(ctx context.Context, org org.Org) error { return nil } -func (o Org) UpdateMembers(ctx context.Context, org org.Org) error { - memberE := make([]int64, len(org.Members)) - for i := range org.Members { - memberE[i] = org.Members[i].GetValue() +func (o Org) UpdateUsers(ctx context.Context, org org.Org) error { + usersE := make([]int64, len(org.Users)) + for i := range org.Users { + usersE[i] = org.Users[i].GetValue() } _, err := o.collection.Doc(org.ID.ExportID()).Update(ctx, []firestore.Update{ - {Path: "member_ids", Value: memberE}, + {Path: "user_ids", Value: usersE}, }) if err != nil { - log.Printf("Failed to update org members: %v", err) - return fmt.Errorf("failed to update org members: %w", err) + log.Printf("Failed to update org users: %v", err) + return fmt.Errorf("failed to update org users: %w", err) } return nil } diff --git a/svc/pkg/uc/org/create.go b/svc/pkg/uc/org/create.go index 6da4e33..4813b17 100644 --- a/svc/pkg/uc/org/create.go +++ b/svc/pkg/uc/org/create.go @@ -39,11 +39,11 @@ func (uc CreateOrgUseCase) Do(ipt CreateOrgInput) (*CreateOrgOutput, error) { return nil, err } o := org.Org{ - ID: org.ID(identity.IssueID()), - Event: *e, - Name: ipt.OrgName, - Members: nil, - IsOpen: ipt.IsOpen, + ID: org.ID(identity.IssueID()), + Event: *e, + Name: ipt.OrgName, + Users: nil, + IsOpen: ipt.IsOpen, } err = uc.orgC.Create(ipt.Ctx, o) if err != nil { diff --git a/svc/pkg/uc/org/register.go b/svc/pkg/uc/org/register.go index 3bc4762..53ae690 100644 --- a/svc/pkg/uc/org/register.go +++ b/svc/pkg/uc/org/register.go @@ -52,14 +52,14 @@ func (uc RegisterUseCase) Do(ipt RegisterInput) (*RegisterOutput, error) { if err != nil { return nil, fmt.Errorf("failed to get org in RegisterUC: %w", err) } - if hasMember(&o.Members, ipt.UserID) { + if hasUser(&o.Users, ipt.UserID) { return &RegisterOutput{ Added: false, Org: *o, }, nil } - o.Members = append(o.Members, ipt.UserID) - if err := uc.orgC.UpdateMembers(ipt.Ctx, *o); err != nil { + o.Users = append(o.Users, ipt.UserID) + if err := uc.orgC.UpdateUsers(ipt.Ctx, *o); err != nil { return nil, err } return &RegisterOutput{ @@ -68,9 +68,9 @@ func (uc RegisterUseCase) Do(ipt RegisterInput) (*RegisterOutput, error) { }, nil } -func hasMember(members *[]user.ID, member user.ID) bool { - for _, m := range *members { - if m.GetValue() == member.GetValue() { +func hasUser(users *[]user.ID, targetUser user.ID) bool { + for _, m := range *users { + if m.GetValue() == targetUser.GetValue() { return true } } From f8319efc3cbb12a47e93f6e93a9eb6e1566fd43b Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Thu, 16 Feb 2023 06:26:35 +0900 Subject: [PATCH 08/19] =?UTF-8?q?=F0=9F=8E=A8=20(settings)Application>Admi?= =?UTF-8?q?n=20->=20Service>Authentication?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/setting/application.go | 7 ++----- pkg/setting/files/setting.template.yaml | 3 +-- pkg/setting/files/setting.testing.yaml | 3 +-- pkg/setting/service.go | 3 ++- svc/pkg/config/jwt.go | 2 +- svc/pkg/handler/admin/org.go | 2 +- svc/pkg/uc/org/register.go | 2 +- svc/pkg/uc/user/login.go | 2 +- 8 files changed, 10 insertions(+), 14 deletions(-) diff --git a/pkg/setting/application.go b/pkg/setting/application.go index 010ea01..f9c2c47 100644 --- a/pkg/setting/application.go +++ b/pkg/setting/application.go @@ -2,17 +2,14 @@ package setting type ( Application struct { - Server Server `yaml:"server"` - Admin Admin `yaml:"admin"` + Server Server `yaml:"server"` + Authentication Authentication `yaml:"authentication"` } Server struct { OnProduction bool `yaml:"on_production"` Frontend Frontend `yaml:"frontend"` Backend Backend `yaml:"backend"` } - Admin struct { - JwtSecret string `yaml:"jwt_secret"` - } Frontend struct { Protocol string `yaml:"protocol"` Domain string `yaml:"domain"` diff --git a/pkg/setting/files/setting.template.yaml b/pkg/setting/files/setting.template.yaml index f76d4c9..3a93575 100644 --- a/pkg/setting/files/setting.template.yaml +++ b/pkg/setting/files/setting.template.yaml @@ -11,8 +11,6 @@ application: domain: "ynufes-mypage.shion.pro" port: ":1306" # CONCATING PROTOCOL, DOMAIN, AND PORT TOGETHER SHOULD GIVE THE FULL URL - admin: - jwt_secret: "ADMIN_AUTH_CIPHER_KEY_TEST_TOKEN" infrastructure: firestore: project_id: "ynufes-mypage" @@ -27,5 +25,6 @@ third_party: enable_line_auth: true service: authentication: + jwt_secret: "LEN32_AUTH_CIPHER_KEY_TEST_TOKEN" secure_cookie: true # YOU CAN DISABLE SECURE COOKIE FOR TESTING PURPOSES \ No newline at end of file diff --git a/pkg/setting/files/setting.testing.yaml b/pkg/setting/files/setting.testing.yaml index 57980cc..aae29e8 100644 --- a/pkg/setting/files/setting.testing.yaml +++ b/pkg/setting/files/setting.testing.yaml @@ -12,8 +12,6 @@ application: domain: "ynufes-mypage.shion.pro" port: "" # CONCATING PROTOCOL, DOMAIN, AND PORT TOGETHER SHOULD GIVE THE FULL URL - admin: - jwt_secret: "ADMIN_AUTH_CIPHER_KEY_TEST_TOKEN" infrastructure: firestore: project_id: "ynufes-mypage" @@ -29,5 +27,6 @@ third_party: # DISABLE LINE AUTH ON TESTING service: authentication: + jwt_secret: "LEN32_AUTH_CIPHER_KEY_TEST_TOKEN" secure_cookie: false # DISABLE SECURE COOKIE ON TESTING \ No newline at end of file diff --git a/pkg/setting/service.go b/pkg/setting/service.go index a7cf33f..fc39024 100644 --- a/pkg/setting/service.go +++ b/pkg/setting/service.go @@ -5,6 +5,7 @@ type ( Authentication Authentication `yaml:"authentication"` } Authentication struct { - SecureCookie bool `yaml:"secure_cookie"` + SecureCookie bool `yaml:"secure_cookie"` + JwtSecret string `yaml:"jwt_secret"` } ) diff --git a/svc/pkg/config/jwt.go b/svc/pkg/config/jwt.go index d8c153c..9c9a668 100644 --- a/svc/pkg/config/jwt.go +++ b/svc/pkg/config/jwt.go @@ -6,7 +6,7 @@ var JWT jwt func init() { config := setting.Get() - JWT = jwt{JWTSecret: config.Application.Admin.JwtSecret} + JWT = jwt{JWTSecret: config.Application.Authentication.JwtSecret} } type jwt struct { diff --git a/svc/pkg/handler/admin/org.go b/svc/pkg/handler/admin/org.go index 7e899b0..52d9979 100644 --- a/svc/pkg/handler/admin/org.go +++ b/svc/pkg/handler/admin/org.go @@ -23,7 +23,7 @@ func NewOrg(rgst registry.Registry) *Org { return &Org{ createOrgUC: org.NewCreateOrg(rgst), infoOrgUC: org.NewInfo(rgst), - jwtSecret: config.Application.Admin.JwtSecret, + jwtSecret: config.Application.Authentication.JwtSecret, } } diff --git a/svc/pkg/uc/org/register.go b/svc/pkg/uc/org/register.go index 53ae690..fd7e340 100644 --- a/svc/pkg/uc/org/register.go +++ b/svc/pkg/uc/org/register.go @@ -35,7 +35,7 @@ func NewRegister(rgst registry.Registry) RegisterUseCase { return RegisterUseCase{ orgC: rgst.Repository().NewOrgCommand(), orgQ: rgst.Repository().NewOrgQuery(), - jwtSecret: config.Application.Admin.JwtSecret, + jwtSecret: config.Application.Authentication.JwtSecret, } } diff --git a/svc/pkg/uc/user/login.go b/svc/pkg/uc/user/login.go index a2f37b4..5e48576 100644 --- a/svc/pkg/uc/user/login.go +++ b/svc/pkg/uc/user/login.go @@ -27,7 +27,7 @@ func NewLoginUseCase(registry registry.Registry) LoginUseCase { config := setting.Get() return LoginUseCase{ userQuery: registry.Repository().NewUserQuery(), - jwtSecret: config.Application.Admin.JwtSecret, + jwtSecret: config.Application.Authentication.JwtSecret, } } From abe23d002beba77cf6f1c528c881dfa3fdf24f8f Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Thu, 16 Feb 2023 06:33:46 +0900 Subject: [PATCH 09/19] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Admin=20->=20Agent,?= =?UTF-8?q?=20according=20to=20#21?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- svc/cmd/dev/main.go | 2 +- svc/cmd/release/main.go | 2 +- svc/pkg/handler/{admin => agent}/event.go | 6 +++--- svc/pkg/handler/{admin => agent}/org.go | 8 ++++---- svc/pkg/schema/{admin => agent}/event.go | 2 +- svc/pkg/schema/{admin => agent}/org.go | 2 +- svc/runner/server.go | 20 ++++++++++---------- svc/runner/server_test.go | 2 +- 8 files changed, 22 insertions(+), 22 deletions(-) rename svc/pkg/handler/{admin => agent}/event.go (88%) rename svc/pkg/handler/{admin => agent}/org.go (94%) rename svc/pkg/schema/{admin => agent}/event.go (88%) rename svc/pkg/schema/{admin => agent}/org.go (96%) diff --git a/svc/cmd/dev/main.go b/svc/cmd/dev/main.go index f43bce3..b0b684e 100644 --- a/svc/cmd/dev/main.go +++ b/svc/cmd/dev/main.go @@ -14,7 +14,7 @@ func main() { log.Fatalf("Failed to start server... %v", err) return } - if err := runner.ImplementAdmin(apiV1); err != nil { + if err := runner.ImplementAgent(apiV1); err != nil { log.Fatalf("Failed to start server... %v", err) return } diff --git a/svc/cmd/release/main.go b/svc/cmd/release/main.go index cd7b5f6..5ee3943 100644 --- a/svc/cmd/release/main.go +++ b/svc/cmd/release/main.go @@ -14,7 +14,7 @@ func main() { log.Fatalf("Failed to start server... %v", err) return } - if err := runner.ImplementAdmin(apiV1); err != nil { + if err := runner.ImplementAgent(apiV1); err != nil { log.Fatalf("Failed to start server... %v", err) return } diff --git a/svc/pkg/handler/admin/event.go b/svc/pkg/handler/agent/event.go similarity index 88% rename from svc/pkg/handler/admin/event.go rename to svc/pkg/handler/agent/event.go index 681be36..c0db0fb 100644 --- a/svc/pkg/handler/admin/event.go +++ b/svc/pkg/handler/agent/event.go @@ -1,9 +1,9 @@ -package admin +package agent import ( "github.com/gin-gonic/gin" "ynufes-mypage-backend/svc/pkg/registry" - "ynufes-mypage-backend/svc/pkg/schema/admin" + "ynufes-mypage-backend/svc/pkg/schema/agent" "ynufes-mypage-backend/svc/pkg/uc/event" ) @@ -33,7 +33,7 @@ func (uc Event) CreateHandler() gin.HandlerFunc { if err != nil { return } - resp := admin.CreateEventResponse{ + resp := agent.CreateEventResponse{ EventID: opt.Event.ID.ExportID(), EventName: opt.Event.Name, } diff --git a/svc/pkg/handler/admin/org.go b/svc/pkg/handler/agent/org.go similarity index 94% rename from svc/pkg/handler/admin/org.go rename to svc/pkg/handler/agent/org.go index 52d9979..38abe38 100644 --- a/svc/pkg/handler/admin/org.go +++ b/svc/pkg/handler/agent/org.go @@ -1,4 +1,4 @@ -package admin +package agent import ( "github.com/gin-gonic/gin" @@ -8,7 +8,7 @@ import ( "ynufes-mypage-backend/pkg/jwt" "ynufes-mypage-backend/pkg/setting" "ynufes-mypage-backend/svc/pkg/registry" - "ynufes-mypage-backend/svc/pkg/schema/admin" + "ynufes-mypage-backend/svc/pkg/schema/agent" "ynufes-mypage-backend/svc/pkg/uc/org" ) @@ -52,7 +52,7 @@ func (o Org) CreateHandler() gin.HandlerFunc { c.AbortWithStatusJSON(500, gin.H{"error": "failed to create org"}) return } - resp := admin.CreateOrgResponse{ + resp := agent.CreateOrgResponse{ EventID: opt.Org.Event.ID.ExportID(), EventName: opt.Org.Event.Name, OrgID: opt.Org.ID.ExportID(), @@ -91,7 +91,7 @@ func (o Org) IssueOrgInviteToken() gin.HandlerFunc { if err != nil { return } - resp := admin.IssueOrgInviteTokenResponse{ + resp := agent.IssueOrgInviteTokenResponse{ Token: issueJWT, OrgID: opt.Org.ID.ExportID(), ValidUntil: time.Now().Add(duration).Format(time.RFC3339), diff --git a/svc/pkg/schema/admin/event.go b/svc/pkg/schema/agent/event.go similarity index 88% rename from svc/pkg/schema/admin/event.go rename to svc/pkg/schema/agent/event.go index 7c01e07..c0eb403 100644 --- a/svc/pkg/schema/admin/event.go +++ b/svc/pkg/schema/agent/event.go @@ -1,4 +1,4 @@ -package admin +package agent type CreateEventResponse struct { EventID string `json:"event_id"` diff --git a/svc/pkg/schema/admin/org.go b/svc/pkg/schema/agent/org.go similarity index 96% rename from svc/pkg/schema/admin/org.go rename to svc/pkg/schema/agent/org.go index 6594f93..3676c8d 100644 --- a/svc/pkg/schema/admin/org.go +++ b/svc/pkg/schema/agent/org.go @@ -1,4 +1,4 @@ -package admin +package agent type CreateOrgResponse struct { EventID string `json:"event_id"` diff --git a/svc/runner/server.go b/svc/runner/server.go index 45cebed..3730712 100644 --- a/svc/runner/server.go +++ b/svc/runner/server.go @@ -2,7 +2,7 @@ package runner import ( "github.com/gin-gonic/gin" - adminOrg "ynufes-mypage-backend/svc/pkg/handler/admin" + agentOrg "ynufes-mypage-backend/svc/pkg/handler/agent" "ynufes-mypage-backend/svc/pkg/handler/line" orgHandler "ynufes-mypage-backend/svc/pkg/handler/org" userHandler "ynufes-mypage-backend/svc/pkg/handler/user" @@ -43,19 +43,19 @@ func Implement(rg *gin.RouterGroup, devTool bool) error { return nil } -func ImplementAdmin(rg *gin.RouterGroup) error { - // TODO: Implement admin Auth middleware +func ImplementAgent(rg *gin.RouterGroup) error { + // TODO: Implement agent Auth middleware rgst, err := registry.New() if err != nil { return err } //middlewareAuth := middleware.NewAuth(*rgst) - //middlewareAdmin := middleware.NewAdmin(*rgst) - //adminRg := rg.Use(middlewareAuth.VerifyUser(), middlewareAdmin.VerifyAdmin()) - event := adminOrg.NewEvent(*rgst) - org := adminOrg.NewOrg(*rgst) - rg.Handle("GET", "/admin/event/create", event.CreateHandler()) - rg.Handle("GET", "/admin/org/create", org.CreateHandler()) - rg.Handle("GET", "/admin/org/token", org.IssueOrgInviteToken()) + //middlewareAgent := middleware.NewAgent(*rgst) + //agentRg := rg.Use(middlewareAuth.VerifyUser(), middlewareAgent.VerifyAgent()) + event := agentOrg.NewEvent(*rgst) + org := agentOrg.NewOrg(*rgst) + rg.Handle("GET", "/agent/event/create", event.CreateHandler()) + rg.Handle("GET", "/agent/org/create", org.CreateHandler()) + rg.Handle("GET", "/agent/org/token", org.IssueOrgInviteToken()) return nil } diff --git a/svc/runner/server_test.go b/svc/runner/server_test.go index 3a431df..15a7a4c 100644 --- a/svc/runner/server_test.go +++ b/svc/runner/server_test.go @@ -15,7 +15,7 @@ func TestImplement(t *testing.T) { rg := r.Group("/api/v1") err := Implement(rg, true) assert.NoError(t, err) - err = ImplementAdmin(rg) + err = ImplementAgent(rg) assert.NoError(t, err) req, _ := http.NewRequest("GET", "/api/v1/auth/line/state", nil) From d5e097d048ccbc67ae7bf7298df3ab00e060a3cf Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Thu, 16 Feb 2023 06:36:54 +0900 Subject: [PATCH 10/19] =?UTF-8?q?=F0=9F=94=A8=20reload.ps1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- reload.ps1 | 1 + 1 file changed, 1 insertion(+) create mode 100644 reload.ps1 diff --git a/reload.ps1 b/reload.ps1 new file mode 100644 index 0000000..f12df4f --- /dev/null +++ b/reload.ps1 @@ -0,0 +1 @@ +go build -o ynufes-mypage-backend.exe ./svc/cmd/dev/main.go From 36305feca8c3cf80adfa4d4f7159e5171372febb Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Thu, 16 Feb 2023 06:38:09 +0900 Subject: [PATCH 11/19] =?UTF-8?q?=F0=9F=99=88=20DevYAML=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=A4=E3=83=AB,exe=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB,JSON?= =?UTF-8?q?=E8=AA=8D=E8=A8=BC=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=81=AE?= =?UTF-8?q?=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3bf780b..7fa0050 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ .idea -.env \ No newline at end of file +.env +setting.dev.yaml +gcloud-credential.json +*.exe \ No newline at end of file From 0baa544e11fb1859d4fb0922cd98741fbbdc3405 Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Fri, 17 Feb 2023 04:12:53 +0900 Subject: [PATCH 12/19] =?UTF-8?q?=E2=9C=A8=20(model)UserModel=E3=81=ABAdmi?= =?UTF-8?q?n,Agent=E3=81=AE=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- svc/pkg/domain/model/user/admin.go | 10 ++++++++++ svc/pkg/domain/model/user/agent.go | 25 +++++++++++++++++++++++++ svc/pkg/domain/model/user/aggregate.go | 2 ++ 3 files changed, 37 insertions(+) create mode 100644 svc/pkg/domain/model/user/admin.go create mode 100644 svc/pkg/domain/model/user/agent.go diff --git a/svc/pkg/domain/model/user/admin.go b/svc/pkg/domain/model/user/admin.go new file mode 100644 index 0000000..6c653e0 --- /dev/null +++ b/svc/pkg/domain/model/user/admin.go @@ -0,0 +1,10 @@ +package user + +import "time" + +type ( + Admin struct { + IsSuperAdmin bool + GrantedTime time.Time + } +) diff --git a/svc/pkg/domain/model/user/agent.go b/svc/pkg/domain/model/user/agent.go new file mode 100644 index 0000000..976142c --- /dev/null +++ b/svc/pkg/domain/model/user/agent.go @@ -0,0 +1,25 @@ +package user + +import ( + "time" + "ynufes-mypage-backend/svc/pkg/domain/model/util" +) + +type ( + Agent struct { + Roles []Role + } + Role struct { + ID RoleID + Level RoleLevel + GrantedTime time.Time + } + RoleID util.ID + RoleLevel int +) + +const ( + Viewer RoleLevel = 1 + Editor = 2 + Manager = 3 +) diff --git a/svc/pkg/domain/model/user/aggregate.go b/svc/pkg/domain/model/user/aggregate.go index c5ca7b4..43675e8 100644 --- a/svc/pkg/domain/model/user/aggregate.go +++ b/svc/pkg/domain/model/user/aggregate.go @@ -8,6 +8,8 @@ type ( Status Status Detail Detail Line Line + Admin Admin + Agent Agent } ID util.ID Status int From 113fe6ab4eea2bccab8daf02373462f7ccc296c0 Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Fri, 17 Feb 2023 09:37:29 +0900 Subject: [PATCH 13/19] =?UTF-8?q?=E2=9C=A8=20(model)NewRoleLevel=E3=83=A1?= =?UTF-8?q?=E3=82=BE=E3=83=83=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- svc/pkg/domain/model/user/agent.go | 18 ++++++++++++++++-- svc/pkg/exception/error.go | 7 ++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/svc/pkg/domain/model/user/agent.go b/svc/pkg/domain/model/user/agent.go index 976142c..47405f4 100644 --- a/svc/pkg/domain/model/user/agent.go +++ b/svc/pkg/domain/model/user/agent.go @@ -3,6 +3,7 @@ package user import ( "time" "ynufes-mypage-backend/svc/pkg/domain/model/util" + "ynufes-mypage-backend/svc/pkg/exception" ) type ( @@ -20,6 +21,19 @@ type ( const ( Viewer RoleLevel = 1 - Editor = 2 - Manager = 3 + Editor RoleLevel = 2 + Manager RoleLevel = 3 ) + +func NewRoleLevel(level int) (RoleLevel, error) { + switch RoleLevel(level) { + case Viewer: + return Viewer, nil + case Editor: + return Editor, nil + case Manager: + return Manager, nil + default: + return 0, exception.ErrInvalidRoleLevel + } +} diff --git a/svc/pkg/exception/error.go b/svc/pkg/exception/error.go index ea706c1..733785f 100644 --- a/svc/pkg/exception/error.go +++ b/svc/pkg/exception/error.go @@ -3,7 +3,8 @@ package exception import "errors" var ( - ErrorInvalidHeader = errors.New("INVALID Authorization Header") - ErrInvalidJWT = errors.New("INVALID JWT") - ErrIDNotAssigned = errors.New("ID NOT ASSIGNED") + ErrorInvalidHeader = errors.New("INVALID Authorization Header") + ErrInvalidJWT = errors.New("INVALID JWT") + ErrIDNotAssigned = errors.New("ID NOT ASSIGNED") + ErrInvalidRoleLevel = errors.New("INVALID ROLE LEVEL") ) From 1362be048a627f4f7135ae3f91e2dc5f03634862 Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Fri, 17 Feb 2023 09:38:18 +0900 Subject: [PATCH 14/19] =?UTF-8?q?=E2=9C=A8=20(entity)Admin,Agent=E3=83=87?= =?UTF-8?q?=E3=83=BC=E3=82=BF=E3=81=AE=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- svc/pkg/infra/entity/user/admin.go | 8 ++++++++ svc/pkg/infra/entity/user/agent.go | 12 ++++++++++++ svc/pkg/infra/entity/user/aggregate.go | 23 +++++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 svc/pkg/infra/entity/user/admin.go create mode 100644 svc/pkg/infra/entity/user/agent.go diff --git a/svc/pkg/infra/entity/user/admin.go b/svc/pkg/infra/entity/user/admin.go new file mode 100644 index 0000000..fdca8f4 --- /dev/null +++ b/svc/pkg/infra/entity/user/admin.go @@ -0,0 +1,8 @@ +package entity + +type ( + Admin struct { + IsSuperAdmin bool `firestore:"admin-super_admin"` + GrantedTime int64 `firestore:"admin-granted_time"` + } +) diff --git a/svc/pkg/infra/entity/user/agent.go b/svc/pkg/infra/entity/user/agent.go new file mode 100644 index 0000000..4bc0cae --- /dev/null +++ b/svc/pkg/infra/entity/user/agent.go @@ -0,0 +1,12 @@ +package entity + +type ( + Agent struct { + Roles []Role `firestore:"agent-roles"` + } + Role struct { + ID int64 `firestore:"id"` + Level int `firestore:"level"` + GrantedTime int64 `firestore:"granted_time"` + } +) diff --git a/svc/pkg/infra/entity/user/aggregate.go b/svc/pkg/infra/entity/user/aggregate.go index 7fe0110..812a4a0 100644 --- a/svc/pkg/infra/entity/user/aggregate.go +++ b/svc/pkg/infra/entity/user/aggregate.go @@ -1,6 +1,8 @@ package entity import ( + "time" + "ynufes-mypage-backend/pkg/identity" "ynufes-mypage-backend/svc/pkg/domain/model/user" ) @@ -12,6 +14,8 @@ type User struct { Status int `firestore:"status"` UserDetail Line + Admin + Agent } func (u User) ToModel() (*user.User, error) { @@ -23,6 +27,18 @@ func (u User) ToModel() (*user.User, error) { if err != nil { return nil, err } + roles := make([]user.Role, len(u.Roles)) + for i, role := range u.Roles { + lv, err := user.NewRoleLevel(role.Level) + if err != nil { + return nil, err + } + roles[i] = user.Role{ + ID: identity.NewID(role.ID), + Level: lv, + GrantedTime: time.UnixMilli(role.GrantedTime), + } + } return &user.User{ ID: u.ID, Status: user.Status(u.Status), @@ -46,5 +62,12 @@ func (u User) ToModel() (*user.User, error) { EncryptedAccessToken: user.EncryptedAccessToken(u.EncryptedAccessToken), EncryptedRefreshToken: user.EncryptedRefreshToken(u.EncryptedRefreshToken), }, + Admin: user.Admin{ + IsSuperAdmin: u.IsSuperAdmin, + GrantedTime: time.UnixMilli(u.GrantedTime), + }, + Agent: user.Agent{ + Roles: roles, + }, }, nil } From 1755d83552e3bd18b50a26c80ef9c4f32dbb508a Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Fri, 17 Feb 2023 13:24:58 +0900 Subject: [PATCH 15/19] =?UTF-8?q?=E2=9C=A8=20(model/user)Type=E3=81=ABMemb?= =?UTF-8?q?er=E3=81=AE=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- svc/pkg/domain/model/user/type.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/svc/pkg/domain/model/user/type.go b/svc/pkg/domain/model/user/type.go index baf27a9..e1a939a 100644 --- a/svc/pkg/domain/model/user/type.go +++ b/svc/pkg/domain/model/user/type.go @@ -6,12 +6,15 @@ type Type int const ( TypeNormal Type = 1 + TypeMember Type = 2 ) func NewType(t int) (Type, error) { - switch t { - case int(TypeNormal): + switch Type(t) { + case TypeNormal: return TypeNormal, nil + case TypeMember: + return TypeMember, nil default: return -1, errors.New("USER TYPE VALUE IS INVALID") } From ecde8ecedb8da7cc5c9b34e5c6d62fa32d9e7bf0 Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Fri, 17 Feb 2023 20:24:01 +0900 Subject: [PATCH 16/19] =?UTF-8?q?=F0=9F=8E=A8=20(model,writer/user)Granted?= =?UTF-8?q?Time=E3=82=92=E3=83=9D=E3=82=A4=E3=83=B3=E3=82=BF=E3=81=AB?= =?UTF-8?q?=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- svc/pkg/domain/model/user/admin.go | 7 +++++-- svc/pkg/infra/entity/user/aggregate.go | 7 ++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/svc/pkg/domain/model/user/admin.go b/svc/pkg/domain/model/user/admin.go index 6c653e0..04c9aef 100644 --- a/svc/pkg/domain/model/user/admin.go +++ b/svc/pkg/domain/model/user/admin.go @@ -1,10 +1,13 @@ package user -import "time" +import ( + "time" +) type ( Admin struct { IsSuperAdmin bool - GrantedTime time.Time + GrantedTime *time.Time + // if IsSuperAdmin is false, GrantedTime should be nil. } ) diff --git a/svc/pkg/infra/entity/user/aggregate.go b/svc/pkg/infra/entity/user/aggregate.go index 812a4a0..336e603 100644 --- a/svc/pkg/infra/entity/user/aggregate.go +++ b/svc/pkg/infra/entity/user/aggregate.go @@ -39,6 +39,11 @@ func (u User) ToModel() (*user.User, error) { GrantedTime: time.UnixMilli(role.GrantedTime), } } + var adminGrantedTime *time.Time + if u.IsSuperAdmin { + t := time.UnixMilli(u.GrantedTime) + adminGrantedTime = &t + } return &user.User{ ID: u.ID, Status: user.Status(u.Status), @@ -64,7 +69,7 @@ func (u User) ToModel() (*user.User, error) { }, Admin: user.Admin{ IsSuperAdmin: u.IsSuperAdmin, - GrantedTime: time.UnixMilli(u.GrantedTime), + GrantedTime: adminGrantedTime, }, Agent: user.Agent{ Roles: roles, From 0e03853f8d346d707917e7ae36012c52441f9067 Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Fri, 17 Feb 2023 22:12:27 +0900 Subject: [PATCH 17/19] =?UTF-8?q?=F0=9F=90=9B=20(writer/user)UpdateDetail?= =?UTF-8?q?=E3=81=ABStatus=E6=9B=B4=E6=96=B0=E3=81=AE=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- svc/pkg/infra/writer/user.go | 1 + 1 file changed, 1 insertion(+) diff --git a/svc/pkg/infra/writer/user.go b/svc/pkg/infra/writer/user.go index 8cd1c53..f22cede 100644 --- a/svc/pkg/infra/writer/user.go +++ b/svc/pkg/infra/writer/user.go @@ -184,6 +184,7 @@ func (u User) UpdateUserDetail(ctx context.Context, oldUser *user.User, update u // do not update field: Type update.Type = oldUser.Detail.Type oldUser.Detail = update + oldUser.Status = user.Status(newStatus) } return err } From 3e8b9a61d3993ed407e4b1f4ace17480d6efb387 Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Fri, 17 Feb 2023 22:13:37 +0900 Subject: [PATCH 18/19] =?UTF-8?q?=E2=9C=A8=20(writer/user)UpdateAgent,Upda?= =?UTF-8?q?teAdmin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- svc/pkg/infra/writer/user.go | 59 ++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/svc/pkg/infra/writer/user.go b/svc/pkg/infra/writer/user.go index f22cede..e198c14 100644 --- a/svc/pkg/infra/writer/user.go +++ b/svc/pkg/infra/writer/user.go @@ -4,6 +4,7 @@ import ( "cloud.google.com/go/firestore" "context" "log" + "time" "ynufes-mypage-backend/svc/pkg/domain/model/user" "ynufes-mypage-backend/svc/pkg/exception" entity "ynufes-mypage-backend/svc/pkg/infra/entity/user" @@ -19,6 +20,7 @@ func NewUser(c *firestore.Client) User { } } +// Create Note that new user will be created with no roles or authority. func (u User) Create(ctx context.Context, model user.User) error { if !model.ID.HasValue() { return exception.ErrIDNotAssigned @@ -44,6 +46,14 @@ func (u User) Create(ctx context.Context, model user.User) error { EncryptedAccessToken: string(model.Line.EncryptedAccessToken), EncryptedRefreshToken: string(model.Line.EncryptedRefreshToken), }, + // new user will not have any roles + Admin: entity.Admin{ + IsSuperAdmin: false, + GrantedTime: 0, + }, + Agent: entity.Agent{ + Roles: []entity.Role{}, + }, } //NOTE: Create fails if the document already exists _, err := u.collection.Doc(model.ID.ExportID()). @@ -189,6 +199,55 @@ func (u User) UpdateUserDetail(ctx context.Context, oldUser *user.User, update u return err } +func (u User) UpdateAgent(ctx context.Context, oldUser *user.User, newAgent user.Agent) error { + var updateTargets []firestore.Update + targets := map[string]struct { + oldValue interface{} + newValue interface{} + }{ + "agent-roles": { + oldValue: oldUser.Agent.Roles, + newValue: newAgent.Roles, + }, + } + for key, value := range targets { + if value.oldValue != value.newValue { + updateTargets = append(updateTargets, firestore.Update{Path: key, Value: value.newValue}) + } + } + if len(targets) == 0 { + return nil + } + if _, err := u.collection.Doc(oldUser.ID.ExportID()). + Update(ctx, updateTargets); err != nil { + return err + } + oldUser.Agent = newAgent + return nil +} + +func (u User) UpdateAdmin(ctx context.Context, oldUser *user.User, newAdmin user.Admin) error { + var updateTargets []firestore.Update + if oldUser.Admin.IsSuperAdmin == newAdmin.IsSuperAdmin { + return nil + } + if newAdmin.IsSuperAdmin { + updateTargets = append(updateTargets, + firestore.Update{Path: "admin-is_super_admin", Value: true}, + firestore.Update{Path: "admin-granted_time", Value: time.Now()}) + } else { + updateTargets = append(updateTargets, + firestore.Update{Path: "admin-is_super_admin", Value: false}, + firestore.Update{Path: "admin-granted_time", Value: 0}) + } + if _, err := u.collection.Doc(oldUser.ID.ExportID()). + Update(ctx, updateTargets); err != nil { + return err + } + oldUser.Admin = newAdmin + return nil +} + func (u User) Delete(ctx context.Context, model user.User) error { log.Printf("DELETE USER: %v", model) _, err := u.collection.Doc(model.ID.ExportID()). From 4d28eae6b88928905f08df34b8435a80948c938d Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Fri, 17 Feb 2023 22:19:46 +0900 Subject: [PATCH 19/19] =?UTF-8?q?=E2=9C=A8=20(command/user)UpdateAgent,Upd?= =?UTF-8?q?ateAdmin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- svc/pkg/domain/command/user.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/svc/pkg/domain/command/user.go b/svc/pkg/domain/command/user.go index 1b592a7..4c9c90f 100644 --- a/svc/pkg/domain/command/user.go +++ b/svc/pkg/domain/command/user.go @@ -9,6 +9,8 @@ type User interface { Create(context.Context, user.User) error UpdateLine(ctx context.Context, oldUser *user.User, update user.Line) error UpdateUserDetail(ctx context.Context, oldUser *user.User, update user.Detail) error + UpdateAgent(ctx context.Context, oldUser *user.User, update user.Agent) error + UpdateAdmin(ctx context.Context, oldUser *user.User, update user.Admin) error UpdateAll(context.Context, user.User) error Delete(context.Context, user.User) error }