feature: add team creation & project deletion
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@ -36,10 +36,29 @@ type CreateTaskChecklistItem struct {
|
||||
Position float64 `json:"position"`
|
||||
}
|
||||
|
||||
type CreateTeamMember struct {
|
||||
UserID uuid.UUID `json:"userID"`
|
||||
TeamID uuid.UUID `json:"teamID"`
|
||||
}
|
||||
|
||||
type CreateTeamMemberPayload struct {
|
||||
Team *pg.Team `json:"team"`
|
||||
TeamMember *ProjectMember `json:"teamMember"`
|
||||
}
|
||||
|
||||
type DeleteProject struct {
|
||||
ProjectID uuid.UUID `json:"projectID"`
|
||||
}
|
||||
|
||||
type DeleteProjectLabel struct {
|
||||
ProjectLabelID uuid.UUID `json:"projectLabelID"`
|
||||
}
|
||||
|
||||
type DeleteProjectPayload struct {
|
||||
Ok bool `json:"ok"`
|
||||
Project *pg.Project `json:"project"`
|
||||
}
|
||||
|
||||
type DeleteTaskChecklistItem struct {
|
||||
TaskChecklistItemID uuid.UUID `json:"taskChecklistItemID"`
|
||||
}
|
||||
@ -123,8 +142,8 @@ type NewTaskLocation struct {
|
||||
}
|
||||
|
||||
type NewTeam struct {
|
||||
Name string `json:"name"`
|
||||
OrganizationID string `json:"organizationID"`
|
||||
Name string `json:"name"`
|
||||
OrganizationID uuid.UUID `json:"organizationID"`
|
||||
}
|
||||
|
||||
type NewUserAccount struct {
|
||||
|
@ -55,6 +55,7 @@ type Team {
|
||||
id: ID!
|
||||
createdAt: Time!
|
||||
name: String!
|
||||
members: [ProjectMember!]!
|
||||
}
|
||||
|
||||
type Project {
|
||||
@ -117,7 +118,13 @@ input FindTask {
|
||||
taskID: UUID!
|
||||
}
|
||||
|
||||
type Organization {
|
||||
id: ID!
|
||||
name: String!
|
||||
}
|
||||
|
||||
type Query {
|
||||
organizations: [Organization!]!
|
||||
users: [UserAccount!]!
|
||||
findUser(input: FindUser!): UserAccount!
|
||||
findProject(input: FindProject!): Project!
|
||||
@ -143,7 +150,7 @@ input NewUserAccount {
|
||||
|
||||
input NewTeam {
|
||||
name: String!
|
||||
organizationID: String!
|
||||
organizationID: UUID!
|
||||
}
|
||||
|
||||
input NewProject {
|
||||
@ -330,6 +337,25 @@ input UpdateTaskChecklistItemName {
|
||||
name: String!
|
||||
}
|
||||
|
||||
input CreateTeamMember {
|
||||
userID: UUID!
|
||||
teamID: UUID!
|
||||
}
|
||||
|
||||
type CreateTeamMemberPayload {
|
||||
team: Team!
|
||||
teamMember: ProjectMember!
|
||||
}
|
||||
|
||||
input DeleteProject {
|
||||
projectID: UUID!
|
||||
}
|
||||
|
||||
type DeleteProjectPayload {
|
||||
ok: Boolean!
|
||||
project: Project!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createRefreshToken(input: NewRefreshToken!): RefreshToken!
|
||||
|
||||
@ -338,7 +364,10 @@ type Mutation {
|
||||
createTeam(input: NewTeam!): Team!
|
||||
clearProfileAvatar: UserAccount!
|
||||
|
||||
createTeamMember(input: CreateTeamMember!): CreateTeamMemberPayload!
|
||||
|
||||
createProject(input: NewProject!): Project!
|
||||
deleteProject(input: DeleteProject!): DeleteProjectPayload!
|
||||
updateProjectName(input: UpdateProjectName): Project!
|
||||
|
||||
createProjectLabel(input: NewProjectLabel!): ProjectLabel!
|
||||
|
@ -41,12 +41,8 @@ func (r *mutationResolver) CreateUserAccount(ctx context.Context, input NewUserA
|
||||
}
|
||||
|
||||
func (r *mutationResolver) CreateTeam(ctx context.Context, input NewTeam) (*pg.Team, error) {
|
||||
organizationID, err := uuid.Parse(input.OrganizationID)
|
||||
if err != nil {
|
||||
return &pg.Team{}, err
|
||||
}
|
||||
createdAt := time.Now().UTC()
|
||||
team, err := r.Repository.CreateTeam(ctx, pg.CreateTeamParams{organizationID, createdAt, input.Name})
|
||||
team, err := r.Repository.CreateTeam(ctx, pg.CreateTeamParams{input.OrganizationID, createdAt, input.Name})
|
||||
return &team, err
|
||||
}
|
||||
|
||||
@ -65,12 +61,49 @@ func (r *mutationResolver) ClearProfileAvatar(ctx context.Context) (*pg.UserAcco
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) CreateTeamMember(ctx context.Context, input CreateTeamMember) (*CreateTeamMemberPayload, error) {
|
||||
addedDate := time.Now().UTC()
|
||||
team, err := r.Repository.GetTeamByID(ctx, input.TeamID)
|
||||
if err != nil {
|
||||
return &CreateTeamMemberPayload{}, err
|
||||
}
|
||||
_, err = r.Repository.CreateTeamMember(ctx, pg.CreateTeamMemberParams{TeamID: input.TeamID, UserID: input.UserID, Addeddate: addedDate})
|
||||
user, err := r.Repository.GetUserAccountByID(ctx, input.UserID)
|
||||
if err != nil {
|
||||
return &CreateTeamMemberPayload{}, err
|
||||
}
|
||||
var url *string
|
||||
if user.ProfileAvatarUrl.Valid {
|
||||
url = &user.ProfileAvatarUrl.String
|
||||
}
|
||||
profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor}
|
||||
return &CreateTeamMemberPayload{
|
||||
Team: &team,
|
||||
TeamMember: &ProjectMember{
|
||||
ID: user.UserID,
|
||||
FullName: user.FullName,
|
||||
ProfileIcon: profileIcon,
|
||||
}}, nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) CreateProject(ctx context.Context, input NewProject) (*pg.Project, error) {
|
||||
createdAt := time.Now().UTC()
|
||||
project, err := r.Repository.CreateProject(ctx, pg.CreateProjectParams{input.UserID, input.TeamID, createdAt, input.Name})
|
||||
return &project, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) DeleteProject(ctx context.Context, input DeleteProject) (*DeleteProjectPayload, error) {
|
||||
project, err := r.Repository.GetProjectByID(ctx, input.ProjectID)
|
||||
if err != nil {
|
||||
return &DeleteProjectPayload{Ok: false}, err
|
||||
}
|
||||
err = r.Repository.DeleteProjectByID(ctx, input.ProjectID)
|
||||
if err != nil {
|
||||
return &DeleteProjectPayload{Ok: false}, err
|
||||
}
|
||||
return &DeleteProjectPayload{Project: &project, Ok: true}, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) UpdateProjectName(ctx context.Context, input *UpdateProjectName) (*pg.Project, error) {
|
||||
project, err := r.Repository.UpdateProjectNameByID(ctx, pg.UpdateProjectNameByIDParams{ProjectID: input.ProjectID, Name: input.Name})
|
||||
if err != nil {
|
||||
@ -409,6 +442,10 @@ func (r *mutationResolver) LogoutUser(ctx context.Context, input LogoutUser) (bo
|
||||
return true, err
|
||||
}
|
||||
|
||||
func (r *organizationResolver) ID(ctx context.Context, obj *pg.Organization) (uuid.UUID, error) {
|
||||
return obj.OrganizationID, nil
|
||||
}
|
||||
|
||||
func (r *projectResolver) ID(ctx context.Context, obj *pg.Project) (uuid.UUID, error) {
|
||||
return obj.ProjectID, nil
|
||||
}
|
||||
@ -475,6 +512,10 @@ func (r *projectLabelResolver) Name(ctx context.Context, obj *pg.ProjectLabel) (
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (r *queryResolver) Organizations(ctx context.Context) ([]pg.Organization, error) {
|
||||
return r.Repository.GetAllOrganizations(ctx)
|
||||
}
|
||||
|
||||
func (r *queryResolver) Users(ctx context.Context) ([]pg.UserAccount, error) {
|
||||
return r.Repository.GetAllUserAccounts(ctx)
|
||||
}
|
||||
@ -683,6 +724,31 @@ func (r *teamResolver) ID(ctx context.Context, obj *pg.Team) (uuid.UUID, error)
|
||||
return obj.TeamID, nil
|
||||
}
|
||||
|
||||
func (r *teamResolver) Members(ctx context.Context, obj *pg.Team) ([]ProjectMember, error) {
|
||||
teamMembers, err := r.Repository.GetTeamMembersForTeamID(ctx, obj.TeamID)
|
||||
var projectMembers []ProjectMember
|
||||
if err != nil {
|
||||
return projectMembers, err
|
||||
}
|
||||
for _, teamMember := range teamMembers {
|
||||
user, err := r.Repository.GetUserAccountByID(ctx, teamMember.UserID)
|
||||
if err != nil {
|
||||
return projectMembers, err
|
||||
}
|
||||
var url *string
|
||||
if user.ProfileAvatarUrl.Valid {
|
||||
url = &user.ProfileAvatarUrl.String
|
||||
}
|
||||
profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor}
|
||||
projectMembers = append(projectMembers, ProjectMember{
|
||||
ID: user.UserID,
|
||||
FullName: user.FullName,
|
||||
ProfileIcon: profileIcon,
|
||||
})
|
||||
}
|
||||
return projectMembers, nil
|
||||
}
|
||||
|
||||
func (r *userAccountResolver) ID(ctx context.Context, obj *pg.UserAccount) (uuid.UUID, error) {
|
||||
return obj.UserID, nil
|
||||
}
|
||||
@ -702,6 +768,9 @@ func (r *Resolver) LabelColor() LabelColorResolver { return &labelColorResolver{
|
||||
// Mutation returns MutationResolver implementation.
|
||||
func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} }
|
||||
|
||||
// Organization returns OrganizationResolver implementation.
|
||||
func (r *Resolver) Organization() OrganizationResolver { return &organizationResolver{r} }
|
||||
|
||||
// Project returns ProjectResolver implementation.
|
||||
func (r *Resolver) Project() ProjectResolver { return &projectResolver{r} }
|
||||
|
||||
@ -737,6 +806,7 @@ func (r *Resolver) UserAccount() UserAccountResolver { return &userAccountResolv
|
||||
|
||||
type labelColorResolver struct{ *Resolver }
|
||||
type mutationResolver struct{ *Resolver }
|
||||
type organizationResolver struct{ *Resolver }
|
||||
type projectResolver struct{ *Resolver }
|
||||
type projectLabelResolver struct{ *Resolver }
|
||||
type queryResolver struct{ *Resolver }
|
||||
|
8
api/migrations/0031_add-team-member-table.up.sql
Normal file
8
api/migrations/0031_add-team-member-table.up.sql
Normal file
@ -0,0 +1,8 @@
|
||||
CREATE TABLE team_member (
|
||||
team_member_id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
team_id uuid NOT NULL REFERENCES team(team_id) ON DELETE CASCADE,
|
||||
user_id uuid NOT NULL REFERENCES user_account(user_id) ON DELETE CASCADE,
|
||||
UNIQUE(team_id, user_id),
|
||||
addedDate timestamptz NOT NULL
|
||||
);
|
||||
|
@ -0,0 +1,6 @@
|
||||
ALTER TABLE project_label DROP CONSTRAINT project_label_project_id_fkey;
|
||||
ALTER TABLE project_label
|
||||
ADD CONSTRAINT project_label_project_id_fkey
|
||||
FOREIGN KEY (project_id)
|
||||
REFERENCES project(project_id)
|
||||
ON DELETE CASCADE;
|
@ -0,0 +1,6 @@
|
||||
ALTER TABLE task_label DROP CONSTRAINT task_label_project_label_id_fkey;
|
||||
ALTER TABLE task_label
|
||||
ADD CONSTRAINT task_label_project_label_id_fkey
|
||||
FOREIGN KEY (project_label_id)
|
||||
REFERENCES project_label(project_label_id)
|
||||
ON DELETE CASCADE;
|
@ -103,6 +103,13 @@ type Team struct {
|
||||
OrganizationID uuid.UUID `json:"organization_id"`
|
||||
}
|
||||
|
||||
type TeamMember struct {
|
||||
TeamMemberID uuid.UUID `json:"team_member_id"`
|
||||
TeamID uuid.UUID `json:"team_id"`
|
||||
UserID uuid.UUID `json:"user_id"`
|
||||
Addeddate time.Time `json:"addeddate"`
|
||||
}
|
||||
|
||||
type UserAccount struct {
|
||||
UserID uuid.UUID `json:"user_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
|
@ -7,11 +7,17 @@ import (
|
||||
)
|
||||
|
||||
type Repository interface {
|
||||
CreateTeamMember(ctx context.Context, arg CreateTeamMemberParams) (TeamMember, error)
|
||||
DeleteTeamMemberByUserID(ctx context.Context, userID uuid.UUID) error
|
||||
GetTeamMembersForTeamID(ctx context.Context, teamID uuid.UUID) ([]TeamMember, error)
|
||||
|
||||
CreateTeam(ctx context.Context, arg CreateTeamParams) (Team, error)
|
||||
DeleteTeamByID(ctx context.Context, teamID uuid.UUID) error
|
||||
GetTeamByID(ctx context.Context, teamID uuid.UUID) (Team, error)
|
||||
GetAllTeams(ctx context.Context) ([]Team, error)
|
||||
|
||||
DeleteProjectByID(ctx context.Context, projectID uuid.UUID) error
|
||||
|
||||
CreateProject(ctx context.Context, arg CreateProjectParams) (Project, error)
|
||||
GetAllProjects(ctx context.Context) ([]Project, error)
|
||||
GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error)
|
||||
|
@ -39,6 +39,15 @@ func (q *Queries) CreateProject(ctx context.Context, arg CreateProjectParams) (P
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteProjectByID = `-- name: DeleteProjectByID :exec
|
||||
DELETE FROM project WHERE project_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteProjectByID(ctx context.Context, projectID uuid.UUID) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteProjectByID, projectID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getAllProjects = `-- name: GetAllProjects :many
|
||||
SELECT project_id, team_id, created_at, name, owner FROM project
|
||||
`
|
||||
|
@ -21,8 +21,10 @@ type Querier interface {
|
||||
CreateTaskGroup(ctx context.Context, arg CreateTaskGroupParams) (TaskGroup, error)
|
||||
CreateTaskLabelForTask(ctx context.Context, arg CreateTaskLabelForTaskParams) (TaskLabel, error)
|
||||
CreateTeam(ctx context.Context, arg CreateTeamParams) (Team, error)
|
||||
CreateTeamMember(ctx context.Context, arg CreateTeamMemberParams) (TeamMember, error)
|
||||
CreateUserAccount(ctx context.Context, arg CreateUserAccountParams) (UserAccount, error)
|
||||
DeleteExpiredTokens(ctx context.Context) error
|
||||
DeleteProjectByID(ctx context.Context, projectID uuid.UUID) error
|
||||
DeleteProjectLabelByID(ctx context.Context, projectLabelID uuid.UUID) error
|
||||
DeleteRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) error
|
||||
DeleteRefreshTokenByUserID(ctx context.Context, userID uuid.UUID) error
|
||||
@ -34,6 +36,7 @@ type Querier interface {
|
||||
DeleteTaskLabelForTaskByProjectLabelID(ctx context.Context, arg DeleteTaskLabelForTaskByProjectLabelIDParams) error
|
||||
DeleteTasksByTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) (int64, error)
|
||||
DeleteTeamByID(ctx context.Context, teamID uuid.UUID) error
|
||||
DeleteTeamMemberByUserID(ctx context.Context, userID uuid.UUID) error
|
||||
GetAllOrganizations(ctx context.Context) ([]Organization, error)
|
||||
GetAllProjects(ctx context.Context) ([]Project, error)
|
||||
GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error)
|
||||
@ -59,6 +62,7 @@ type Querier interface {
|
||||
GetTaskLabelsForTaskID(ctx context.Context, taskID uuid.UUID) ([]TaskLabel, error)
|
||||
GetTasksForTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) ([]Task, error)
|
||||
GetTeamByID(ctx context.Context, teamID uuid.UUID) (Team, error)
|
||||
GetTeamMembersForTeamID(ctx context.Context, teamID uuid.UUID) ([]TeamMember, error)
|
||||
GetTeamsForOrganization(ctx context.Context, organizationID uuid.UUID) ([]Team, error)
|
||||
GetUserAccountByID(ctx context.Context, userID uuid.UUID) (UserAccount, error)
|
||||
GetUserAccountByUsername(ctx context.Context, username string) (UserAccount, error)
|
||||
|
75
api/pg/team_member.sql.go
Normal file
75
api/pg/team_member.sql.go
Normal file
@ -0,0 +1,75 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// source: team_member.sql
|
||||
|
||||
package pg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const createTeamMember = `-- name: CreateTeamMember :one
|
||||
INSERT INTO team_member (team_id, user_id, addedDate) VALUES ($1, $2, $3)
|
||||
RETURNING team_member_id, team_id, user_id, addeddate
|
||||
`
|
||||
|
||||
type CreateTeamMemberParams struct {
|
||||
TeamID uuid.UUID `json:"team_id"`
|
||||
UserID uuid.UUID `json:"user_id"`
|
||||
Addeddate time.Time `json:"addeddate"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateTeamMember(ctx context.Context, arg CreateTeamMemberParams) (TeamMember, error) {
|
||||
row := q.db.QueryRowContext(ctx, createTeamMember, arg.TeamID, arg.UserID, arg.Addeddate)
|
||||
var i TeamMember
|
||||
err := row.Scan(
|
||||
&i.TeamMemberID,
|
||||
&i.TeamID,
|
||||
&i.UserID,
|
||||
&i.Addeddate,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteTeamMemberByUserID = `-- name: DeleteTeamMemberByUserID :exec
|
||||
DELETE FROM team_member WHERE user_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteTeamMemberByUserID(ctx context.Context, userID uuid.UUID) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteTeamMemberByUserID, userID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getTeamMembersForTeamID = `-- name: GetTeamMembersForTeamID :many
|
||||
SELECT team_member_id, team_id, user_id, addeddate FROM team_member WHERE team_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetTeamMembersForTeamID(ctx context.Context, teamID uuid.UUID) ([]TeamMember, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getTeamMembersForTeamID, teamID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []TeamMember
|
||||
for rows.Next() {
|
||||
var i TeamMember
|
||||
if err := rows.Scan(
|
||||
&i.TeamMemberID,
|
||||
&i.TeamID,
|
||||
&i.UserID,
|
||||
&i.Addeddate,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
@ -12,3 +12,6 @@ INSERT INTO project(owner, team_id, created_at, name) VALUES ($1, $2, $3, $4) RE
|
||||
|
||||
-- name: UpdateProjectNameByID :one
|
||||
UPDATE project SET name = $2 WHERE project_id = $1 RETURNING *;
|
||||
|
||||
-- name: DeleteProjectByID :exec
|
||||
DELETE FROM project WHERE project_id = $1;
|
||||
|
9
api/query/team_member.sql
Normal file
9
api/query/team_member.sql
Normal file
@ -0,0 +1,9 @@
|
||||
-- name: CreateTeamMember :one
|
||||
INSERT INTO team_member (team_id, user_id, addedDate) VALUES ($1, $2, $3)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetTeamMembersForTeamID :many
|
||||
SELECT * FROM team_member WHERE team_id = $1;
|
||||
|
||||
-- name: DeleteTeamMemberByUserID :exec
|
||||
DELETE FROM team_member WHERE user_id = $1;
|
Reference in New Issue
Block a user