From 0467a7870efa043dacbb9d3da07a4b047ad4ed4c Mon Sep 17 00:00:00 2001 From: IgorPolyakov Date: Wed, 10 Jul 2024 15:29:59 +0300 Subject: [PATCH] Add role in the team. close #44 --- CONCEPT.md | 2 +- api/openapi.yaml | 24 +++++++++++++ html/assets/js/index.js | 4 +-- internal/app/database/struct_updater.go | 1 + .../app/database/update0016_update0017.go | 32 +++++++++++++++++ .../database/update0017_update0017testdata.go | 29 +++++++++++++++ .../app/database/update0017_update0018.go | 32 +++++++++++++++++ internal/app/db/profile.go | 11 ++++-- internal/app/repository/user.go | 8 ++--- internal/app/server/server.gen.go | 36 +++++++++++++++++-- internal/app/view/profile.go | 7 ++-- 11 files changed, 171 insertions(+), 15 deletions(-) create mode 100644 internal/app/database/update0016_update0017.go create mode 100644 internal/app/database/update0017_update0017testdata.go create mode 100644 internal/app/database/update0017_update0018.go diff --git a/CONCEPT.md b/CONCEPT.md index 0af71eb..9a44e20 100644 --- a/CONCEPT.md +++ b/CONCEPT.md @@ -106,7 +106,7 @@ Отдельной таблицей по отношению к пользователям (многие ко многим? - да владеть двумя командами это возможно): - команда uuid - *уникальный идентификатор* -- Роль в команде - (четыре захаркоженные роли: owner, captain, vice-captain, player) +- Роль в [команде](https://github.com/sea-kg/ctf01d-training-platform/issues/44) - (owner, captain, vice-captain, player, guest) - Название роли в команде - *роли придуманные капитанами и замами команд* - публичный комментарий - *виден всем* - приватный комментарий - *виден только владельцу капитану и зам капитана* diff --git a/api/openapi.yaml b/api/openapi.yaml index 58d4839..8aa79b2 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -892,6 +892,11 @@ components: ProfileResponse: type: object description: The response schema for a user's profile, including id, timestamps, team name, and team history. + required: + - id + - created_at + - team_name + - team_role properties: id: type: string @@ -908,6 +913,15 @@ components: team_name: type: string description: The current name of the user's team. + team_role: + type: string + description: The current role of the user's team. + enum: + - owner + - captain + - vice-captain + - player + - guest team_history: type: array description: The list of teams the user has been part of, including the periods of membership. @@ -917,12 +931,22 @@ components: required: - name - join + - role type: object description: The schema for recording the history of teams a user has joined and left. properties: name: type: string description: The name of the team. + role: + type: string + description: Role of the user's team. + enum: + - owner + - captain + - vice-captain + - player + - guest join: type: string format: date-time diff --git a/html/assets/js/index.js b/html/assets/js/index.js index aadb2b0..9eeec05 100644 --- a/html/assets/js/index.js +++ b/html/assets/js/index.js @@ -716,13 +716,13 @@ function showMyTeams(userId) { for (var i in data.team_history) { var team = data.team_history[i]; teamHistoryHtml += '
'; - teamHistoryHtml += '
' + team.name + '
'; + teamHistoryHtml += '
[' + team.name + '] ' + team.role + '
'; teamHistoryHtml += '
Joined at: ' + new Date(team.join).toLocaleString() + (team.left ? ', Left at: ' + new Date(team.left).toLocaleString() : ', ... ') + '
'; teamHistoryHtml += '
'; } teamHistoryHtml += ''; - var currentTeamHtml = '
Current Team: ' + data.team_name + '
'; + var currentTeamHtml = '
Current Team: [' + data.team_name + '] ' + data.team_role + '
'; $('#my_teams_content').html(currentTeamHtml + teamHistoryHtml); $('#modal_my_teams').modal('show'); }); diff --git a/internal/app/database/struct_updater.go b/internal/app/database/struct_updater.go index decda7e..a6b773c 100644 --- a/internal/app/database/struct_updater.go +++ b/internal/app/database/struct_updater.go @@ -44,6 +44,7 @@ func RegisterAllUpdates() map[string][]DatabaseUpdateFunc { allUpdates = RegisterDatabaseUpdate(allUpdates, DatabaseUpdate_update0015_update0016) allUpdates = RegisterDatabaseUpdate(allUpdates, DatabaseUpdate_update0016_update0016testdata) allUpdates = RegisterDatabaseUpdate(allUpdates, DatabaseUpdate_update0016_update0017_no_university) + allUpdates = RegisterDatabaseUpdate(allUpdates, DatabaseUpdate_update0017_update0018) return allUpdates } diff --git a/internal/app/database/update0016_update0017.go b/internal/app/database/update0016_update0017.go new file mode 100644 index 0000000..110f048 --- /dev/null +++ b/internal/app/database/update0016_update0017.go @@ -0,0 +1,32 @@ +package database + +import ( + "database/sql" + "log/slog" + "runtime" +) + +func DatabaseUpdate_update0016_update0017(db *sql.DB, getInfo bool) (string, string, string, error) { + + // WARNING!!! + // Do not change the update if it has already been installed by other developers or in production. + // To correct the database, create a new update and register it in the list of updates. + + fromUpdateId, toUpdateId := ParseNameFuncUpdate(runtime.Caller(0)) + description := "Add role to profile and team history" + if getInfo { + return fromUpdateId, toUpdateId, description, nil + } + query := ` + BEGIN; + ALTER TABLE profiles ADD COLUMN role varchar(50) default 'player' NOT NULL; + ALTER TABLE team_history ADD COLUMN role varchar(50) default 'player' NOT NULL; + COMMIT; + ` + _, err := db.Query(query) + if err != nil { + slog.Error("Problem with select, query: " + query + "\n error:" + err.Error()) + return fromUpdateId, toUpdateId, description, err + } + return fromUpdateId, toUpdateId, description, nil +} diff --git a/internal/app/database/update0017_update0017testdata.go b/internal/app/database/update0017_update0017testdata.go new file mode 100644 index 0000000..249388e --- /dev/null +++ b/internal/app/database/update0017_update0017testdata.go @@ -0,0 +1,29 @@ +package database + +import ( + "database/sql" + "log/slog" + "runtime" +) + +func DatabaseUpdate_update0017_update0017testdata(db *sql.DB, getInfo bool) (string, string, string, error) { + + // WARNING!!! + // Do not change the update if it has already been installed by other developers or in production. + // To correct the database, create a new update and register it in the list of updates. + + fromUpdateId, toUpdateId := ParseNameFuncUpdate(runtime.Caller(0)) + description := "Flush team_history table" + if getInfo { + return fromUpdateId, toUpdateId, description, nil + } + + query := ` + ` + _, err := db.Query(query) + if err != nil { + slog.Error("Problem with select, query: " + query + "\n error:" + err.Error()) + return fromUpdateId, toUpdateId, description, err + } + return fromUpdateId, toUpdateId, description, nil +} diff --git a/internal/app/database/update0017_update0018.go b/internal/app/database/update0017_update0018.go new file mode 100644 index 0000000..8159b24 --- /dev/null +++ b/internal/app/database/update0017_update0018.go @@ -0,0 +1,32 @@ +package database + +import ( + "database/sql" + "log/slog" + "runtime" +) + +func DatabaseUpdate_update0017_update0018(db *sql.DB, getInfo bool) (string, string, string, error) { + + // WARNING!!! + // Do not change the update if it has already been installed by other developers or in production. + // To correct the database, create a new update and register it in the list of updates. + + fromUpdateId, toUpdateId := ParseNameFuncUpdate(runtime.Caller(0)) + description := "Add role to profile and team history" + if getInfo { + return fromUpdateId, toUpdateId, description, nil + } + query := ` + BEGIN; + ALTER TABLE profiles ADD COLUMN role varchar(50) default 'player' NOT NULL; + ALTER TABLE team_history ADD COLUMN role varchar(50) default 'player' NOT NULL; + COMMIT; + ` + _, err := db.Query(query) + if err != nil { + slog.Error("Problem with select, query: " + query + "\n error:" + err.Error()) + return fromUpdateId, toUpdateId, description, err + } + return fromUpdateId, toUpdateId, description, nil +} diff --git a/internal/app/db/profile.go b/internal/app/db/profile.go index ce72465..f7bac51 100644 --- a/internal/app/db/profile.go +++ b/internal/app/db/profile.go @@ -2,17 +2,22 @@ package db import ( "time" + + openapi_types "github.com/oapi-codegen/runtime/types" ) type Profile struct { - CurrentTeam string `db:"name"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"created_at"` + Id openapi_types.UUID `db:"id"` + CurrentTeam string `db:"name"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"created_at"` + Role string `db:"role"` } type ProfileTeams struct { JoinedAt time.Time `db:"joined_at"` LeftAt *time.Time `db:"left_at"` + Role string `db:"role"` Name string `db:"name"` } diff --git a/internal/app/repository/user.go b/internal/app/repository/user.go index c8dfe8e..f36534f 100644 --- a/internal/app/repository/user.go +++ b/internal/app/repository/user.go @@ -51,17 +51,17 @@ func (r *userRepo) AddUserToTeams(ctx context.Context, userId openapi_types.UUID func (r *userRepo) GetProfileWithHistory(ctx context.Context, id openapi_types.UUID) (*models.ProfileWithHistory, error) { query := ` - SELECT teams.name, created_at, updated_at + SELECT profiles.id, teams.name, role, created_at, updated_at FROM profiles JOIN teams on profiles.current_team_id=teams.id WHERE profiles.user_id = $1 ` profile := models.Profile{} - err := r.db.QueryRowContext(ctx, query, id).Scan(&profile.CurrentTeam, &profile.CreatedAt, &profile.UpdatedAt) + err := r.db.QueryRowContext(ctx, query, id).Scan(&profile.Id, &profile.CurrentTeam, &profile.Role, &profile.CreatedAt, &profile.UpdatedAt) if err != nil { return nil, err } query = ` - SELECT joined_at, left_at, name + SELECT joined_at, left_at, name, role FROM team_history JOIN teams ON teams.id = team_history.team_id WHERE user_id = $1 @@ -70,7 +70,7 @@ func (r *userRepo) GetProfileWithHistory(ctx context.Context, id openapi_types.U var history []models.ProfileTeams for rows.Next() { var team models.ProfileTeams - err := rows.Scan(&team.JoinedAt, &team.LeftAt, &team.Name) + err := rows.Scan(&team.JoinedAt, &team.LeftAt, &team.Name, &team.Role) if err != nil { return nil, err } diff --git a/internal/app/server/server.gen.go b/internal/app/server/server.gen.go index 69b7ee8..d3553c1 100644 --- a/internal/app/server/server.gen.go +++ b/internal/app/server/server.gen.go @@ -13,6 +13,24 @@ import ( openapi_types "github.com/oapi-codegen/runtime/types" ) +// Defines values for ProfileResponseTeamRole. +const ( + ProfileResponseTeamRoleCaptain ProfileResponseTeamRole = "captain" + ProfileResponseTeamRoleGuest ProfileResponseTeamRole = "guest" + ProfileResponseTeamRoleOwner ProfileResponseTeamRole = "owner" + ProfileResponseTeamRolePlayer ProfileResponseTeamRole = "player" + ProfileResponseTeamRoleViceCaptain ProfileResponseTeamRole = "vice-captain" +) + +// Defines values for TeamHistoryRole. +const ( + TeamHistoryRoleCaptain TeamHistoryRole = "captain" + TeamHistoryRoleGuest TeamHistoryRole = "guest" + TeamHistoryRoleOwner TeamHistoryRole = "owner" + TeamHistoryRolePlayer TeamHistoryRole = "player" + TeamHistoryRoleViceCaptain TeamHistoryRole = "vice-captain" +) + // Defines values for UserRequestRole. const ( UserRequestRoleAdmin UserRequestRole = "admin" @@ -57,21 +75,27 @@ type GameResponse struct { // ProfileResponse The response schema for a user's profile, including id, timestamps, team name, and team history. type ProfileResponse struct { // CreatedAt The timestamp when the user profile was created. - CreatedAt *time.Time `json:"created_at,omitempty"` + CreatedAt time.Time `json:"created_at"` // Id The unique identifier for the user. - Id *openapi_types.UUID `json:"id,omitempty"` + Id openapi_types.UUID `json:"id"` // TeamHistory The list of teams the user has been part of, including the periods of membership. TeamHistory *[]TeamHistory `json:"team_history,omitempty"` // TeamName The current name of the user's team. - TeamName *string `json:"team_name,omitempty"` + TeamName string `json:"team_name"` + + // TeamRole The current role of the user's team. + TeamRole ProfileResponseTeamRole `json:"team_role"` // UpdatedAt The timestamp when the user profile was last updated. UpdatedAt *time.Time `json:"updated_at,omitempty"` } +// ProfileResponseTeamRole The current role of the user's team. +type ProfileResponseTeamRole string + // ResultRequest defines model for ResultRequest. type ResultRequest struct { // GameId Identifier of the game this result is for @@ -165,8 +189,14 @@ type TeamHistory struct { // Name The name of the team. Name string `json:"name"` + + // Role Role of the user's team. + Role TeamHistoryRole `json:"role"` } +// TeamHistoryRole Role of the user's team. +type TeamHistoryRole string + // TeamRequest defines model for TeamRequest. type TeamRequest struct { // AvatarUrl URL to the team's avatar diff --git a/internal/app/view/profile.go b/internal/app/view/profile.go index afeee2a..537df81 100644 --- a/internal/app/view/profile.go +++ b/internal/app/view/profile.go @@ -8,9 +8,11 @@ import ( func NewProfileFromModel(p *db.ProfileWithHistory) *server.ProfileResponse { return &server.ProfileResponse{ - CreatedAt: &p.Profile.CreatedAt, + Id: p.Profile.Id, + CreatedAt: p.Profile.CreatedAt, UpdatedAt: &p.Profile.UpdatedAt, - TeamName: &p.Profile.CurrentTeam, + TeamName: p.Profile.CurrentTeam, + TeamRole: server.ProfileResponseTeamRole(p.Profile.Role), TeamHistory: makeTeamHistory(p.History), } } @@ -22,6 +24,7 @@ func makeTeamHistory(tms []db.ProfileTeams) *[]server.TeamHistory { Join: tm.JoinedAt, Left: tm.LeftAt, Name: tm.Name, + Role: server.TeamHistoryRole(tm.Role), }) } return &out