feature: add team creation & project deletion

This commit is contained in:
Jordan Knott
2020-06-20 17:49:11 -05:00
parent 5c3afaba7c
commit fd7c006b73
27 changed files with 1590 additions and 98 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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 {

View File

@ -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!

View File

@ -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 }

View 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
);

View File

@ -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;

View File

@ -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;

View File

@ -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"`

View File

@ -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)

View File

@ -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
`

View File

@ -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
View 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
}

View File

@ -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;

View 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;