feature: add ability to assign tasks
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,7 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
@ -10,6 +11,7 @@ import (
|
||||
"github.com/99designs/gqlgen/graphql/handler/lru"
|
||||
"github.com/99designs/gqlgen/graphql/handler/transport"
|
||||
"github.com/99designs/gqlgen/graphql/playground"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jordanknott/project-citadel/api/pg"
|
||||
)
|
||||
|
||||
@ -45,3 +47,7 @@ func NewHandler(repo pg.Repository) http.Handler {
|
||||
func NewPlaygroundHandler(endpoint string) http.Handler {
|
||||
return playground.Handler("GraphQL Playground", endpoint)
|
||||
}
|
||||
func GetUserID(ctx context.Context) (uuid.UUID, bool) {
|
||||
userID, ok := ctx.Value("userID").(uuid.UUID)
|
||||
return userID, ok
|
||||
}
|
||||
|
@ -7,6 +7,16 @@ import (
|
||||
"github.com/jordanknott/project-citadel/api/pg"
|
||||
)
|
||||
|
||||
type AddTaskLabelInput struct {
|
||||
TaskID uuid.UUID `json:"taskID"`
|
||||
LabelColorID uuid.UUID `json:"labelColorID"`
|
||||
}
|
||||
|
||||
type AssignTaskInput struct {
|
||||
TaskID uuid.UUID `json:"taskID"`
|
||||
UserID uuid.UUID `json:"userID"`
|
||||
}
|
||||
|
||||
type DeleteTaskGroupInput struct {
|
||||
TaskGroupID uuid.UUID `json:"taskGroupID"`
|
||||
}
|
||||
@ -29,6 +39,10 @@ type FindProject struct {
|
||||
ProjectID string `json:"projectId"`
|
||||
}
|
||||
|
||||
type FindTask struct {
|
||||
TaskID uuid.UUID `json:"taskID"`
|
||||
}
|
||||
|
||||
type FindUser struct {
|
||||
UserID string `json:"userId"`
|
||||
}
|
||||
@ -37,13 +51,10 @@ type LogoutUser struct {
|
||||
UserID string `json:"userID"`
|
||||
}
|
||||
|
||||
type NewOrganization struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type NewProject struct {
|
||||
TeamID string `json:"teamID"`
|
||||
Name string `json:"name"`
|
||||
UserID uuid.UUID `json:"userID"`
|
||||
TeamID uuid.UUID `json:"teamID"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type NewRefreshToken struct {
|
||||
@ -79,16 +90,39 @@ type NewTeam struct {
|
||||
}
|
||||
|
||||
type NewUserAccount struct {
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Password string `json:"password"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
FirstName string `json:"firstName"`
|
||||
LastName string `json:"lastName"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type ProfileIcon struct {
|
||||
URL *string `json:"url"`
|
||||
Initials *string `json:"initials"`
|
||||
}
|
||||
|
||||
type ProjectMember struct {
|
||||
UserID uuid.UUID `json:"userID"`
|
||||
FirstName string `json:"firstName"`
|
||||
LastName string `json:"lastName"`
|
||||
ProfileIcon *ProfileIcon `json:"profileIcon"`
|
||||
}
|
||||
|
||||
type ProjectsFilter struct {
|
||||
TeamID *string `json:"teamID"`
|
||||
}
|
||||
|
||||
type RemoveTaskLabelInput struct {
|
||||
TaskID uuid.UUID `json:"taskID"`
|
||||
TaskLabelID uuid.UUID `json:"taskLabelID"`
|
||||
}
|
||||
|
||||
type UpdateTaskDescriptionInput struct {
|
||||
TaskID uuid.UUID `json:"taskID"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type UpdateTaskName struct {
|
||||
TaskID string `json:"taskID"`
|
||||
Name string `json:"name"`
|
||||
|
@ -1,7 +1,24 @@
|
||||
scalar Time
|
||||
|
||||
scalar UUID
|
||||
|
||||
type TaskLabel {
|
||||
taskLabelID: ID!
|
||||
labelColorID: UUID!
|
||||
colorHex: String!
|
||||
}
|
||||
|
||||
type ProfileIcon {
|
||||
url: String
|
||||
initials: String
|
||||
}
|
||||
|
||||
type ProjectMember {
|
||||
userID: ID!
|
||||
firstName: String!
|
||||
lastName: String!
|
||||
profileIcon: ProfileIcon!
|
||||
}
|
||||
|
||||
type RefreshToken {
|
||||
tokenId: ID!
|
||||
userId: UUID!
|
||||
@ -13,30 +30,26 @@ type UserAccount {
|
||||
userID: ID!
|
||||
email: String!
|
||||
createdAt: Time!
|
||||
displayName: String!
|
||||
firstName: String!
|
||||
lastName: String!
|
||||
username: String!
|
||||
}
|
||||
|
||||
type Organization {
|
||||
organizationID: ID!
|
||||
createdAt: Time!
|
||||
name: String!
|
||||
teams: [Team!]!
|
||||
profileIcon: ProfileIcon!
|
||||
}
|
||||
|
||||
type Team {
|
||||
teamID: ID!
|
||||
createdAt: Time!
|
||||
name: String!
|
||||
projects: [Project!]!
|
||||
}
|
||||
|
||||
type Project {
|
||||
projectID: ID!
|
||||
teamID: String!
|
||||
createdAt: Time!
|
||||
name: String!
|
||||
team: Team!
|
||||
owner: ProjectMember!
|
||||
taskGroups: [TaskGroup!]!
|
||||
members: [ProjectMember!]!
|
||||
}
|
||||
|
||||
type TaskGroup {
|
||||
@ -54,6 +67,9 @@ type Task {
|
||||
createdAt: Time!
|
||||
name: String!
|
||||
position: Float!
|
||||
description: String
|
||||
assigned: [ProjectMember!]!
|
||||
labels: [TaskLabel!]!
|
||||
}
|
||||
|
||||
input ProjectsFilter {
|
||||
@ -68,14 +84,18 @@ input FindProject {
|
||||
projectId: String!
|
||||
}
|
||||
|
||||
input FindTask {
|
||||
taskID: UUID!
|
||||
}
|
||||
|
||||
type Query {
|
||||
organizations: [Organization!]!
|
||||
users: [UserAccount!]!
|
||||
findUser(input: FindUser!): UserAccount!
|
||||
findProject(input: FindProject!): Project!
|
||||
teams: [Team!]!
|
||||
findTask(input: FindTask!): Task!
|
||||
projects(input: ProjectsFilter): [Project!]!
|
||||
taskGroups: [TaskGroup!]!
|
||||
me: UserAccount!
|
||||
}
|
||||
|
||||
input NewRefreshToken {
|
||||
@ -85,7 +105,8 @@ input NewRefreshToken {
|
||||
input NewUserAccount {
|
||||
username: String!
|
||||
email: String!
|
||||
displayName: String!
|
||||
firstName: String!
|
||||
lastName: String!
|
||||
password: String!
|
||||
}
|
||||
|
||||
@ -95,7 +116,8 @@ input NewTeam {
|
||||
}
|
||||
|
||||
input NewProject {
|
||||
teamID: String!
|
||||
userID: UUID!
|
||||
teamID: UUID!
|
||||
name: String!
|
||||
}
|
||||
|
||||
@ -105,10 +127,6 @@ input NewTaskGroup {
|
||||
position: Float!
|
||||
}
|
||||
|
||||
input NewOrganization {
|
||||
name: String!
|
||||
}
|
||||
|
||||
input LogoutUser {
|
||||
userID: String!
|
||||
}
|
||||
@ -151,13 +169,31 @@ type DeleteTaskGroupPayload {
|
||||
taskGroup: TaskGroup!
|
||||
}
|
||||
|
||||
input AssignTaskInput {
|
||||
taskID: UUID!
|
||||
userID: UUID!
|
||||
}
|
||||
|
||||
input UpdateTaskDescriptionInput {
|
||||
taskID: UUID!
|
||||
description: String!
|
||||
}
|
||||
|
||||
input AddTaskLabelInput {
|
||||
taskID: UUID!
|
||||
labelColorID: UUID!
|
||||
}
|
||||
|
||||
input RemoveTaskLabelInput {
|
||||
taskID: UUID!
|
||||
taskLabelID: UUID!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createRefreshToken(input: NewRefreshToken!): RefreshToken!
|
||||
|
||||
createUserAccount(input: NewUserAccount!): UserAccount!
|
||||
|
||||
createOrganization(input: NewOrganization!): Organization!
|
||||
|
||||
createTeam(input: NewTeam!): Team!
|
||||
|
||||
createProject(input: NewProject!): Project!
|
||||
@ -166,10 +202,15 @@ type Mutation {
|
||||
updateTaskGroupLocation(input: NewTaskGroupLocation!): TaskGroup!
|
||||
deleteTaskGroup(input: DeleteTaskGroupInput!): DeleteTaskGroupPayload!
|
||||
|
||||
addTaskLabel(input: AddTaskLabelInput): Task!
|
||||
removeTaskLabel(input: RemoveTaskLabelInput): Task!
|
||||
|
||||
createTask(input: NewTask!): Task!
|
||||
updateTaskDescription(input: UpdateTaskDescriptionInput!): Task!
|
||||
updateTaskLocation(input: NewTaskLocation!): Task!
|
||||
updateTaskName(input: UpdateTaskName!): Task!
|
||||
deleteTask(input: DeleteTaskInput!): DeleteTaskPayload!
|
||||
assignTask(input: AssignTaskInput): Task!
|
||||
|
||||
logoutUser(input: LogoutUser!): Boolean!
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ package graph
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@ -24,16 +25,10 @@ func (r *mutationResolver) CreateRefreshToken(ctx context.Context, input NewRefr
|
||||
|
||||
func (r *mutationResolver) CreateUserAccount(ctx context.Context, input NewUserAccount) (*pg.UserAccount, error) {
|
||||
createdAt := time.Now().UTC()
|
||||
userAccount, err := r.Repository.CreateUserAccount(ctx, pg.CreateUserAccountParams{input.Username, input.Email, input.DisplayName, createdAt, input.Password})
|
||||
userAccount, err := r.Repository.CreateUserAccount(ctx, pg.CreateUserAccountParams{input.FirstName, input.LastName, input.Email, input.Username, createdAt, input.Password})
|
||||
return &userAccount, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) CreateOrganization(ctx context.Context, input NewOrganization) (*pg.Organization, error) {
|
||||
createdAt := time.Now().UTC()
|
||||
organization, err := r.Repository.CreateOrganization(ctx, pg.CreateOrganizationParams{createdAt, input.Name})
|
||||
return &organization, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) CreateTeam(ctx context.Context, input NewTeam) (*pg.Team, error) {
|
||||
organizationID, err := uuid.Parse(input.OrganizationID)
|
||||
if err != nil {
|
||||
@ -46,11 +41,7 @@ func (r *mutationResolver) CreateTeam(ctx context.Context, input NewTeam) (*pg.T
|
||||
|
||||
func (r *mutationResolver) CreateProject(ctx context.Context, input NewProject) (*pg.Project, error) {
|
||||
createdAt := time.Now().UTC()
|
||||
teamID, err := uuid.Parse(input.TeamID)
|
||||
if err != nil {
|
||||
return &pg.Project{}, err
|
||||
}
|
||||
project, err := r.Repository.CreateProject(ctx, pg.CreateProjectParams{teamID, createdAt, input.Name})
|
||||
project, err := r.Repository.CreateProject(ctx, pg.CreateProjectParams{input.UserID, input.TeamID, createdAt, input.Name})
|
||||
return &project, err
|
||||
}
|
||||
|
||||
@ -89,6 +80,20 @@ func (r *mutationResolver) DeleteTaskGroup(ctx context.Context, input DeleteTask
|
||||
return &DeleteTaskGroupPayload{true, int(deletedTasks + deletedTaskGroups), &taskGroup}, nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) AddTaskLabel(ctx context.Context, input *AddTaskLabelInput) (*pg.Task, error) {
|
||||
assignedDate := time.Now().UTC()
|
||||
_, err := r.Repository.CreateTaskLabelForTask(ctx, pg.CreateTaskLabelForTaskParams{input.TaskID, input.LabelColorID, assignedDate})
|
||||
if err != nil {
|
||||
return &pg.Task{}, err
|
||||
}
|
||||
task, err := r.Repository.GetTaskByID(ctx, input.TaskID)
|
||||
return &task, nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) RemoveTaskLabel(ctx context.Context, input *RemoveTaskLabelInput) (*pg.Task, error) {
|
||||
panic(fmt.Errorf("not implemented"))
|
||||
}
|
||||
|
||||
func (r *mutationResolver) CreateTask(ctx context.Context, input NewTask) (*pg.Task, error) {
|
||||
taskGroupID, err := uuid.Parse(input.TaskGroupID)
|
||||
createdAt := time.Now().UTC()
|
||||
@ -100,6 +105,11 @@ func (r *mutationResolver) CreateTask(ctx context.Context, input NewTask) (*pg.T
|
||||
return &task, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) UpdateTaskDescription(ctx context.Context, input UpdateTaskDescriptionInput) (*pg.Task, error) {
|
||||
task, err := r.Repository.UpdateTaskDescription(ctx, pg.UpdateTaskDescriptionParams{input.TaskID, sql.NullString{String: input.Description, Valid: true}})
|
||||
return &task, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) UpdateTaskLocation(ctx context.Context, input NewTaskLocation) (*pg.Task, error) {
|
||||
taskID, err := uuid.Parse(input.TaskID)
|
||||
if err != nil {
|
||||
@ -139,6 +149,21 @@ func (r *mutationResolver) DeleteTask(ctx context.Context, input DeleteTaskInput
|
||||
return &DeleteTaskPayload{taskID.String()}, nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) AssignTask(ctx context.Context, input *AssignTaskInput) (*pg.Task, error) {
|
||||
assignedDate := time.Now().UTC()
|
||||
assignedTask, err := r.Repository.CreateTaskAssigned(ctx, pg.CreateTaskAssignedParams{input.TaskID, input.UserID, assignedDate})
|
||||
log.WithFields(log.Fields{
|
||||
"userID": assignedTask.UserID,
|
||||
"taskID": assignedTask.TaskID,
|
||||
"assignedTaskID": assignedTask.TaskAssignedID,
|
||||
}).Info("assigned task")
|
||||
if err != nil {
|
||||
return &pg.Task{}, err
|
||||
}
|
||||
task, err := r.Repository.GetTaskByID(ctx, input.TaskID)
|
||||
return &task, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) LogoutUser(ctx context.Context, input LogoutUser) (bool, error) {
|
||||
userID, err := uuid.Parse(input.UserID)
|
||||
if err != nil {
|
||||
@ -149,21 +174,35 @@ func (r *mutationResolver) LogoutUser(ctx context.Context, input LogoutUser) (bo
|
||||
return true, err
|
||||
}
|
||||
|
||||
func (r *organizationResolver) Teams(ctx context.Context, obj *pg.Organization) ([]pg.Team, error) {
|
||||
teams, err := r.Repository.GetTeamsForOrganization(ctx, obj.OrganizationID)
|
||||
return teams, err
|
||||
func (r *projectResolver) Team(ctx context.Context, obj *pg.Project) (*pg.Team, error) {
|
||||
team, err := r.Repository.GetTeamByID(ctx, obj.TeamID)
|
||||
return &team, err
|
||||
}
|
||||
|
||||
func (r *projectResolver) TeamID(ctx context.Context, obj *pg.Project) (string, error) {
|
||||
return obj.TeamID.String(), nil
|
||||
func (r *projectResolver) Owner(ctx context.Context, obj *pg.Project) (*ProjectMember, error) {
|
||||
user, err := r.Repository.GetUserAccountByID(ctx, obj.Owner)
|
||||
if err != nil {
|
||||
return &ProjectMember{}, err
|
||||
}
|
||||
initials := string([]rune(user.FirstName)[0]) + string([]rune(user.LastName)[0])
|
||||
profileIcon := &ProfileIcon{nil, &initials}
|
||||
return &ProjectMember{obj.Owner, user.FirstName, user.LastName, profileIcon}, nil
|
||||
}
|
||||
|
||||
func (r *projectResolver) TaskGroups(ctx context.Context, obj *pg.Project) ([]pg.TaskGroup, error) {
|
||||
return r.Repository.GetTaskGroupsForProject(ctx, obj.ProjectID)
|
||||
}
|
||||
|
||||
func (r *queryResolver) Organizations(ctx context.Context) ([]pg.Organization, error) {
|
||||
return r.Repository.GetAllOrganizations(ctx)
|
||||
func (r *projectResolver) Members(ctx context.Context, obj *pg.Project) ([]ProjectMember, error) {
|
||||
user, err := r.Repository.GetUserAccountByID(ctx, obj.Owner)
|
||||
members := []ProjectMember{}
|
||||
if err != nil {
|
||||
return members, err
|
||||
}
|
||||
initials := string([]rune(user.FirstName)[0]) + string([]rune(user.LastName)[0])
|
||||
profileIcon := &ProfileIcon{nil, &initials}
|
||||
members = append(members, ProjectMember{obj.Owner, user.FirstName, user.LastName, profileIcon})
|
||||
return members, nil
|
||||
}
|
||||
|
||||
func (r *queryResolver) Users(ctx context.Context) ([]pg.UserAccount, error) {
|
||||
@ -204,8 +243,9 @@ func (r *queryResolver) FindProject(ctx context.Context, input FindProject) (*pg
|
||||
return &project, err
|
||||
}
|
||||
|
||||
func (r *queryResolver) Teams(ctx context.Context) ([]pg.Team, error) {
|
||||
return r.Repository.GetAllTeams(ctx)
|
||||
func (r *queryResolver) FindTask(ctx context.Context, input FindTask) (*pg.Task, error) {
|
||||
task, err := r.Repository.GetTaskByID(ctx, input.TaskID)
|
||||
return &task, err
|
||||
}
|
||||
|
||||
func (r *queryResolver) Projects(ctx context.Context, input *ProjectsFilter) ([]pg.Project, error) {
|
||||
@ -223,11 +263,59 @@ func (r *queryResolver) TaskGroups(ctx context.Context) ([]pg.TaskGroup, error)
|
||||
return r.Repository.GetAllTaskGroups(ctx)
|
||||
}
|
||||
|
||||
func (r *queryResolver) Me(ctx context.Context) (*pg.UserAccount, error) {
|
||||
userID, ok := GetUserID(ctx)
|
||||
if !ok {
|
||||
return &pg.UserAccount{}, fmt.Errorf("internal server error")
|
||||
}
|
||||
log.WithFields(log.Fields{
|
||||
"userID": userID,
|
||||
}).Info("getting user account")
|
||||
user, err := r.Repository.GetUserAccountByID(ctx, userID)
|
||||
if err != nil {
|
||||
return &pg.UserAccount{}, err
|
||||
}
|
||||
return &user, err
|
||||
}
|
||||
|
||||
func (r *taskResolver) TaskGroup(ctx context.Context, obj *pg.Task) (*pg.TaskGroup, error) {
|
||||
taskGroup, err := r.Repository.GetTaskGroupByID(ctx, obj.TaskGroupID)
|
||||
return &taskGroup, err
|
||||
}
|
||||
|
||||
func (r *taskResolver) Description(ctx context.Context, obj *pg.Task) (*string, error) {
|
||||
task, err := r.Repository.GetTaskByID(ctx, obj.TaskID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !task.Description.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return &task.Description.String, nil
|
||||
}
|
||||
|
||||
func (r *taskResolver) Assigned(ctx context.Context, obj *pg.Task) ([]ProjectMember, error) {
|
||||
taskMemberLinks, err := r.Repository.GetAssignedMembersForTask(ctx, obj.TaskID)
|
||||
taskMembers := []ProjectMember{}
|
||||
if err != nil {
|
||||
return taskMembers, err
|
||||
}
|
||||
for _, taskMemberLink := range taskMemberLinks {
|
||||
user, err := r.Repository.GetUserAccountByID(ctx, taskMemberLink.UserID)
|
||||
if err != nil {
|
||||
return taskMembers, err
|
||||
}
|
||||
initials := string([]rune(user.FirstName)[0]) + string([]rune(user.LastName)[0])
|
||||
profileIcon := &ProfileIcon{nil, &initials}
|
||||
taskMembers = append(taskMembers, ProjectMember{taskMemberLink.UserID, user.FirstName, user.LastName, profileIcon})
|
||||
}
|
||||
return taskMembers, nil
|
||||
}
|
||||
|
||||
func (r *taskResolver) Labels(ctx context.Context, obj *pg.Task) ([]pg.TaskLabel, error) {
|
||||
return r.Repository.GetTaskLabelsForTaskID(ctx, obj.TaskID)
|
||||
}
|
||||
|
||||
func (r *taskGroupResolver) ProjectID(ctx context.Context, obj *pg.TaskGroup) (string, error) {
|
||||
return obj.ProjectID.String(), nil
|
||||
}
|
||||
@ -237,16 +325,23 @@ func (r *taskGroupResolver) Tasks(ctx context.Context, obj *pg.TaskGroup) ([]pg.
|
||||
return tasks, err
|
||||
}
|
||||
|
||||
func (r *teamResolver) Projects(ctx context.Context, obj *pg.Team) ([]pg.Project, error) {
|
||||
return r.Repository.GetAllProjectsForTeam(ctx, obj.TeamID)
|
||||
func (r *taskLabelResolver) ColorHex(ctx context.Context, obj *pg.TaskLabel) (string, error) {
|
||||
labelColor, err := r.Repository.GetLabelColorByID(ctx, obj.LabelColorID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return labelColor.ColorHex, nil
|
||||
}
|
||||
|
||||
func (r *userAccountResolver) ProfileIcon(ctx context.Context, obj *pg.UserAccount) (*ProfileIcon, error) {
|
||||
initials := string([]rune(obj.FirstName)[0]) + string([]rune(obj.LastName)[0])
|
||||
profileIcon := &ProfileIcon{nil, &initials}
|
||||
return profileIcon, nil
|
||||
}
|
||||
|
||||
// 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} }
|
||||
|
||||
@ -259,13 +354,26 @@ func (r *Resolver) Task() TaskResolver { return &taskResolver{r} }
|
||||
// TaskGroup returns TaskGroupResolver implementation.
|
||||
func (r *Resolver) TaskGroup() TaskGroupResolver { return &taskGroupResolver{r} }
|
||||
|
||||
// Team returns TeamResolver implementation.
|
||||
func (r *Resolver) Team() TeamResolver { return &teamResolver{r} }
|
||||
// TaskLabel returns TaskLabelResolver implementation.
|
||||
func (r *Resolver) TaskLabel() TaskLabelResolver { return &taskLabelResolver{r} }
|
||||
|
||||
// UserAccount returns UserAccountResolver implementation.
|
||||
func (r *Resolver) UserAccount() UserAccountResolver { return &userAccountResolver{r} }
|
||||
|
||||
type mutationResolver struct{ *Resolver }
|
||||
type organizationResolver struct{ *Resolver }
|
||||
type projectResolver struct{ *Resolver }
|
||||
type queryResolver struct{ *Resolver }
|
||||
type taskResolver struct{ *Resolver }
|
||||
type taskGroupResolver struct{ *Resolver }
|
||||
type teamResolver struct{ *Resolver }
|
||||
type taskLabelResolver struct{ *Resolver }
|
||||
type userAccountResolver struct{ *Resolver }
|
||||
|
||||
// !!! WARNING !!!
|
||||
// The code below was going to be deleted when updating resolvers. It has been copied here so you have
|
||||
// one last chance to move it out of harms way if you want. There are two reasons this happens:
|
||||
// - When renaming or deleting a resolver the old code will be put in here. You can safely delete
|
||||
// it when you're done.
|
||||
// - You have helper methods in this file. Move them out to keep these resolver files clean.
|
||||
func (r *userAccountResolver) DisplayName(ctx context.Context, obj *pg.UserAccount) (string, error) {
|
||||
return obj.FirstName + " " + obj.LastName, nil
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
CREATE TABLE user_account (
|
||||
user_id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
created_at timestamptz NOT NULL,
|
||||
display_name text NOT NULL,
|
||||
first_name text NOT NULL,
|
||||
last_name text NOT NULL,
|
||||
email text NOT NULL UNIQUE,
|
||||
username text NOT NULL UNIQUE,
|
||||
password_hash text NOT NULL
|
6
api/migrations/0009_add-task-assigned-table.up.sql
Normal file
6
api/migrations/0009_add-task-assigned-table.up.sql
Normal file
@ -0,0 +1,6 @@
|
||||
CREATE TABLE task_assigned (
|
||||
task_assigned_id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
task_id uuid NOT NULL REFERENCES task(task_id),
|
||||
user_id uuid NOT NULL REFERENCES user_account(user_id),
|
||||
assigned_date timestamptz NOT NULL
|
||||
);
|
1
api/migrations/0010_add-description-to-task-table.up.sql
Normal file
1
api/migrations/0010_add-description-to-task-table.up.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE task ADD COLUMN description text;
|
5
api/migrations/0011_add-label-color-table.up.sql
Normal file
5
api/migrations/0011_add-label-color-table.up.sql
Normal file
@ -0,0 +1,5 @@
|
||||
CREATE TABLE label_color (
|
||||
label_color_id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
color_hex TEXT NOT NULL,
|
||||
position FLOAT NOT NULL
|
||||
);
|
6
api/migrations/0012-add-task-label-table.up.sql
Normal file
6
api/migrations/0012-add-task-label-table.up.sql
Normal file
@ -0,0 +1,6 @@
|
||||
CREATE TABLE task_label (
|
||||
task_label_id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
task_id uuid NOT NULL REFERENCES task(task_id),
|
||||
label_color_id uuid NOT NULL REFERENCES label_color(label_color_id),
|
||||
assigned_date timestamptz NOT NULL
|
||||
);
|
1
api/migrations/0013_add-due-date-to-task-table.up.sql
Normal file
1
api/migrations/0013_add-due-date-to-task-table.up.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE task ADD COLUMN due_date timestamptz;
|
@ -0,0 +1 @@
|
||||
ALTER TABLE project ADD COLUMN owner uuid NOT NULL;
|
21
api/pg/label_color.sql.go
Normal file
21
api/pg/label_color.sql.go
Normal file
@ -0,0 +1,21 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// source: label_color.sql
|
||||
|
||||
package pg
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const getLabelColorByID = `-- name: GetLabelColorByID :one
|
||||
SELECT label_color_id, color_hex, position FROM label_color WHERE label_color_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetLabelColorByID(ctx context.Context, labelColorID uuid.UUID) (LabelColor, error) {
|
||||
row := q.db.QueryRowContext(ctx, getLabelColorByID, labelColorID)
|
||||
var i LabelColor
|
||||
err := row.Scan(&i.LabelColorID, &i.ColorHex, &i.Position)
|
||||
return i, err
|
||||
}
|
@ -3,11 +3,18 @@
|
||||
package pg
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type LabelColor struct {
|
||||
LabelColorID uuid.UUID `json:"label_color_id"`
|
||||
ColorHex string `json:"color_hex"`
|
||||
Position float64 `json:"position"`
|
||||
}
|
||||
|
||||
type Organization struct {
|
||||
OrganizationID uuid.UUID `json:"organization_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
@ -19,6 +26,7 @@ type Project struct {
|
||||
TeamID uuid.UUID `json:"team_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Name string `json:"name"`
|
||||
Owner uuid.UUID `json:"owner"`
|
||||
}
|
||||
|
||||
type RefreshToken struct {
|
||||
@ -29,11 +37,20 @@ type RefreshToken struct {
|
||||
}
|
||||
|
||||
type Task struct {
|
||||
TaskID uuid.UUID `json:"task_id"`
|
||||
TaskGroupID uuid.UUID `json:"task_group_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Name string `json:"name"`
|
||||
Position float64 `json:"position"`
|
||||
TaskID uuid.UUID `json:"task_id"`
|
||||
TaskGroupID uuid.UUID `json:"task_group_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Name string `json:"name"`
|
||||
Position float64 `json:"position"`
|
||||
Description sql.NullString `json:"description"`
|
||||
DueDate sql.NullTime `json:"due_date"`
|
||||
}
|
||||
|
||||
type TaskAssigned struct {
|
||||
TaskAssignedID uuid.UUID `json:"task_assigned_id"`
|
||||
TaskID uuid.UUID `json:"task_id"`
|
||||
UserID uuid.UUID `json:"user_id"`
|
||||
AssignedDate time.Time `json:"assigned_date"`
|
||||
}
|
||||
|
||||
type TaskGroup struct {
|
||||
@ -44,6 +61,13 @@ type TaskGroup struct {
|
||||
Position float64 `json:"position"`
|
||||
}
|
||||
|
||||
type TaskLabel struct {
|
||||
TaskLabelID uuid.UUID `json:"task_label_id"`
|
||||
TaskID uuid.UUID `json:"task_id"`
|
||||
LabelColorID uuid.UUID `json:"label_color_id"`
|
||||
AssignedDate time.Time `json:"assigned_date"`
|
||||
}
|
||||
|
||||
type Team struct {
|
||||
TeamID uuid.UUID `json:"team_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
@ -54,7 +78,8 @@ type Team struct {
|
||||
type UserAccount struct {
|
||||
UserID uuid.UUID `json:"user_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
DisplayName string `json:"display_name"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
Email string `json:"email"`
|
||||
Username string `json:"username"`
|
||||
PasswordHash string `json:"password_hash"`
|
||||
|
@ -41,11 +41,20 @@ type Repository interface {
|
||||
GetTeamsForOrganization(ctx context.Context, organizationID uuid.UUID) ([]Team, error)
|
||||
|
||||
CreateTask(ctx context.Context, arg CreateTaskParams) (Task, error)
|
||||
GetTaskByID(ctx context.Context, taskID uuid.UUID) (Task, error)
|
||||
GetAllTasks(ctx context.Context) ([]Task, error)
|
||||
GetTasksForTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) ([]Task, error)
|
||||
UpdateTaskLocation(ctx context.Context, arg UpdateTaskLocationParams) (Task, error)
|
||||
DeleteTaskByID(ctx context.Context, taskID uuid.UUID) error
|
||||
UpdateTaskName(ctx context.Context, arg UpdateTaskNameParams) (Task, error)
|
||||
UpdateTaskDescription(ctx context.Context, arg UpdateTaskDescriptionParams) (Task, error)
|
||||
|
||||
CreateTaskLabelForTask(ctx context.Context, arg CreateTaskLabelForTaskParams) (TaskLabel, error)
|
||||
GetTaskLabelsForTaskID(ctx context.Context, taskID uuid.UUID) ([]TaskLabel, error)
|
||||
GetLabelColorByID(ctx context.Context, labelColorID uuid.UUID) (LabelColor, error)
|
||||
|
||||
CreateTaskAssigned(ctx context.Context, arg CreateTaskAssignedParams) (TaskAssigned, error)
|
||||
GetAssignedMembersForTask(ctx context.Context, taskID uuid.UUID) ([]TaskAssigned, error)
|
||||
}
|
||||
|
||||
type repoSvc struct {
|
||||
|
@ -11,29 +11,36 @@ import (
|
||||
)
|
||||
|
||||
const createProject = `-- name: CreateProject :one
|
||||
INSERT INTO project(team_id, created_at, name) VALUES ($1, $2, $3) RETURNING project_id, team_id, created_at, name
|
||||
INSERT INTO project(owner, team_id, created_at, name) VALUES ($1, $2, $3, $4) RETURNING project_id, team_id, created_at, name, owner
|
||||
`
|
||||
|
||||
type CreateProjectParams struct {
|
||||
Owner uuid.UUID `json:"owner"`
|
||||
TeamID uuid.UUID `json:"team_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateProject(ctx context.Context, arg CreateProjectParams) (Project, error) {
|
||||
row := q.db.QueryRowContext(ctx, createProject, arg.TeamID, arg.CreatedAt, arg.Name)
|
||||
row := q.db.QueryRowContext(ctx, createProject,
|
||||
arg.Owner,
|
||||
arg.TeamID,
|
||||
arg.CreatedAt,
|
||||
arg.Name,
|
||||
)
|
||||
var i Project
|
||||
err := row.Scan(
|
||||
&i.ProjectID,
|
||||
&i.TeamID,
|
||||
&i.CreatedAt,
|
||||
&i.Name,
|
||||
&i.Owner,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getAllProjects = `-- name: GetAllProjects :many
|
||||
SELECT project_id, team_id, created_at, name FROM project
|
||||
SELECT project_id, team_id, created_at, name, owner FROM project
|
||||
`
|
||||
|
||||
func (q *Queries) GetAllProjects(ctx context.Context) ([]Project, error) {
|
||||
@ -50,6 +57,7 @@ func (q *Queries) GetAllProjects(ctx context.Context) ([]Project, error) {
|
||||
&i.TeamID,
|
||||
&i.CreatedAt,
|
||||
&i.Name,
|
||||
&i.Owner,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -65,7 +73,7 @@ func (q *Queries) GetAllProjects(ctx context.Context) ([]Project, error) {
|
||||
}
|
||||
|
||||
const getAllProjectsForTeam = `-- name: GetAllProjectsForTeam :many
|
||||
SELECT project_id, team_id, created_at, name FROM project WHERE team_id = $1
|
||||
SELECT project_id, team_id, created_at, name, owner FROM project WHERE team_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error) {
|
||||
@ -82,6 +90,7 @@ func (q *Queries) GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) (
|
||||
&i.TeamID,
|
||||
&i.CreatedAt,
|
||||
&i.Name,
|
||||
&i.Owner,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -97,7 +106,7 @@ func (q *Queries) GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) (
|
||||
}
|
||||
|
||||
const getProjectByID = `-- name: GetProjectByID :one
|
||||
SELECT project_id, team_id, created_at, name FROM project WHERE project_id = $1
|
||||
SELECT project_id, team_id, created_at, name, owner FROM project WHERE project_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetProjectByID(ctx context.Context, projectID uuid.UUID) (Project, error) {
|
||||
@ -108,6 +117,7 @@ func (q *Queries) GetProjectByID(ctx context.Context, projectID uuid.UUID) (Proj
|
||||
&i.TeamID,
|
||||
&i.CreatedAt,
|
||||
&i.Name,
|
||||
&i.Owner,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
@ -13,7 +13,9 @@ type Querier interface {
|
||||
CreateProject(ctx context.Context, arg CreateProjectParams) (Project, error)
|
||||
CreateRefreshToken(ctx context.Context, arg CreateRefreshTokenParams) (RefreshToken, error)
|
||||
CreateTask(ctx context.Context, arg CreateTaskParams) (Task, error)
|
||||
CreateTaskAssigned(ctx context.Context, arg CreateTaskAssignedParams) (TaskAssigned, error)
|
||||
CreateTaskGroup(ctx context.Context, arg CreateTaskGroupParams) (TaskGroup, error)
|
||||
CreateTaskLabelForTask(ctx context.Context, arg CreateTaskLabelForTaskParams) (TaskLabel, error)
|
||||
CreateTeam(ctx context.Context, arg CreateTeamParams) (Team, error)
|
||||
CreateUserAccount(ctx context.Context, arg CreateUserAccountParams) (UserAccount, error)
|
||||
DeleteExpiredTokens(ctx context.Context) error
|
||||
@ -30,15 +32,20 @@ type Querier interface {
|
||||
GetAllTasks(ctx context.Context) ([]Task, error)
|
||||
GetAllTeams(ctx context.Context) ([]Team, error)
|
||||
GetAllUserAccounts(ctx context.Context) ([]UserAccount, error)
|
||||
GetAssignedMembersForTask(ctx context.Context, taskID uuid.UUID) ([]TaskAssigned, error)
|
||||
GetLabelColorByID(ctx context.Context, labelColorID uuid.UUID) (LabelColor, error)
|
||||
GetProjectByID(ctx context.Context, projectID uuid.UUID) (Project, error)
|
||||
GetRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) (RefreshToken, error)
|
||||
GetTaskByID(ctx context.Context, taskID uuid.UUID) (Task, error)
|
||||
GetTaskGroupByID(ctx context.Context, taskGroupID uuid.UUID) (TaskGroup, error)
|
||||
GetTaskGroupsForProject(ctx context.Context, projectID uuid.UUID) ([]TaskGroup, error)
|
||||
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)
|
||||
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)
|
||||
UpdateTaskDescription(ctx context.Context, arg UpdateTaskDescriptionParams) (Task, error)
|
||||
UpdateTaskGroupLocation(ctx context.Context, arg UpdateTaskGroupLocationParams) (TaskGroup, error)
|
||||
UpdateTaskLocation(ctx context.Context, arg UpdateTaskLocationParams) (Task, error)
|
||||
UpdateTaskName(ctx context.Context, arg UpdateTaskNameParams) (Task, error)
|
||||
|
@ -5,6 +5,7 @@ package pg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@ -12,7 +13,7 @@ import (
|
||||
|
||||
const createTask = `-- name: CreateTask :one
|
||||
INSERT INTO task (task_group_id, created_at, name, position)
|
||||
VALUES($1, $2, $3, $4) RETURNING task_id, task_group_id, created_at, name, position
|
||||
VALUES($1, $2, $3, $4) RETURNING task_id, task_group_id, created_at, name, position, description, due_date
|
||||
`
|
||||
|
||||
type CreateTaskParams struct {
|
||||
@ -36,6 +37,8 @@ func (q *Queries) CreateTask(ctx context.Context, arg CreateTaskParams) (Task, e
|
||||
&i.CreatedAt,
|
||||
&i.Name,
|
||||
&i.Position,
|
||||
&i.Description,
|
||||
&i.DueDate,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@ -62,7 +65,7 @@ func (q *Queries) DeleteTasksByTaskGroupID(ctx context.Context, taskGroupID uuid
|
||||
}
|
||||
|
||||
const getAllTasks = `-- name: GetAllTasks :many
|
||||
SELECT task_id, task_group_id, created_at, name, position FROM task
|
||||
SELECT task_id, task_group_id, created_at, name, position, description, due_date FROM task
|
||||
`
|
||||
|
||||
func (q *Queries) GetAllTasks(ctx context.Context) ([]Task, error) {
|
||||
@ -80,6 +83,8 @@ func (q *Queries) GetAllTasks(ctx context.Context) ([]Task, error) {
|
||||
&i.CreatedAt,
|
||||
&i.Name,
|
||||
&i.Position,
|
||||
&i.Description,
|
||||
&i.DueDate,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -94,8 +99,27 @@ func (q *Queries) GetAllTasks(ctx context.Context) ([]Task, error) {
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getTaskByID = `-- name: GetTaskByID :one
|
||||
SELECT task_id, task_group_id, created_at, name, position, description, due_date FROM task WHERE task_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetTaskByID(ctx context.Context, taskID uuid.UUID) (Task, error) {
|
||||
row := q.db.QueryRowContext(ctx, getTaskByID, taskID)
|
||||
var i Task
|
||||
err := row.Scan(
|
||||
&i.TaskID,
|
||||
&i.TaskGroupID,
|
||||
&i.CreatedAt,
|
||||
&i.Name,
|
||||
&i.Position,
|
||||
&i.Description,
|
||||
&i.DueDate,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getTasksForTaskGroupID = `-- name: GetTasksForTaskGroupID :many
|
||||
SELECT task_id, task_group_id, created_at, name, position FROM task WHERE task_group_id = $1
|
||||
SELECT task_id, task_group_id, created_at, name, position, description, due_date FROM task WHERE task_group_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetTasksForTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) ([]Task, error) {
|
||||
@ -113,6 +137,8 @@ func (q *Queries) GetTasksForTaskGroupID(ctx context.Context, taskGroupID uuid.U
|
||||
&i.CreatedAt,
|
||||
&i.Name,
|
||||
&i.Position,
|
||||
&i.Description,
|
||||
&i.DueDate,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -127,8 +153,32 @@ func (q *Queries) GetTasksForTaskGroupID(ctx context.Context, taskGroupID uuid.U
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateTaskDescription = `-- name: UpdateTaskDescription :one
|
||||
UPDATE task SET description = $2 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date
|
||||
`
|
||||
|
||||
type UpdateTaskDescriptionParams struct {
|
||||
TaskID uuid.UUID `json:"task_id"`
|
||||
Description sql.NullString `json:"description"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateTaskDescription(ctx context.Context, arg UpdateTaskDescriptionParams) (Task, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateTaskDescription, arg.TaskID, arg.Description)
|
||||
var i Task
|
||||
err := row.Scan(
|
||||
&i.TaskID,
|
||||
&i.TaskGroupID,
|
||||
&i.CreatedAt,
|
||||
&i.Name,
|
||||
&i.Position,
|
||||
&i.Description,
|
||||
&i.DueDate,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateTaskLocation = `-- name: UpdateTaskLocation :one
|
||||
UPDATE task SET task_group_id = $2, position = $3 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position
|
||||
UPDATE task SET task_group_id = $2, position = $3 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date
|
||||
`
|
||||
|
||||
type UpdateTaskLocationParams struct {
|
||||
@ -146,12 +196,14 @@ func (q *Queries) UpdateTaskLocation(ctx context.Context, arg UpdateTaskLocation
|
||||
&i.CreatedAt,
|
||||
&i.Name,
|
||||
&i.Position,
|
||||
&i.Description,
|
||||
&i.DueDate,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateTaskName = `-- name: UpdateTaskName :one
|
||||
UPDATE task SET name = $2 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position
|
||||
UPDATE task SET name = $2 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date
|
||||
`
|
||||
|
||||
type UpdateTaskNameParams struct {
|
||||
@ -168,6 +220,8 @@ func (q *Queries) UpdateTaskName(ctx context.Context, arg UpdateTaskNameParams)
|
||||
&i.CreatedAt,
|
||||
&i.Name,
|
||||
&i.Position,
|
||||
&i.Description,
|
||||
&i.DueDate,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
66
api/pg/task_assigned.sql.go
Normal file
66
api/pg/task_assigned.sql.go
Normal file
@ -0,0 +1,66 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// source: task_assigned.sql
|
||||
|
||||
package pg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const createTaskAssigned = `-- name: CreateTaskAssigned :one
|
||||
INSERT INTO task_assigned (task_id, user_id, assigned_date)
|
||||
VALUES($1, $2, $3) RETURNING task_assigned_id, task_id, user_id, assigned_date
|
||||
`
|
||||
|
||||
type CreateTaskAssignedParams struct {
|
||||
TaskID uuid.UUID `json:"task_id"`
|
||||
UserID uuid.UUID `json:"user_id"`
|
||||
AssignedDate time.Time `json:"assigned_date"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateTaskAssigned(ctx context.Context, arg CreateTaskAssignedParams) (TaskAssigned, error) {
|
||||
row := q.db.QueryRowContext(ctx, createTaskAssigned, arg.TaskID, arg.UserID, arg.AssignedDate)
|
||||
var i TaskAssigned
|
||||
err := row.Scan(
|
||||
&i.TaskAssignedID,
|
||||
&i.TaskID,
|
||||
&i.UserID,
|
||||
&i.AssignedDate,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getAssignedMembersForTask = `-- name: GetAssignedMembersForTask :many
|
||||
SELECT task_assigned_id, task_id, user_id, assigned_date FROM task_assigned WHERE task_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetAssignedMembersForTask(ctx context.Context, taskID uuid.UUID) ([]TaskAssigned, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getAssignedMembersForTask, taskID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []TaskAssigned
|
||||
for rows.Next() {
|
||||
var i TaskAssigned
|
||||
if err := rows.Scan(
|
||||
&i.TaskAssignedID,
|
||||
&i.TaskID,
|
||||
&i.UserID,
|
||||
&i.AssignedDate,
|
||||
); 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
|
||||
}
|
66
api/pg/task_label.sql.go
Normal file
66
api/pg/task_label.sql.go
Normal file
@ -0,0 +1,66 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// source: task_label.sql
|
||||
|
||||
package pg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const createTaskLabelForTask = `-- name: CreateTaskLabelForTask :one
|
||||
INSERT INTO task_label (task_id, label_color_id, assigned_date)
|
||||
VALUES ($1, $2, $3) RETURNING task_label_id, task_id, label_color_id, assigned_date
|
||||
`
|
||||
|
||||
type CreateTaskLabelForTaskParams struct {
|
||||
TaskID uuid.UUID `json:"task_id"`
|
||||
LabelColorID uuid.UUID `json:"label_color_id"`
|
||||
AssignedDate time.Time `json:"assigned_date"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateTaskLabelForTask(ctx context.Context, arg CreateTaskLabelForTaskParams) (TaskLabel, error) {
|
||||
row := q.db.QueryRowContext(ctx, createTaskLabelForTask, arg.TaskID, arg.LabelColorID, arg.AssignedDate)
|
||||
var i TaskLabel
|
||||
err := row.Scan(
|
||||
&i.TaskLabelID,
|
||||
&i.TaskID,
|
||||
&i.LabelColorID,
|
||||
&i.AssignedDate,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getTaskLabelsForTaskID = `-- name: GetTaskLabelsForTaskID :many
|
||||
SELECT task_label_id, task_id, label_color_id, assigned_date FROM task_label WHERE task_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetTaskLabelsForTaskID(ctx context.Context, taskID uuid.UUID) ([]TaskLabel, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getTaskLabelsForTaskID, taskID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []TaskLabel
|
||||
for rows.Next() {
|
||||
var i TaskLabel
|
||||
if err := rows.Scan(
|
||||
&i.TaskLabelID,
|
||||
&i.TaskID,
|
||||
&i.LabelColorID,
|
||||
&i.AssignedDate,
|
||||
); 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
|
||||
}
|
@ -11,13 +11,14 @@ import (
|
||||
)
|
||||
|
||||
const createUserAccount = `-- name: CreateUserAccount :one
|
||||
INSERT INTO user_account(display_name, email, username, created_at, password_hash)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING user_id, created_at, display_name, email, username, password_hash
|
||||
INSERT INTO user_account(first_name, last_name, email, username, created_at, password_hash)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING user_id, created_at, first_name, last_name, email, username, password_hash
|
||||
`
|
||||
|
||||
type CreateUserAccountParams struct {
|
||||
DisplayName string `json:"display_name"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
Email string `json:"email"`
|
||||
Username string `json:"username"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
@ -26,7 +27,8 @@ type CreateUserAccountParams struct {
|
||||
|
||||
func (q *Queries) CreateUserAccount(ctx context.Context, arg CreateUserAccountParams) (UserAccount, error) {
|
||||
row := q.db.QueryRowContext(ctx, createUserAccount,
|
||||
arg.DisplayName,
|
||||
arg.FirstName,
|
||||
arg.LastName,
|
||||
arg.Email,
|
||||
arg.Username,
|
||||
arg.CreatedAt,
|
||||
@ -36,7 +38,8 @@ func (q *Queries) CreateUserAccount(ctx context.Context, arg CreateUserAccountPa
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.CreatedAt,
|
||||
&i.DisplayName,
|
||||
&i.FirstName,
|
||||
&i.LastName,
|
||||
&i.Email,
|
||||
&i.Username,
|
||||
&i.PasswordHash,
|
||||
@ -45,7 +48,7 @@ func (q *Queries) CreateUserAccount(ctx context.Context, arg CreateUserAccountPa
|
||||
}
|
||||
|
||||
const getAllUserAccounts = `-- name: GetAllUserAccounts :many
|
||||
SELECT user_id, created_at, display_name, email, username, password_hash FROM user_account
|
||||
SELECT user_id, created_at, first_name, last_name, email, username, password_hash FROM user_account
|
||||
`
|
||||
|
||||
func (q *Queries) GetAllUserAccounts(ctx context.Context) ([]UserAccount, error) {
|
||||
@ -60,7 +63,8 @@ func (q *Queries) GetAllUserAccounts(ctx context.Context) ([]UserAccount, error)
|
||||
if err := rows.Scan(
|
||||
&i.UserID,
|
||||
&i.CreatedAt,
|
||||
&i.DisplayName,
|
||||
&i.FirstName,
|
||||
&i.LastName,
|
||||
&i.Email,
|
||||
&i.Username,
|
||||
&i.PasswordHash,
|
||||
@ -79,7 +83,7 @@ func (q *Queries) GetAllUserAccounts(ctx context.Context) ([]UserAccount, error)
|
||||
}
|
||||
|
||||
const getUserAccountByID = `-- name: GetUserAccountByID :one
|
||||
SELECT user_id, created_at, display_name, email, username, password_hash FROM user_account WHERE user_id = $1
|
||||
SELECT user_id, created_at, first_name, last_name, email, username, password_hash FROM user_account WHERE user_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserAccountByID(ctx context.Context, userID uuid.UUID) (UserAccount, error) {
|
||||
@ -88,7 +92,8 @@ func (q *Queries) GetUserAccountByID(ctx context.Context, userID uuid.UUID) (Use
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.CreatedAt,
|
||||
&i.DisplayName,
|
||||
&i.FirstName,
|
||||
&i.LastName,
|
||||
&i.Email,
|
||||
&i.Username,
|
||||
&i.PasswordHash,
|
||||
@ -97,7 +102,7 @@ func (q *Queries) GetUserAccountByID(ctx context.Context, userID uuid.UUID) (Use
|
||||
}
|
||||
|
||||
const getUserAccountByUsername = `-- name: GetUserAccountByUsername :one
|
||||
SELECT user_id, created_at, display_name, email, username, password_hash FROM user_account WHERE username = $1
|
||||
SELECT user_id, created_at, first_name, last_name, email, username, password_hash FROM user_account WHERE username = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserAccountByUsername(ctx context.Context, username string) (UserAccount, error) {
|
||||
@ -106,7 +111,8 @@ func (q *Queries) GetUserAccountByUsername(ctx context.Context, username string)
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.CreatedAt,
|
||||
&i.DisplayName,
|
||||
&i.FirstName,
|
||||
&i.LastName,
|
||||
&i.Email,
|
||||
&i.Username,
|
||||
&i.PasswordHash,
|
||||
|
2
api/query/label_color.sql
Normal file
2
api/query/label_color.sql
Normal file
@ -0,0 +1,2 @@
|
||||
-- name: GetLabelColorByID :one
|
||||
SELECT * FROM label_color WHERE label_color_id = $1;
|
@ -8,4 +8,4 @@ SELECT * FROM project WHERE team_id = $1;
|
||||
SELECT * FROM project WHERE project_id = $1;
|
||||
|
||||
-- name: CreateProject :one
|
||||
INSERT INTO project(team_id, created_at, name) VALUES ($1, $2, $3) RETURNING *;
|
||||
INSERT INTO project(owner, team_id, created_at, name) VALUES ($1, $2, $3, $4) RETURNING *;
|
||||
|
@ -2,6 +2,12 @@
|
||||
INSERT INTO task (task_group_id, created_at, name, position)
|
||||
VALUES($1, $2, $3, $4) RETURNING *;
|
||||
|
||||
-- name: UpdateTaskDescription :one
|
||||
UPDATE task SET description = $2 WHERE task_id = $1 RETURNING *;
|
||||
|
||||
-- name: GetTaskByID :one
|
||||
SELECT * FROM task WHERE task_id = $1;
|
||||
|
||||
-- name: GetTasksForTaskGroupID :many
|
||||
SELECT * FROM task WHERE task_group_id = $1;
|
||||
|
||||
|
6
api/query/task_assigned.sql
Normal file
6
api/query/task_assigned.sql
Normal file
@ -0,0 +1,6 @@
|
||||
-- name: CreateTaskAssigned :one
|
||||
INSERT INTO task_assigned (task_id, user_id, assigned_date)
|
||||
VALUES($1, $2, $3) RETURNING *;
|
||||
|
||||
-- name: GetAssignedMembersForTask :many
|
||||
SELECT * FROM task_assigned WHERE task_id = $1;
|
6
api/query/task_label.sql
Normal file
6
api/query/task_label.sql
Normal file
@ -0,0 +1,6 @@
|
||||
-- name: CreateTaskLabelForTask :one
|
||||
INSERT INTO task_label (task_id, label_color_id, assigned_date)
|
||||
VALUES ($1, $2, $3) RETURNING *;
|
||||
|
||||
-- name: GetTaskLabelsForTaskID :many
|
||||
SELECT * FROM task_label WHERE task_id = $1;
|
@ -8,6 +8,6 @@ SELECT * FROM user_account;
|
||||
SELECT * FROM user_account WHERE username = $1;
|
||||
|
||||
-- name: CreateUserAccount :one
|
||||
INSERT INTO user_account(display_name, email, username, created_at, password_hash)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
INSERT INTO user_account(first_name, last_name, email, username, created_at, password_hash)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING *;
|
||||
|
@ -42,7 +42,7 @@ func (h *CitadelHandler) RefreshTokenHandler(w http.ResponseWriter, r *http.Requ
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
accessTokenString, err := NewAccessToken("1")
|
||||
accessTokenString, err := NewAccessToken(token.UserID.String())
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
@ -57,6 +57,25 @@ func (h *CitadelHandler) RefreshTokenHandler(w http.ResponseWriter, r *http.Requ
|
||||
json.NewEncoder(w).Encode(LoginResponseData{AccessToken: accessTokenString})
|
||||
}
|
||||
|
||||
func (h *CitadelHandler) LogoutHandler(w http.ResponseWriter, r *http.Request) {
|
||||
c, err := r.Cookie("refreshToken")
|
||||
if err != nil {
|
||||
if err == http.ErrNoCookie {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
refreshTokenID := uuid.MustParse(c.Value)
|
||||
err = h.repo.DeleteRefreshTokenByID(r.Context(), refreshTokenID)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
json.NewEncoder(w).Encode(LogoutResponseData{Status: "success"})
|
||||
}
|
||||
|
||||
func (h *CitadelHandler) LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var requestData LoginRequestData
|
||||
err := json.NewDecoder(r.Body).Decode(&requestData)
|
||||
@ -85,12 +104,11 @@ func (h *CitadelHandler) LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
userID := uuid.MustParse("0183d9ab-d0ed-4c9b-a3df-77a0cdd93dca")
|
||||
refreshCreatedAt := time.Now().UTC()
|
||||
refreshExpiresAt := refreshCreatedAt.AddDate(0, 0, 1)
|
||||
refreshTokenString, err := h.repo.CreateRefreshToken(r.Context(), pg.CreateRefreshTokenParams{userID, refreshCreatedAt, refreshExpiresAt})
|
||||
refreshTokenString, err := h.repo.CreateRefreshToken(r.Context(), pg.CreateRefreshTokenParams{user.UserID, refreshCreatedAt, refreshExpiresAt})
|
||||
|
||||
accessTokenString, err := NewAccessToken("1")
|
||||
accessTokenString, err := NewAccessToken(user.UserID.String())
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
@ -109,5 +127,6 @@ func (rs authResource) Routes(citadelHandler CitadelHandler) chi.Router {
|
||||
r := chi.NewRouter()
|
||||
r.Post("/login", citadelHandler.LoginHandler)
|
||||
r.Post("/refresh_token", citadelHandler.RefreshTokenHandler)
|
||||
r.Post("/logout", citadelHandler.LogoutHandler)
|
||||
return r
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@ -40,7 +41,13 @@ func AuthenticationMiddleware(next http.Handler) http.Handler {
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.WithValue(r.Context(), "accessClaims", accessClaims)
|
||||
userID, err := uuid.Parse(accessClaims.UserID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
ctx := context.WithValue(r.Context(), "userID", userID)
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
|
@ -23,6 +23,10 @@ type LoginResponseData struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
}
|
||||
|
||||
type LogoutResponseData struct {
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type RefreshTokenResponseData struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
}
|
||||
|
Reference in New Issue
Block a user