diff --git a/gqlgen.yml b/gqlgen.yml index 20322a2..9fb2473 100644 --- a/gqlgen.yml +++ b/gqlgen.yml @@ -1,6 +1,6 @@ # Where are all the schema files located? globs are supported eg src/**/*.graphqls schema: - - internal/graph/*.graphqls + - internal/graph/schema/*.gql # Where should the generated server code go? exec: @@ -22,6 +22,8 @@ resolver: layout: follow-schema dir: internal/graph package: graph + filename_template: "{name}.resolvers.go" + # Optional: turn on to use []Thing instead of []*Thing omit_slice_element_pointers: true diff --git a/internal/graph/generated.go b/internal/graph/generated.go index b2c3cd6..7c78862 100644 --- a/internal/graph/generated.go +++ b/internal/graph/generated.go @@ -557,10 +557,6 @@ type LabelColorResolver interface { ID(ctx context.Context, obj *db.LabelColor) (uuid.UUID, error) } type MutationResolver interface { - CreateProject(ctx context.Context, input NewProject) (*db.Project, error) - DeleteProject(ctx context.Context, input DeleteProject) (*DeleteProjectPayload, error) - UpdateProjectName(ctx context.Context, input *UpdateProjectName) (*db.Project, error) - ToggleProjectVisibility(ctx context.Context, input ToggleProjectVisibility) (*ToggleProjectVisibilityPayload, error) CreateProjectLabel(ctx context.Context, input NewProjectLabel) (*db.ProjectLabel, error) DeleteProjectLabel(ctx context.Context, input DeleteProjectLabel) (*db.ProjectLabel, error) UpdateProjectLabel(ctx context.Context, input UpdateProjectLabel) (*db.ProjectLabel, error) @@ -570,15 +566,10 @@ type MutationResolver interface { DeleteProjectMember(ctx context.Context, input DeleteProjectMember) (*DeleteProjectMemberPayload, error) UpdateProjectMemberRole(ctx context.Context, input UpdateProjectMemberRole) (*UpdateProjectMemberRolePayload, error) DeleteInvitedProjectMember(ctx context.Context, input DeleteInvitedProjectMember) (*DeleteInvitedProjectMemberPayload, error) - CreateTask(ctx context.Context, input NewTask) (*db.Task, error) - DeleteTask(ctx context.Context, input DeleteTaskInput) (*DeleteTaskPayload, error) - UpdateTaskDescription(ctx context.Context, input UpdateTaskDescriptionInput) (*db.Task, error) - UpdateTaskLocation(ctx context.Context, input NewTaskLocation) (*UpdateTaskLocationPayload, error) - UpdateTaskName(ctx context.Context, input UpdateTaskName) (*db.Task, error) - SetTaskComplete(ctx context.Context, input SetTaskComplete) (*db.Task, error) - UpdateTaskDueDate(ctx context.Context, input UpdateTaskDueDate) (*db.Task, error) - AssignTask(ctx context.Context, input *AssignTaskInput) (*db.Task, error) - UnassignTask(ctx context.Context, input *UnassignTaskInput) (*db.Task, error) + CreateProject(ctx context.Context, input NewProject) (*db.Project, error) + DeleteProject(ctx context.Context, input DeleteProject) (*DeleteProjectPayload, error) + UpdateProjectName(ctx context.Context, input *UpdateProjectName) (*db.Project, error) + ToggleProjectVisibility(ctx context.Context, input ToggleProjectVisibility) (*ToggleProjectVisibilityPayload, error) CreateTaskChecklist(ctx context.Context, input CreateTaskChecklist) (*db.TaskChecklist, error) DeleteTaskChecklist(ctx context.Context, input DeleteTaskChecklist) (*DeleteTaskChecklistPayload, error) UpdateTaskChecklistName(ctx context.Context, input UpdateTaskChecklistName) (*db.TaskChecklist, error) @@ -601,11 +592,20 @@ type MutationResolver interface { AddTaskLabel(ctx context.Context, input *AddTaskLabelInput) (*db.Task, error) RemoveTaskLabel(ctx context.Context, input *RemoveTaskLabelInput) (*db.Task, error) ToggleTaskLabel(ctx context.Context, input ToggleTaskLabelInput) (*ToggleTaskLabelPayload, error) - DeleteTeam(ctx context.Context, input DeleteTeam) (*DeleteTeamPayload, error) - CreateTeam(ctx context.Context, input NewTeam) (*db.Team, error) + CreateTask(ctx context.Context, input NewTask) (*db.Task, error) + DeleteTask(ctx context.Context, input DeleteTaskInput) (*DeleteTaskPayload, error) + UpdateTaskDescription(ctx context.Context, input UpdateTaskDescriptionInput) (*db.Task, error) + UpdateTaskLocation(ctx context.Context, input NewTaskLocation) (*UpdateTaskLocationPayload, error) + UpdateTaskName(ctx context.Context, input UpdateTaskName) (*db.Task, error) + SetTaskComplete(ctx context.Context, input SetTaskComplete) (*db.Task, error) + UpdateTaskDueDate(ctx context.Context, input UpdateTaskDueDate) (*db.Task, error) + AssignTask(ctx context.Context, input *AssignTaskInput) (*db.Task, error) + UnassignTask(ctx context.Context, input *UnassignTaskInput) (*db.Task, error) CreateTeamMember(ctx context.Context, input CreateTeamMember) (*CreateTeamMemberPayload, error) UpdateTeamMemberRole(ctx context.Context, input UpdateTeamMemberRole) (*UpdateTeamMemberRolePayload, error) DeleteTeamMember(ctx context.Context, input DeleteTeamMember) (*DeleteTeamMemberPayload, error) + DeleteTeam(ctx context.Context, input DeleteTeam) (*DeleteTeamPayload, error) + CreateTeam(ctx context.Context, input NewTeam) (*db.Team, error) CreateUserAccount(ctx context.Context, input NewUserAccount) (*db.UserAccount, error) DeleteUserAccount(ctx context.Context, input DeleteUserAccount) (*DeleteUserAccountPayload, error) DeleteInvitedUserAccount(ctx context.Context, input DeleteInvitedUserAccount) (*DeleteInvitedUserAccountPayload, error) @@ -3099,114 +3099,45 @@ func (ec *executionContext) introspectType(name string) (*introspection.Type, er } var sources = []*ast.Source{ - {Name: "internal/graph/schema.graphqls", Input: `scalar Time -scalar UUID -scalar Upload - -enum RoleCode { - owner - admin - member - observer + {Name: "internal/graph/schema/notification.gql", Input: `extend type Query { + notifications: [Notification!]! } -type ProjectLabel { - id: ID! - createdDate: Time! - labelColor: LabelColor! - name: String +enum EntityType { + TASK } -type LabelColor { - id: ID! - name: String! - position: Float! - colorHex: String! +enum ActorType { + USER } -type TaskLabel { - id: ID! - projectLabel: ProjectLabel! - assignedDate: Time! +enum ActionType { + TASK_MEMBER_ADDED } -type ProfileIcon { - url: String - initials: String - bgColor: String -} - -type OwnersList { - projects: [UUID!]! - teams: [UUID!]! -} - -type Member { - id: ID! - role: Role! - fullName: String! - username: String! - profileIcon: ProfileIcon! - owned: OwnedList! - member: MemberList! -} - -type Role { - code: String! +type NotificationActor { + id: UUID! + type: ActorType! name: String! } -type OwnedList { - teams: [Team!]! - projects: [Project!]! +type NotificationEntity { + id: UUID! + type: EntityType! + name: String! } -type MemberList { - teams: [Team!]! - projects: [Project!]! -} - -type UserAccount { +type Notification { id: ID! - email: String! + entity: NotificationEntity! + actionType: ActionType! + actor: NotificationActor! + read: Boolean! createdAt: Time! - fullName: String! - initials: String! - bio: String! - role: Role! - username: String! - profileIcon: ProfileIcon! - owned: OwnedList! - member: MemberList! } -type InvitedUserAccount { - id: ID! - email: String! - invitedOn: Time! - member: MemberList! -} - -type Team { - id: ID! - createdAt: Time! - name: String! - permission: TeamPermission! - members: [Member!]! -} - - -type InvitedMember { - email: String! - invitedOn: Time! -} - -type TeamPermission { - team: RoleCode! - org: RoleCode! -} - -type ProjectPermission { +`, BuiltIn: false}, + {Name: "internal/graph/schema/project.gql", Input: `type ProjectPermission { team: RoleCode! project: RoleCode! org: RoleCode! @@ -3225,94 +3156,207 @@ type Project { labels: [ProjectLabel!]! } -type TaskGroup { +type ProjectLabel { + id: ID! + createdDate: Time! + labelColor: LabelColor! + name: String +} + +type LabelColor { id: ID! - projectID: String! - createdAt: Time! name: String! position: Float! - tasks: [Task!]! + colorHex: String! } -type ChecklistBadge { - complete: Int! - total: Int! -} - -type CommentsBadge { - total: Int! - unread: Boolean! -} - -type TaskBadges { - checklist: ChecklistBadge - comments: CommentsBadge -} - -type CausedBy { - id: ID! - fullName: String! - profileIcon: ProfileIcon -} - -type TaskActivityData { - name: String! - value: String! -} - -enum ActivityType { - TASK_ADDED - TASK_MOVED - TASK_MARKED_COMPLETE - TASK_MARKED_INCOMPLETE - TASK_DUE_DATE_CHANGED - TASK_DUE_DATE_ADDED - TASK_DUE_DATE_REMOVED - TASK_CHECKLIST_CHANGED - TASK_CHECKLIST_ADDED - TASK_CHECKLIST_REMOVED -} - -type TaskActivity { - id: ID! - type: ActivityType! - data: [TaskActivityData!]! - causedBy: CausedBy! - createdAt: Time! -} - -type Task { - id: ID! - taskGroup: TaskGroup! - createdAt: Time! - name: String! - position: Float! - description: String - dueDate: Time - hasTime: Boolean! - complete: Boolean! - completedAt: Time - assigned: [Member!]! - labels: [TaskLabel!]! - checklists: [TaskChecklist!]! - badges: TaskBadges! - activity: [TaskActivity!]! - comments: [TaskComment!]! -} - -type CreatedBy { +type Member { id: ID! + role: Role! fullName: String! + username: String! profileIcon: ProfileIcon! + owned: OwnedList! + member: MemberList! } -type TaskComment { - id: ID! - createdAt: Time! - updatedAt: Time - message: String! - createdBy: CreatedBy! - pinned: Boolean! +type InvitedMember { + email: String! + invitedOn: Time! +} + +extend type Mutation { + createProjectLabel(input: NewProjectLabel!): + ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) + deleteProjectLabel(input: DeleteProjectLabel!): + ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) + updateProjectLabel(input: UpdateProjectLabel!): + ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) + updateProjectLabelName(input: UpdateProjectLabelName!): + ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) + updateProjectLabelColor(input: UpdateProjectLabelColor!): + ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) +} + +input NewProjectLabel { + projectID: UUID! + labelColorID: UUID! + name: String +} + +input DeleteProjectLabel { + projectLabelID: UUID! +} + +input UpdateProjectLabelName { + projectLabelID: UUID! + name: String! +} + +input UpdateProjectLabel { + projectLabelID: UUID! + labelColorID: UUID! + name: String! +} + +input UpdateProjectLabelColor { + projectLabelID: UUID! + labelColorID: UUID! +} + +extend type Mutation { + inviteProjectMembers(input: InviteProjectMembers!): + InviteProjectMembersPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + deleteProjectMember(input: DeleteProjectMember!): + DeleteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + updateProjectMemberRole(input: UpdateProjectMemberRole!): + UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + + deleteInvitedProjectMember(input: DeleteInvitedProjectMember!): + DeleteInvitedProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) +} + +input DeleteInvitedProjectMember { + projectID: UUID! + email: String! +} + +type DeleteInvitedProjectMemberPayload { + invitedMember: InvitedMember! +} + +input MemberInvite { + userID: UUID + email: String +} + +input InviteProjectMembers { + projectID: UUID! + members: [MemberInvite!]! +} + +type InviteProjectMembersPayload { + ok: Boolean! + projectID: UUID! + members: [Member!]! + invitedMembers: [InvitedMember!]! +} + +input DeleteProjectMember { + projectID: UUID! + userID: UUID! +} + +type DeleteProjectMemberPayload { + ok: Boolean! + member: Member! + projectID: UUID! +} + +input UpdateProjectMemberRole { + projectID: UUID! + userID: UUID! + roleCode: RoleCode! +} + +type UpdateProjectMemberRolePayload { + ok: Boolean! + member: Member! +} + +extend type Mutation { + createProject(input: NewProject!): Project! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM) + deleteProject(input: DeleteProject!): + DeleteProjectPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + updateProjectName(input: UpdateProjectName): + Project! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + toggleProjectVisibility(input: ToggleProjectVisibility!): ToggleProjectVisibilityPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) +} + +input ToggleProjectVisibility { + projectID: UUID! + isPublic: Boolean! +} + +type ToggleProjectVisibilityPayload { + project: Project! +} + +input NewProject { + teamID: UUID + name: String! +} + +input UpdateProjectName { + projectID: UUID! + name: String! +} + +input DeleteProject { + projectID: UUID! +} + +type DeleteProjectPayload { + ok: Boolean! + project: Project! +} + +`, BuiltIn: false}, + {Name: "internal/graph/schema/schema.gql", Input: `scalar Time +scalar UUID +scalar Upload + +enum RoleCode { + owner + admin + member + observer +} + +type Role { + code: String! + name: String! +} + +type ProfileIcon { + url: String + initials: String + bgColor: String +} + +type OwnersList { + projects: [UUID!]! + teams: [UUID!]! +} + +type OwnedList { + teams: [Team!]! + projects: [Project!]! +} + +type MemberList { + teams: [Team!]! + projects: [Project!]! } type Organization { @@ -3320,22 +3364,6 @@ type Organization { name: String! } -type TaskChecklistItem { - id: ID! - name: String! - taskChecklistID: UUID! - complete: Boolean! - position: Float! - dueDate: Time! -} - -type TaskChecklist { - id: ID! - name: String! - position: Float! - items: [TaskChecklistItem!]! -} - enum ShareStatus { INVITED JOINED @@ -3363,6 +3391,7 @@ enum ObjectType { } directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION + directive @requiresUser on FIELD_DEFINITION type Query { @@ -3453,258 +3482,110 @@ input FindTask { input FindTeam { teamID: UUID! } - -extend type Query { - notifications: [Notification!]! -} - -enum EntityType { - TASK -} - -enum ActorType { - USER -} - -enum ActionType { - TASK_MEMBER_ADDED -} - -type NotificationActor { - id: UUID! - type: ActorType! - name: String! -} - -type NotificationEntity { - id: UUID! - type: EntityType! - name: String! -} - -type Notification { +`, BuiltIn: false}, + {Name: "internal/graph/schema/task.gql", Input: ` +type TaskLabel { id: ID! - entity: NotificationEntity! - actionType: ActionType! - actor: NotificationActor! - read: Boolean! + projectLabel: ProjectLabel! + assignedDate: Time! +} + +type ChecklistBadge { + complete: Int! + total: Int! +} + +type CommentsBadge { + total: Int! + unread: Boolean! +} + +type TaskBadges { + checklist: ChecklistBadge + comments: CommentsBadge +} + +type Task { + id: ID! + taskGroup: TaskGroup! + createdAt: Time! + name: String! + position: Float! + description: String + dueDate: Time + hasTime: Boolean! + complete: Boolean! + completedAt: Time + assigned: [Member!]! + labels: [TaskLabel!]! + checklists: [TaskChecklist!]! + badges: TaskBadges! + activity: [TaskActivity!]! + comments: [TaskComment!]! +} + +type TaskActivityData { + name: String! + value: String! +} + +enum ActivityType { + TASK_ADDED + TASK_MOVED + TASK_MARKED_COMPLETE + TASK_MARKED_INCOMPLETE + TASK_DUE_DATE_CHANGED + TASK_DUE_DATE_ADDED + TASK_DUE_DATE_REMOVED + TASK_CHECKLIST_CHANGED + TASK_CHECKLIST_ADDED + TASK_CHECKLIST_REMOVED +} + +type TaskActivity { + id: ID! + type: ActivityType! + data: [TaskActivityData!]! + causedBy: CausedBy! createdAt: Time! } -extend type Mutation { - createProject(input: NewProject!): Project! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM) - deleteProject(input: DeleteProject!): - DeleteProjectPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) - updateProjectName(input: UpdateProjectName): - Project! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) - toggleProjectVisibility(input: ToggleProjectVisibility!): ToggleProjectVisibilityPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) +type CausedBy { + id: ID! + fullName: String! + profileIcon: ProfileIcon } -input ToggleProjectVisibility { - projectID: UUID! - isPublic: Boolean! + +type CreatedBy { + id: ID! + fullName: String! + profileIcon: ProfileIcon! } -type ToggleProjectVisibilityPayload { - project: Project! +type TaskComment { + id: ID! + createdAt: Time! + updatedAt: Time + message: String! + createdBy: CreatedBy! + pinned: Boolean! } -input NewProject { - teamID: UUID +type TaskChecklistItem { + id: ID! name: String! -} - -input UpdateProjectName { - projectID: UUID! - name: String! -} - -input DeleteProject { - projectID: UUID! -} - -type DeleteProjectPayload { - ok: Boolean! - project: Project! -} - -extend type Mutation { - createProjectLabel(input: NewProjectLabel!): - ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) - deleteProjectLabel(input: DeleteProjectLabel!): - ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) - updateProjectLabel(input: UpdateProjectLabel!): - ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) - updateProjectLabelName(input: UpdateProjectLabelName!): - ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) - updateProjectLabelColor(input: UpdateProjectLabelColor!): - ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) -} - -input NewProjectLabel { - projectID: UUID! - labelColorID: UUID! - name: String -} - -input DeleteProjectLabel { - projectLabelID: UUID! -} - -input UpdateProjectLabelName { - projectLabelID: UUID! - name: String! -} - -input UpdateProjectLabel { - projectLabelID: UUID! - labelColorID: UUID! - name: String! -} - -input UpdateProjectLabelColor { - projectLabelID: UUID! - labelColorID: UUID! -} - -extend type Mutation { - inviteProjectMembers(input: InviteProjectMembers!): - InviteProjectMembersPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) - deleteProjectMember(input: DeleteProjectMember!): - DeleteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) - updateProjectMemberRole(input: UpdateProjectMemberRole!): - UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) - -deleteInvitedProjectMember(input: DeleteInvitedProjectMember!): - DeleteInvitedProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) -} - -input DeleteInvitedProjectMember { - projectID: UUID! - email: String! -} - -type DeleteInvitedProjectMemberPayload { - invitedMember: InvitedMember! -} - -input MemberInvite { - userID: UUID - email: String -} - -input InviteProjectMembers { - projectID: UUID! - members: [MemberInvite!]! -} - -type InviteProjectMembersPayload { - ok: Boolean! - projectID: UUID! - members: [Member!]! - invitedMembers: [InvitedMember!]! -} - -input DeleteProjectMember { - projectID: UUID! - userID: UUID! -} - -type DeleteProjectMemberPayload { - ok: Boolean! - member: Member! - projectID: UUID! -} - -input UpdateProjectMemberRole { - projectID: UUID! - userID: UUID! - roleCode: RoleCode! -} - -type UpdateProjectMemberRolePayload { - ok: Boolean! - member: Member! -} - -extend type Mutation { - createTask(input: NewTask!): - Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_GROUP) - deleteTask(input: DeleteTaskInput!): - DeleteTaskPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - - updateTaskDescription(input: UpdateTaskDescriptionInput!): - Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - updateTaskLocation(input: NewTaskLocation!): - UpdateTaskLocationPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - updateTaskName(input: UpdateTaskName!): - Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - setTaskComplete(input: SetTaskComplete!): - Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - updateTaskDueDate(input: UpdateTaskDueDate!): - Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - - assignTask(input: AssignTaskInput): - Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - unassignTask(input: UnassignTaskInput): - Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) -} - -input NewTask { - taskGroupID: UUID! - name: String! - position: Float! - assigned: [UUID!] -} - -input AssignTaskInput { - taskID: UUID! - userID: UUID! -} - -input UnassignTaskInput { - taskID: UUID! - userID: UUID! -} - -input UpdateTaskDescriptionInput { - taskID: UUID! - description: String! -} - -type UpdateTaskLocationPayload { - previousTaskGroupID: UUID! - task: Task! -} - -input UpdateTaskDueDate { - taskID: UUID! - hasTime: Boolean! - dueDate: Time -} - -input SetTaskComplete { - taskID: UUID! + taskChecklistID: UUID! complete: Boolean! -} - -input NewTaskLocation { - taskID: UUID! - taskGroupID: UUID! position: Float! + dueDate: Time! } -input DeleteTaskInput { - taskID: UUID! -} - -type DeleteTaskPayload { - taskID: UUID! -} - -input UpdateTaskName { - taskID: UUID! +type TaskChecklist { + id: ID! name: String! + position: Float! + items: [TaskChecklistItem!]! } extend type Mutation { @@ -3941,27 +3822,111 @@ type ToggleTaskLabelPayload { } extend type Mutation { - deleteTeam(input: DeleteTeam!): - DeleteTeamPayload! @hasRole(roles:[ ADMIN], level: TEAM, type: TEAM) - createTeam(input: NewTeam!): - Team! @hasRole(roles: [ADMIN], level: ORG, type: ORG) + createTask(input: NewTask!): + Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_GROUP) + deleteTask(input: DeleteTaskInput!): + DeleteTaskPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + + updateTaskDescription(input: UpdateTaskDescriptionInput!): + Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + updateTaskLocation(input: NewTaskLocation!): + UpdateTaskLocationPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + updateTaskName(input: UpdateTaskName!): + Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + setTaskComplete(input: SetTaskComplete!): + Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + updateTaskDueDate(input: UpdateTaskDueDate!): + Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + + assignTask(input: AssignTaskInput): + Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + unassignTask(input: UnassignTaskInput): + Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) } -input NewTeam { +input NewTask { + taskGroupID: UUID! name: String! - organizationID: UUID! + position: Float! + assigned: [UUID!] } -input DeleteTeam { - teamID: UUID! +input AssignTaskInput { + taskID: UUID! + userID: UUID! } -type DeleteTeamPayload { - ok: Boolean! - team: Team! - projects: [Project!]! +input UnassignTaskInput { + taskID: UUID! + userID: UUID! } +input UpdateTaskDescriptionInput { + taskID: UUID! + description: String! +} + +type UpdateTaskLocationPayload { + previousTaskGroupID: UUID! + task: Task! +} + +input UpdateTaskDueDate { + taskID: UUID! + hasTime: Boolean! + dueDate: Time +} + +input SetTaskComplete { + taskID: UUID! + complete: Boolean! +} + +input NewTaskLocation { + taskID: UUID! + taskGroupID: UUID! + position: Float! +} + +input DeleteTaskInput { + taskID: UUID! +} + +type DeleteTaskPayload { + taskID: UUID! +} + +input UpdateTaskName { + taskID: UUID! + name: String! +} + +`, BuiltIn: false}, + {Name: "internal/graph/schema/taskList.gql", Input: `type TaskGroup { + id: ID! + projectID: String! + createdAt: Time! + name: String! + position: Float! + tasks: [Task!]! +} + + +`, BuiltIn: false}, + {Name: "internal/graph/schema/team.gql", Input: `type Team { + id: ID! + createdAt: Time! + name: String! + permission: TeamPermission! + members: [Member!]! +} + +type TeamPermission { + team: RoleCode! + org: RoleCode! +} + + extend type Mutation { createTeamMember(input: CreateTeamMember!): CreateTeamMemberPayload! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM) @@ -4006,6 +3971,52 @@ type UpdateTeamMemberRolePayload { member: Member! } +extend type Mutation { + deleteTeam(input: DeleteTeam!): + DeleteTeamPayload! @hasRole(roles:[ ADMIN], level: TEAM, type: TEAM) + createTeam(input: NewTeam!): + Team! @hasRole(roles: [ADMIN], level: ORG, type: ORG) +} + +input NewTeam { + name: String! + organizationID: UUID! +} + +input DeleteTeam { + teamID: UUID! +} + +type DeleteTeamPayload { + ok: Boolean! + team: Team! + projects: [Project!]! +} + +`, BuiltIn: false}, + {Name: "internal/graph/schema/user.gql", Input: `type UserAccount { + id: ID! + email: String! + createdAt: Time! + fullName: String! + initials: String! + bio: String! + role: Role! + username: String! + profileIcon: ProfileIcon! + owned: OwnedList! + member: MemberList! +} + +type InvitedUserAccount { + id: ID! + email: String! + invitedOn: Time! + member: MemberList! +} + + + extend type Mutation { createUserAccount(input: NewUserAccount!): UserAccount! @hasRole(roles: [ADMIN], level: ORG, type: ORG) @@ -7687,302 +7698,6 @@ func (ec *executionContext) _MemberSearchResult_status(ctx context.Context, fiel return ec.marshalNShareStatus2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐShareStatus(ctx, field.Selections, res) } -func (ec *executionContext) _Mutation_createProject(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - fc := &graphql.FieldContext{ - Object: "Mutation", - Field: field, - Args: nil, - IsMethod: true, - IsResolver: true, - } - - ctx = graphql.WithFieldContext(ctx, fc) - rawArgs := field.ArgumentMap(ec.Variables) - args, err := ec.field_Mutation_createProject_args(ctx, rawArgs) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - fc.Args = args - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - directive0 := func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().CreateProject(rctx, args["input"].(NewProject)) - } - directive1 := func(ctx context.Context) (interface{}, error) { - roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN"}) - if err != nil { - return nil, err - } - level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "TEAM") - if err != nil { - return nil, err - } - typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TEAM") - if err != nil { - return nil, err - } - if ec.directives.HasRole == nil { - return nil, errors.New("directive hasRole is not implemented") - } - return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) - } - - tmp, err := directive1(rctx) - if err != nil { - return nil, graphql.ErrorOnPath(ctx, err) - } - if tmp == nil { - return nil, nil - } - if data, ok := tmp.(*db.Project); ok { - return data, nil - } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Project`, tmp) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*db.Project) - fc.Result = res - return ec.marshalNProject2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐProject(ctx, field.Selections, res) -} - -func (ec *executionContext) _Mutation_deleteProject(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - fc := &graphql.FieldContext{ - Object: "Mutation", - Field: field, - Args: nil, - IsMethod: true, - IsResolver: true, - } - - ctx = graphql.WithFieldContext(ctx, fc) - rawArgs := field.ArgumentMap(ec.Variables) - args, err := ec.field_Mutation_deleteProject_args(ctx, rawArgs) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - fc.Args = args - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - directive0 := func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().DeleteProject(rctx, args["input"].(DeleteProject)) - } - directive1 := func(ctx context.Context) (interface{}, error) { - roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN"}) - if err != nil { - return nil, err - } - level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") - if err != nil { - return nil, err - } - typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "PROJECT") - if err != nil { - return nil, err - } - if ec.directives.HasRole == nil { - return nil, errors.New("directive hasRole is not implemented") - } - return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) - } - - tmp, err := directive1(rctx) - if err != nil { - return nil, graphql.ErrorOnPath(ctx, err) - } - if tmp == nil { - return nil, nil - } - if data, ok := tmp.(*DeleteProjectPayload); ok { - return data, nil - } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/graph.DeleteProjectPayload`, tmp) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*DeleteProjectPayload) - fc.Result = res - return ec.marshalNDeleteProjectPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐDeleteProjectPayload(ctx, field.Selections, res) -} - -func (ec *executionContext) _Mutation_updateProjectName(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - fc := &graphql.FieldContext{ - Object: "Mutation", - Field: field, - Args: nil, - IsMethod: true, - IsResolver: true, - } - - ctx = graphql.WithFieldContext(ctx, fc) - rawArgs := field.ArgumentMap(ec.Variables) - args, err := ec.field_Mutation_updateProjectName_args(ctx, rawArgs) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - fc.Args = args - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - directive0 := func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().UpdateProjectName(rctx, args["input"].(*UpdateProjectName)) - } - directive1 := func(ctx context.Context) (interface{}, error) { - roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN"}) - if err != nil { - return nil, err - } - level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") - if err != nil { - return nil, err - } - typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "PROJECT") - if err != nil { - return nil, err - } - if ec.directives.HasRole == nil { - return nil, errors.New("directive hasRole is not implemented") - } - return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) - } - - tmp, err := directive1(rctx) - if err != nil { - return nil, graphql.ErrorOnPath(ctx, err) - } - if tmp == nil { - return nil, nil - } - if data, ok := tmp.(*db.Project); ok { - return data, nil - } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Project`, tmp) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*db.Project) - fc.Result = res - return ec.marshalNProject2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐProject(ctx, field.Selections, res) -} - -func (ec *executionContext) _Mutation_toggleProjectVisibility(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - fc := &graphql.FieldContext{ - Object: "Mutation", - Field: field, - Args: nil, - IsMethod: true, - IsResolver: true, - } - - ctx = graphql.WithFieldContext(ctx, fc) - rawArgs := field.ArgumentMap(ec.Variables) - args, err := ec.field_Mutation_toggleProjectVisibility_args(ctx, rawArgs) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - fc.Args = args - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - directive0 := func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().ToggleProjectVisibility(rctx, args["input"].(ToggleProjectVisibility)) - } - directive1 := func(ctx context.Context) (interface{}, error) { - roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN"}) - if err != nil { - return nil, err - } - level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") - if err != nil { - return nil, err - } - typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "PROJECT") - if err != nil { - return nil, err - } - if ec.directives.HasRole == nil { - return nil, errors.New("directive hasRole is not implemented") - } - return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) - } - - tmp, err := directive1(rctx) - if err != nil { - return nil, graphql.ErrorOnPath(ctx, err) - } - if tmp == nil { - return nil, nil - } - if data, ok := tmp.(*ToggleProjectVisibilityPayload); ok { - return data, nil - } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/graph.ToggleProjectVisibilityPayload`, tmp) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*ToggleProjectVisibilityPayload) - fc.Result = res - return ec.marshalNToggleProjectVisibilityPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐToggleProjectVisibilityPayload(ctx, field.Selections, res) -} - func (ec *executionContext) _Mutation_createProjectLabel(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -8649,7 +8364,7 @@ func (ec *executionContext) _Mutation_deleteInvitedProjectMember(ctx context.Con return ec.marshalNDeleteInvitedProjectMemberPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐDeleteInvitedProjectMemberPayload(ctx, field.Selections, res) } -func (ec *executionContext) _Mutation_createTask(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_createProject(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -8666,7 +8381,7 @@ func (ec *executionContext) _Mutation_createTask(ctx context.Context, field grap ctx = graphql.WithFieldContext(ctx, fc) rawArgs := field.ArgumentMap(ec.Variables) - args, err := ec.field_Mutation_createTask_args(ctx, rawArgs) + args, err := ec.field_Mutation_createProject_args(ctx, rawArgs) if err != nil { ec.Error(ctx, err) return graphql.Null @@ -8675,18 +8390,18 @@ func (ec *executionContext) _Mutation_createTask(ctx context.Context, field grap resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { directive0 := func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().CreateTask(rctx, args["input"].(NewTask)) + return ec.resolvers.Mutation().CreateProject(rctx, args["input"].(NewProject)) } directive1 := func(ctx context.Context) (interface{}, error) { - roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"}) + roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN"}) if err != nil { return nil, err } - level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") + level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "TEAM") if err != nil { return nil, err } - typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK_GROUP") + typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TEAM") if err != nil { return nil, err } @@ -8703,10 +8418,10 @@ func (ec *executionContext) _Mutation_createTask(ctx context.Context, field grap if tmp == nil { return nil, nil } - if data, ok := tmp.(*db.Task); ok { + if data, ok := tmp.(*db.Project); ok { return data, nil } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Task`, tmp) + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Project`, tmp) }) if err != nil { ec.Error(ctx, err) @@ -8718,12 +8433,12 @@ func (ec *executionContext) _Mutation_createTask(ctx context.Context, field grap } return graphql.Null } - res := resTmp.(*db.Task) + res := resTmp.(*db.Project) fc.Result = res - return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTask(ctx, field.Selections, res) + return ec.marshalNProject2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐProject(ctx, field.Selections, res) } -func (ec *executionContext) _Mutation_deleteTask(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_deleteProject(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -8740,7 +8455,7 @@ func (ec *executionContext) _Mutation_deleteTask(ctx context.Context, field grap ctx = graphql.WithFieldContext(ctx, fc) rawArgs := field.ArgumentMap(ec.Variables) - args, err := ec.field_Mutation_deleteTask_args(ctx, rawArgs) + args, err := ec.field_Mutation_deleteProject_args(ctx, rawArgs) if err != nil { ec.Error(ctx, err) return graphql.Null @@ -8749,10 +8464,10 @@ func (ec *executionContext) _Mutation_deleteTask(ctx context.Context, field grap resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { directive0 := func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().DeleteTask(rctx, args["input"].(DeleteTaskInput)) + return ec.resolvers.Mutation().DeleteProject(rctx, args["input"].(DeleteProject)) } directive1 := func(ctx context.Context) (interface{}, error) { - roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"}) + roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN"}) if err != nil { return nil, err } @@ -8760,7 +8475,7 @@ func (ec *executionContext) _Mutation_deleteTask(ctx context.Context, field grap if err != nil { return nil, err } - typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK") + typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "PROJECT") if err != nil { return nil, err } @@ -8777,10 +8492,10 @@ func (ec *executionContext) _Mutation_deleteTask(ctx context.Context, field grap if tmp == nil { return nil, nil } - if data, ok := tmp.(*DeleteTaskPayload); ok { + if data, ok := tmp.(*DeleteProjectPayload); ok { return data, nil } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/graph.DeleteTaskPayload`, tmp) + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/graph.DeleteProjectPayload`, tmp) }) if err != nil { ec.Error(ctx, err) @@ -8792,12 +8507,12 @@ func (ec *executionContext) _Mutation_deleteTask(ctx context.Context, field grap } return graphql.Null } - res := resTmp.(*DeleteTaskPayload) + res := resTmp.(*DeleteProjectPayload) fc.Result = res - return ec.marshalNDeleteTaskPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐDeleteTaskPayload(ctx, field.Selections, res) + return ec.marshalNDeleteProjectPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐDeleteProjectPayload(ctx, field.Selections, res) } -func (ec *executionContext) _Mutation_updateTaskDescription(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_updateProjectName(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -8814,7 +8529,7 @@ func (ec *executionContext) _Mutation_updateTaskDescription(ctx context.Context, ctx = graphql.WithFieldContext(ctx, fc) rawArgs := field.ArgumentMap(ec.Variables) - args, err := ec.field_Mutation_updateTaskDescription_args(ctx, rawArgs) + args, err := ec.field_Mutation_updateProjectName_args(ctx, rawArgs) if err != nil { ec.Error(ctx, err) return graphql.Null @@ -8823,10 +8538,10 @@ func (ec *executionContext) _Mutation_updateTaskDescription(ctx context.Context, resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { directive0 := func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().UpdateTaskDescription(rctx, args["input"].(UpdateTaskDescriptionInput)) + return ec.resolvers.Mutation().UpdateProjectName(rctx, args["input"].(*UpdateProjectName)) } directive1 := func(ctx context.Context) (interface{}, error) { - roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"}) + roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN"}) if err != nil { return nil, err } @@ -8834,7 +8549,7 @@ func (ec *executionContext) _Mutation_updateTaskDescription(ctx context.Context, if err != nil { return nil, err } - typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK") + typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "PROJECT") if err != nil { return nil, err } @@ -8851,10 +8566,10 @@ func (ec *executionContext) _Mutation_updateTaskDescription(ctx context.Context, if tmp == nil { return nil, nil } - if data, ok := tmp.(*db.Task); ok { + if data, ok := tmp.(*db.Project); ok { return data, nil } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Task`, tmp) + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Project`, tmp) }) if err != nil { ec.Error(ctx, err) @@ -8866,12 +8581,12 @@ func (ec *executionContext) _Mutation_updateTaskDescription(ctx context.Context, } return graphql.Null } - res := resTmp.(*db.Task) + res := resTmp.(*db.Project) fc.Result = res - return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTask(ctx, field.Selections, res) + return ec.marshalNProject2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐProject(ctx, field.Selections, res) } -func (ec *executionContext) _Mutation_updateTaskLocation(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_toggleProjectVisibility(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -8888,7 +8603,7 @@ func (ec *executionContext) _Mutation_updateTaskLocation(ctx context.Context, fi ctx = graphql.WithFieldContext(ctx, fc) rawArgs := field.ArgumentMap(ec.Variables) - args, err := ec.field_Mutation_updateTaskLocation_args(ctx, rawArgs) + args, err := ec.field_Mutation_toggleProjectVisibility_args(ctx, rawArgs) if err != nil { ec.Error(ctx, err) return graphql.Null @@ -8897,10 +8612,10 @@ func (ec *executionContext) _Mutation_updateTaskLocation(ctx context.Context, fi resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { directive0 := func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().UpdateTaskLocation(rctx, args["input"].(NewTaskLocation)) + return ec.resolvers.Mutation().ToggleProjectVisibility(rctx, args["input"].(ToggleProjectVisibility)) } directive1 := func(ctx context.Context) (interface{}, error) { - roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"}) + roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN"}) if err != nil { return nil, err } @@ -8908,7 +8623,7 @@ func (ec *executionContext) _Mutation_updateTaskLocation(ctx context.Context, fi if err != nil { return nil, err } - typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK") + typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "PROJECT") if err != nil { return nil, err } @@ -8925,10 +8640,10 @@ func (ec *executionContext) _Mutation_updateTaskLocation(ctx context.Context, fi if tmp == nil { return nil, nil } - if data, ok := tmp.(*UpdateTaskLocationPayload); ok { + if data, ok := tmp.(*ToggleProjectVisibilityPayload); ok { return data, nil } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/graph.UpdateTaskLocationPayload`, tmp) + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/graph.ToggleProjectVisibilityPayload`, tmp) }) if err != nil { ec.Error(ctx, err) @@ -8940,379 +8655,9 @@ func (ec *executionContext) _Mutation_updateTaskLocation(ctx context.Context, fi } return graphql.Null } - res := resTmp.(*UpdateTaskLocationPayload) + res := resTmp.(*ToggleProjectVisibilityPayload) fc.Result = res - return ec.marshalNUpdateTaskLocationPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐUpdateTaskLocationPayload(ctx, field.Selections, res) -} - -func (ec *executionContext) _Mutation_updateTaskName(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - fc := &graphql.FieldContext{ - Object: "Mutation", - Field: field, - Args: nil, - IsMethod: true, - IsResolver: true, - } - - ctx = graphql.WithFieldContext(ctx, fc) - rawArgs := field.ArgumentMap(ec.Variables) - args, err := ec.field_Mutation_updateTaskName_args(ctx, rawArgs) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - fc.Args = args - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - directive0 := func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().UpdateTaskName(rctx, args["input"].(UpdateTaskName)) - } - directive1 := func(ctx context.Context) (interface{}, error) { - roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"}) - if err != nil { - return nil, err - } - level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") - if err != nil { - return nil, err - } - typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK") - if err != nil { - return nil, err - } - if ec.directives.HasRole == nil { - return nil, errors.New("directive hasRole is not implemented") - } - return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) - } - - tmp, err := directive1(rctx) - if err != nil { - return nil, graphql.ErrorOnPath(ctx, err) - } - if tmp == nil { - return nil, nil - } - if data, ok := tmp.(*db.Task); ok { - return data, nil - } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Task`, tmp) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*db.Task) - fc.Result = res - return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTask(ctx, field.Selections, res) -} - -func (ec *executionContext) _Mutation_setTaskComplete(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - fc := &graphql.FieldContext{ - Object: "Mutation", - Field: field, - Args: nil, - IsMethod: true, - IsResolver: true, - } - - ctx = graphql.WithFieldContext(ctx, fc) - rawArgs := field.ArgumentMap(ec.Variables) - args, err := ec.field_Mutation_setTaskComplete_args(ctx, rawArgs) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - fc.Args = args - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - directive0 := func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().SetTaskComplete(rctx, args["input"].(SetTaskComplete)) - } - directive1 := func(ctx context.Context) (interface{}, error) { - roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"}) - if err != nil { - return nil, err - } - level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") - if err != nil { - return nil, err - } - typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK") - if err != nil { - return nil, err - } - if ec.directives.HasRole == nil { - return nil, errors.New("directive hasRole is not implemented") - } - return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) - } - - tmp, err := directive1(rctx) - if err != nil { - return nil, graphql.ErrorOnPath(ctx, err) - } - if tmp == nil { - return nil, nil - } - if data, ok := tmp.(*db.Task); ok { - return data, nil - } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Task`, tmp) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*db.Task) - fc.Result = res - return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTask(ctx, field.Selections, res) -} - -func (ec *executionContext) _Mutation_updateTaskDueDate(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - fc := &graphql.FieldContext{ - Object: "Mutation", - Field: field, - Args: nil, - IsMethod: true, - IsResolver: true, - } - - ctx = graphql.WithFieldContext(ctx, fc) - rawArgs := field.ArgumentMap(ec.Variables) - args, err := ec.field_Mutation_updateTaskDueDate_args(ctx, rawArgs) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - fc.Args = args - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - directive0 := func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().UpdateTaskDueDate(rctx, args["input"].(UpdateTaskDueDate)) - } - directive1 := func(ctx context.Context) (interface{}, error) { - roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"}) - if err != nil { - return nil, err - } - level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") - if err != nil { - return nil, err - } - typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK") - if err != nil { - return nil, err - } - if ec.directives.HasRole == nil { - return nil, errors.New("directive hasRole is not implemented") - } - return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) - } - - tmp, err := directive1(rctx) - if err != nil { - return nil, graphql.ErrorOnPath(ctx, err) - } - if tmp == nil { - return nil, nil - } - if data, ok := tmp.(*db.Task); ok { - return data, nil - } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Task`, tmp) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*db.Task) - fc.Result = res - return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTask(ctx, field.Selections, res) -} - -func (ec *executionContext) _Mutation_assignTask(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - fc := &graphql.FieldContext{ - Object: "Mutation", - Field: field, - Args: nil, - IsMethod: true, - IsResolver: true, - } - - ctx = graphql.WithFieldContext(ctx, fc) - rawArgs := field.ArgumentMap(ec.Variables) - args, err := ec.field_Mutation_assignTask_args(ctx, rawArgs) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - fc.Args = args - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - directive0 := func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().AssignTask(rctx, args["input"].(*AssignTaskInput)) - } - directive1 := func(ctx context.Context) (interface{}, error) { - roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"}) - if err != nil { - return nil, err - } - level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") - if err != nil { - return nil, err - } - typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK") - if err != nil { - return nil, err - } - if ec.directives.HasRole == nil { - return nil, errors.New("directive hasRole is not implemented") - } - return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) - } - - tmp, err := directive1(rctx) - if err != nil { - return nil, graphql.ErrorOnPath(ctx, err) - } - if tmp == nil { - return nil, nil - } - if data, ok := tmp.(*db.Task); ok { - return data, nil - } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Task`, tmp) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*db.Task) - fc.Result = res - return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTask(ctx, field.Selections, res) -} - -func (ec *executionContext) _Mutation_unassignTask(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - fc := &graphql.FieldContext{ - Object: "Mutation", - Field: field, - Args: nil, - IsMethod: true, - IsResolver: true, - } - - ctx = graphql.WithFieldContext(ctx, fc) - rawArgs := field.ArgumentMap(ec.Variables) - args, err := ec.field_Mutation_unassignTask_args(ctx, rawArgs) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - fc.Args = args - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - directive0 := func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().UnassignTask(rctx, args["input"].(*UnassignTaskInput)) - } - directive1 := func(ctx context.Context) (interface{}, error) { - roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"}) - if err != nil { - return nil, err - } - level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") - if err != nil { - return nil, err - } - typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK") - if err != nil { - return nil, err - } - if ec.directives.HasRole == nil { - return nil, errors.New("directive hasRole is not implemented") - } - return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) - } - - tmp, err := directive1(rctx) - if err != nil { - return nil, graphql.ErrorOnPath(ctx, err) - } - if tmp == nil { - return nil, nil - } - if data, ok := tmp.(*db.Task); ok { - return data, nil - } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Task`, tmp) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*db.Task) - fc.Result = res - return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTask(ctx, field.Selections, res) + return ec.marshalNToggleProjectVisibilityPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐToggleProjectVisibilityPayload(ctx, field.Selections, res) } func (ec *executionContext) _Mutation_createTaskChecklist(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { @@ -10943,7 +10288,7 @@ func (ec *executionContext) _Mutation_toggleTaskLabel(ctx context.Context, field return ec.marshalNToggleTaskLabelPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐToggleTaskLabelPayload(ctx, field.Selections, res) } -func (ec *executionContext) _Mutation_deleteTeam(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_createTask(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -10960,7 +10305,7 @@ func (ec *executionContext) _Mutation_deleteTeam(ctx context.Context, field grap ctx = graphql.WithFieldContext(ctx, fc) rawArgs := field.ArgumentMap(ec.Variables) - args, err := ec.field_Mutation_deleteTeam_args(ctx, rawArgs) + args, err := ec.field_Mutation_createTask_args(ctx, rawArgs) if err != nil { ec.Error(ctx, err) return graphql.Null @@ -10969,18 +10314,18 @@ func (ec *executionContext) _Mutation_deleteTeam(ctx context.Context, field grap resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { directive0 := func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().DeleteTeam(rctx, args["input"].(DeleteTeam)) + return ec.resolvers.Mutation().CreateTask(rctx, args["input"].(NewTask)) } directive1 := func(ctx context.Context) (interface{}, error) { - roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN"}) + roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"}) if err != nil { return nil, err } - level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "TEAM") + level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") if err != nil { return nil, err } - typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TEAM") + typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK_GROUP") if err != nil { return nil, err } @@ -10997,10 +10342,10 @@ func (ec *executionContext) _Mutation_deleteTeam(ctx context.Context, field grap if tmp == nil { return nil, nil } - if data, ok := tmp.(*DeleteTeamPayload); ok { + if data, ok := tmp.(*db.Task); ok { return data, nil } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/graph.DeleteTeamPayload`, tmp) + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Task`, tmp) }) if err != nil { ec.Error(ctx, err) @@ -11012,12 +10357,12 @@ func (ec *executionContext) _Mutation_deleteTeam(ctx context.Context, field grap } return graphql.Null } - res := resTmp.(*DeleteTeamPayload) + res := resTmp.(*db.Task) fc.Result = res - return ec.marshalNDeleteTeamPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐDeleteTeamPayload(ctx, field.Selections, res) + return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTask(ctx, field.Selections, res) } -func (ec *executionContext) _Mutation_createTeam(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_deleteTask(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -11034,7 +10379,7 @@ func (ec *executionContext) _Mutation_createTeam(ctx context.Context, field grap ctx = graphql.WithFieldContext(ctx, fc) rawArgs := field.ArgumentMap(ec.Variables) - args, err := ec.field_Mutation_createTeam_args(ctx, rawArgs) + args, err := ec.field_Mutation_deleteTask_args(ctx, rawArgs) if err != nil { ec.Error(ctx, err) return graphql.Null @@ -11043,18 +10388,18 @@ func (ec *executionContext) _Mutation_createTeam(ctx context.Context, field grap resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { directive0 := func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().CreateTeam(rctx, args["input"].(NewTeam)) + return ec.resolvers.Mutation().DeleteTask(rctx, args["input"].(DeleteTaskInput)) } directive1 := func(ctx context.Context) (interface{}, error) { - roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN"}) + roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"}) if err != nil { return nil, err } - level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "ORG") + level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") if err != nil { return nil, err } - typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "ORG") + typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK") if err != nil { return nil, err } @@ -11071,10 +10416,10 @@ func (ec *executionContext) _Mutation_createTeam(ctx context.Context, field grap if tmp == nil { return nil, nil } - if data, ok := tmp.(*db.Team); ok { + if data, ok := tmp.(*DeleteTaskPayload); ok { return data, nil } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Team`, tmp) + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/graph.DeleteTaskPayload`, tmp) }) if err != nil { ec.Error(ctx, err) @@ -11086,9 +10431,527 @@ func (ec *executionContext) _Mutation_createTeam(ctx context.Context, field grap } return graphql.Null } - res := resTmp.(*db.Team) + res := resTmp.(*DeleteTaskPayload) fc.Result = res - return ec.marshalNTeam2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTeam(ctx, field.Selections, res) + return ec.marshalNDeleteTaskPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐDeleteTaskPayload(ctx, field.Selections, res) +} + +func (ec *executionContext) _Mutation_updateTaskDescription(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + IsResolver: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_updateTaskDescription_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().UpdateTaskDescription(rctx, args["input"].(UpdateTaskDescriptionInput)) + } + directive1 := func(ctx context.Context) (interface{}, error) { + roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"}) + if err != nil { + return nil, err + } + level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") + if err != nil { + return nil, err + } + typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK") + if err != nil { + return nil, err + } + if ec.directives.HasRole == nil { + return nil, errors.New("directive hasRole is not implemented") + } + return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*db.Task); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Task`, tmp) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*db.Task) + fc.Result = res + return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTask(ctx, field.Selections, res) +} + +func (ec *executionContext) _Mutation_updateTaskLocation(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + IsResolver: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_updateTaskLocation_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().UpdateTaskLocation(rctx, args["input"].(NewTaskLocation)) + } + directive1 := func(ctx context.Context) (interface{}, error) { + roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"}) + if err != nil { + return nil, err + } + level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") + if err != nil { + return nil, err + } + typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK") + if err != nil { + return nil, err + } + if ec.directives.HasRole == nil { + return nil, errors.New("directive hasRole is not implemented") + } + return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*UpdateTaskLocationPayload); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/graph.UpdateTaskLocationPayload`, tmp) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*UpdateTaskLocationPayload) + fc.Result = res + return ec.marshalNUpdateTaskLocationPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐUpdateTaskLocationPayload(ctx, field.Selections, res) +} + +func (ec *executionContext) _Mutation_updateTaskName(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + IsResolver: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_updateTaskName_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().UpdateTaskName(rctx, args["input"].(UpdateTaskName)) + } + directive1 := func(ctx context.Context) (interface{}, error) { + roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"}) + if err != nil { + return nil, err + } + level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") + if err != nil { + return nil, err + } + typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK") + if err != nil { + return nil, err + } + if ec.directives.HasRole == nil { + return nil, errors.New("directive hasRole is not implemented") + } + return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*db.Task); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Task`, tmp) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*db.Task) + fc.Result = res + return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTask(ctx, field.Selections, res) +} + +func (ec *executionContext) _Mutation_setTaskComplete(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + IsResolver: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_setTaskComplete_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().SetTaskComplete(rctx, args["input"].(SetTaskComplete)) + } + directive1 := func(ctx context.Context) (interface{}, error) { + roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"}) + if err != nil { + return nil, err + } + level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") + if err != nil { + return nil, err + } + typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK") + if err != nil { + return nil, err + } + if ec.directives.HasRole == nil { + return nil, errors.New("directive hasRole is not implemented") + } + return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*db.Task); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Task`, tmp) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*db.Task) + fc.Result = res + return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTask(ctx, field.Selections, res) +} + +func (ec *executionContext) _Mutation_updateTaskDueDate(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + IsResolver: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_updateTaskDueDate_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().UpdateTaskDueDate(rctx, args["input"].(UpdateTaskDueDate)) + } + directive1 := func(ctx context.Context) (interface{}, error) { + roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"}) + if err != nil { + return nil, err + } + level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") + if err != nil { + return nil, err + } + typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK") + if err != nil { + return nil, err + } + if ec.directives.HasRole == nil { + return nil, errors.New("directive hasRole is not implemented") + } + return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*db.Task); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Task`, tmp) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*db.Task) + fc.Result = res + return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTask(ctx, field.Selections, res) +} + +func (ec *executionContext) _Mutation_assignTask(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + IsResolver: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_assignTask_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().AssignTask(rctx, args["input"].(*AssignTaskInput)) + } + directive1 := func(ctx context.Context) (interface{}, error) { + roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"}) + if err != nil { + return nil, err + } + level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") + if err != nil { + return nil, err + } + typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK") + if err != nil { + return nil, err + } + if ec.directives.HasRole == nil { + return nil, errors.New("directive hasRole is not implemented") + } + return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*db.Task); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Task`, tmp) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*db.Task) + fc.Result = res + return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTask(ctx, field.Selections, res) +} + +func (ec *executionContext) _Mutation_unassignTask(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + IsResolver: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_unassignTask_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().UnassignTask(rctx, args["input"].(*UnassignTaskInput)) + } + directive1 := func(ctx context.Context) (interface{}, error) { + roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"}) + if err != nil { + return nil, err + } + level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") + if err != nil { + return nil, err + } + typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK") + if err != nil { + return nil, err + } + if ec.directives.HasRole == nil { + return nil, errors.New("directive hasRole is not implemented") + } + return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*db.Task); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Task`, tmp) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*db.Task) + fc.Result = res + return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTask(ctx, field.Selections, res) } func (ec *executionContext) _Mutation_createTeamMember(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { @@ -11313,6 +11176,154 @@ func (ec *executionContext) _Mutation_deleteTeamMember(ctx context.Context, fiel return ec.marshalNDeleteTeamMemberPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐDeleteTeamMemberPayload(ctx, field.Selections, res) } +func (ec *executionContext) _Mutation_deleteTeam(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + IsResolver: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_deleteTeam_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().DeleteTeam(rctx, args["input"].(DeleteTeam)) + } + directive1 := func(ctx context.Context) (interface{}, error) { + roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN"}) + if err != nil { + return nil, err + } + level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "TEAM") + if err != nil { + return nil, err + } + typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TEAM") + if err != nil { + return nil, err + } + if ec.directives.HasRole == nil { + return nil, errors.New("directive hasRole is not implemented") + } + return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*DeleteTeamPayload); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/graph.DeleteTeamPayload`, tmp) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*DeleteTeamPayload) + fc.Result = res + return ec.marshalNDeleteTeamPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐDeleteTeamPayload(ctx, field.Selections, res) +} + +func (ec *executionContext) _Mutation_createTeam(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + IsResolver: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_createTeam_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().CreateTeam(rctx, args["input"].(NewTeam)) + } + directive1 := func(ctx context.Context) (interface{}, error) { + roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN"}) + if err != nil { + return nil, err + } + level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "ORG") + if err != nil { + return nil, err + } + typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "ORG") + if err != nil { + return nil, err + } + if ec.directives.HasRole == nil { + return nil, errors.New("directive hasRole is not implemented") + } + return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*db.Team); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Team`, tmp) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*db.Team) + fc.Result = res + return ec.marshalNTeam2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTeam(ctx, field.Selections, res) +} + func (ec *executionContext) _Mutation_createUserAccount(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -21189,26 +21200,6 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Mutation") - case "createProject": - out.Values[i] = ec._Mutation_createProject(ctx, field) - if out.Values[i] == graphql.Null { - invalids++ - } - case "deleteProject": - out.Values[i] = ec._Mutation_deleteProject(ctx, field) - if out.Values[i] == graphql.Null { - invalids++ - } - case "updateProjectName": - out.Values[i] = ec._Mutation_updateProjectName(ctx, field) - if out.Values[i] == graphql.Null { - invalids++ - } - case "toggleProjectVisibility": - out.Values[i] = ec._Mutation_toggleProjectVisibility(ctx, field) - if out.Values[i] == graphql.Null { - invalids++ - } case "createProjectLabel": out.Values[i] = ec._Mutation_createProjectLabel(ctx, field) if out.Values[i] == graphql.Null { @@ -21254,48 +21245,23 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { invalids++ } - case "createTask": - out.Values[i] = ec._Mutation_createTask(ctx, field) + case "createProject": + out.Values[i] = ec._Mutation_createProject(ctx, field) if out.Values[i] == graphql.Null { invalids++ } - case "deleteTask": - out.Values[i] = ec._Mutation_deleteTask(ctx, field) + case "deleteProject": + out.Values[i] = ec._Mutation_deleteProject(ctx, field) if out.Values[i] == graphql.Null { invalids++ } - case "updateTaskDescription": - out.Values[i] = ec._Mutation_updateTaskDescription(ctx, field) + case "updateProjectName": + out.Values[i] = ec._Mutation_updateProjectName(ctx, field) if out.Values[i] == graphql.Null { invalids++ } - case "updateTaskLocation": - out.Values[i] = ec._Mutation_updateTaskLocation(ctx, field) - if out.Values[i] == graphql.Null { - invalids++ - } - case "updateTaskName": - out.Values[i] = ec._Mutation_updateTaskName(ctx, field) - if out.Values[i] == graphql.Null { - invalids++ - } - case "setTaskComplete": - out.Values[i] = ec._Mutation_setTaskComplete(ctx, field) - if out.Values[i] == graphql.Null { - invalids++ - } - case "updateTaskDueDate": - out.Values[i] = ec._Mutation_updateTaskDueDate(ctx, field) - if out.Values[i] == graphql.Null { - invalids++ - } - case "assignTask": - out.Values[i] = ec._Mutation_assignTask(ctx, field) - if out.Values[i] == graphql.Null { - invalids++ - } - case "unassignTask": - out.Values[i] = ec._Mutation_unassignTask(ctx, field) + case "toggleProjectVisibility": + out.Values[i] = ec._Mutation_toggleProjectVisibility(ctx, field) if out.Values[i] == graphql.Null { invalids++ } @@ -21409,13 +21375,48 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { invalids++ } - case "deleteTeam": - out.Values[i] = ec._Mutation_deleteTeam(ctx, field) + case "createTask": + out.Values[i] = ec._Mutation_createTask(ctx, field) if out.Values[i] == graphql.Null { invalids++ } - case "createTeam": - out.Values[i] = ec._Mutation_createTeam(ctx, field) + case "deleteTask": + out.Values[i] = ec._Mutation_deleteTask(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } + case "updateTaskDescription": + out.Values[i] = ec._Mutation_updateTaskDescription(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } + case "updateTaskLocation": + out.Values[i] = ec._Mutation_updateTaskLocation(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } + case "updateTaskName": + out.Values[i] = ec._Mutation_updateTaskName(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } + case "setTaskComplete": + out.Values[i] = ec._Mutation_setTaskComplete(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } + case "updateTaskDueDate": + out.Values[i] = ec._Mutation_updateTaskDueDate(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } + case "assignTask": + out.Values[i] = ec._Mutation_assignTask(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } + case "unassignTask": + out.Values[i] = ec._Mutation_unassignTask(ctx, field) if out.Values[i] == graphql.Null { invalids++ } @@ -21434,6 +21435,16 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { invalids++ } + case "deleteTeam": + out.Values[i] = ec._Mutation_deleteTeam(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } + case "createTeam": + out.Values[i] = ec._Mutation_createTeam(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } case "createUserAccount": out.Values[i] = ec._Mutation_createUserAccount(ctx, field) if out.Values[i] == graphql.Null { diff --git a/internal/graph/notification.resolvers.go b/internal/graph/notification.resolvers.go new file mode 100644 index 0000000..2011f08 --- /dev/null +++ b/internal/graph/notification.resolvers.go @@ -0,0 +1,92 @@ +package graph + +// This file will be automatically regenerated based on the schema, any resolver implementations +// will be copied through when generating and any unknown code will be moved to the end. + +import ( + "context" + "database/sql" + "errors" + "fmt" + "time" + + "github.com/google/uuid" + "github.com/jordanknott/taskcafe/internal/db" + "github.com/jordanknott/taskcafe/internal/logger" + log "github.com/sirupsen/logrus" +) + +func (r *notificationResolver) ID(ctx context.Context, obj *db.Notification) (uuid.UUID, error) { + return obj.NotificationID, nil +} + +func (r *notificationResolver) Entity(ctx context.Context, obj *db.Notification) (*NotificationEntity, error) { + logger.New(ctx).WithFields(log.Fields{"notificationID": obj.NotificationID}).Info("fetching entity for notification") + entity, err := r.Repository.GetEntityForNotificationID(ctx, obj.NotificationID) + logger.New(ctx).WithFields(log.Fields{"entityID": entity.EntityID}).Info("fetched entity") + if err != nil { + return &NotificationEntity{}, err + } + entityType := GetEntityType(entity.EntityType) + switch entityType { + case EntityTypeTask: + task, err := r.Repository.GetTaskByID(ctx, entity.EntityID) + if err != nil { + return &NotificationEntity{}, err + } + return &NotificationEntity{Type: entityType, ID: entity.EntityID, Name: task.Name}, err + + default: + panic(fmt.Errorf("not implemented")) + } +} + +func (r *notificationResolver) ActionType(ctx context.Context, obj *db.Notification) (ActionType, error) { + entity, err := r.Repository.GetEntityForNotificationID(ctx, obj.NotificationID) + if err != nil { + return ActionTypeTaskMemberAdded, err + } + actionType := GetActionType(entity.ActionType) + return actionType, nil +} + +func (r *notificationResolver) Actor(ctx context.Context, obj *db.Notification) (*NotificationActor, error) { + entity, err := r.Repository.GetEntityForNotificationID(ctx, obj.NotificationID) + if err != nil { + return &NotificationActor{}, err + } + logger.New(ctx).WithFields(log.Fields{"entityID": entity.ActorID}).Info("fetching actor") + user, err := r.Repository.GetUserAccountByID(ctx, entity.ActorID) + if err != nil { + return &NotificationActor{}, err + } + return &NotificationActor{ID: entity.ActorID, Name: user.FullName, Type: ActorTypeUser}, nil +} + +func (r *notificationResolver) CreatedAt(ctx context.Context, obj *db.Notification) (*time.Time, error) { + entity, err := r.Repository.GetEntityForNotificationID(ctx, obj.NotificationID) + if err != nil { + return &time.Time{}, err + } + return &entity.CreatedOn, nil +} + +func (r *queryResolver) Notifications(ctx context.Context) ([]db.Notification, error) { + userID, ok := GetUserID(ctx) + logger.New(ctx).Info("fetching notifications") + if !ok { + return []db.Notification{}, errors.New("user id is missing") + } + notifications, err := r.Repository.GetAllNotificationsForUserID(ctx, userID) + if err == sql.ErrNoRows { + return []db.Notification{}, nil + } else if err != nil { + return []db.Notification{}, err + } + return notifications, nil +} + +// Notification returns NotificationResolver implementation. +func (r *Resolver) Notification() NotificationResolver { return ¬ificationResolver{r} } + +type notificationResolver struct{ *Resolver } diff --git a/internal/graph/project.resolvers.go b/internal/graph/project.resolvers.go new file mode 100644 index 0000000..e01826a --- /dev/null +++ b/internal/graph/project.resolvers.go @@ -0,0 +1,406 @@ +package graph + +// This file will be automatically regenerated based on the schema, any resolver implementations +// will be copied through when generating and any unknown code will be moved to the end. + +import ( + "context" + "database/sql" + "errors" + "fmt" + "time" + + "github.com/google/uuid" + "github.com/jordanknott/taskcafe/internal/db" + "github.com/jordanknott/taskcafe/internal/logger" + "github.com/jordanknott/taskcafe/internal/utils" + log "github.com/sirupsen/logrus" + "github.com/vektah/gqlparser/v2/gqlerror" +) + +func (r *labelColorResolver) ID(ctx context.Context, obj *db.LabelColor) (uuid.UUID, error) { + return obj.LabelColorID, nil +} + +func (r *mutationResolver) CreateProjectLabel(ctx context.Context, input NewProjectLabel) (*db.ProjectLabel, error) { + createdAt := time.Now().UTC() + + var name sql.NullString + if input.Name != nil { + name = sql.NullString{ + *input.Name, + true, + } + } else { + name = sql.NullString{ + "", + false, + } + } + projectLabel, err := r.Repository.CreateProjectLabel(ctx, db.CreateProjectLabelParams{input.ProjectID, input.LabelColorID, createdAt, name}) + return &projectLabel, err +} + +func (r *mutationResolver) DeleteProjectLabel(ctx context.Context, input DeleteProjectLabel) (*db.ProjectLabel, error) { + label, err := r.Repository.GetProjectLabelByID(ctx, input.ProjectLabelID) + if err != nil { + return &db.ProjectLabel{}, err + } + err = r.Repository.DeleteProjectLabelByID(ctx, input.ProjectLabelID) + return &label, err +} + +func (r *mutationResolver) UpdateProjectLabel(ctx context.Context, input UpdateProjectLabel) (*db.ProjectLabel, error) { + label, err := r.Repository.UpdateProjectLabel(ctx, db.UpdateProjectLabelParams{ProjectLabelID: input.ProjectLabelID, LabelColorID: input.LabelColorID, Name: sql.NullString{String: input.Name, Valid: true}}) + return &label, err +} + +func (r *mutationResolver) UpdateProjectLabelName(ctx context.Context, input UpdateProjectLabelName) (*db.ProjectLabel, error) { + label, err := r.Repository.UpdateProjectLabelName(ctx, db.UpdateProjectLabelNameParams{ProjectLabelID: input.ProjectLabelID, Name: sql.NullString{String: input.Name, Valid: true}}) + return &label, err +} + +func (r *mutationResolver) UpdateProjectLabelColor(ctx context.Context, input UpdateProjectLabelColor) (*db.ProjectLabel, error) { + label, err := r.Repository.UpdateProjectLabelColor(ctx, db.UpdateProjectLabelColorParams{ProjectLabelID: input.ProjectLabelID, LabelColorID: input.LabelColorID}) + return &label, err +} + +func (r *mutationResolver) InviteProjectMembers(ctx context.Context, input InviteProjectMembers) (*InviteProjectMembersPayload, error) { + members := []Member{} + invitedMembers := []InvitedMember{} + for _, invitedMember := range input.Members { + if invitedMember.Email != nil && invitedMember.UserID != nil { + return &InviteProjectMembersPayload{Ok: false}, &gqlerror.Error{ + Message: "Both email and userID can not be used to invite a project member", + Extensions: map[string]interface{}{ + "code": "403", + }, + } + } else if invitedMember.Email == nil && invitedMember.UserID == nil { + return &InviteProjectMembersPayload{Ok: false}, &gqlerror.Error{ + Message: "Either email or userID must be set to invite a project member", + Extensions: map[string]interface{}{ + "code": "403", + }, + } + } + if invitedMember.UserID != nil { + // Invite by user ID + addedAt := time.Now().UTC() + _, err := r.Repository.CreateProjectMember(ctx, db.CreateProjectMemberParams{ProjectID: input.ProjectID, UserID: *invitedMember.UserID, AddedAt: addedAt, RoleCode: "member"}) + if err != nil { + return &InviteProjectMembersPayload{Ok: false}, err + } + user, err := r.Repository.GetUserAccountByID(ctx, *invitedMember.UserID) + if err != nil && err != sql.ErrNoRows { + return &InviteProjectMembersPayload{Ok: false}, err + + } + var url *string + if user.ProfileAvatarUrl.Valid { + url = &user.ProfileAvatarUrl.String + } + profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor} + + role, err := r.Repository.GetRoleForProjectMemberByUserID(ctx, db.GetRoleForProjectMemberByUserIDParams{UserID: *invitedMember.UserID, ProjectID: input.ProjectID}) + if err != nil { + return &InviteProjectMembersPayload{Ok: false}, err + } + members = append(members, Member{ + ID: *invitedMember.UserID, + FullName: user.FullName, + Username: user.Username, + ProfileIcon: profileIcon, + Role: &db.Role{Code: role.Code, Name: role.Name}, + }) + } else { + // Invite by email + + // if invited user does not exist, create entry + invitedUser, err := r.Repository.GetInvitedUserByEmail(ctx, *invitedMember.Email) + now := time.Now().UTC() + if err != nil { + if err == sql.ErrNoRows { + invitedUser, err = r.Repository.CreateInvitedUser(ctx, *invitedMember.Email) + if err != nil { + return &InviteProjectMembersPayload{Ok: false}, err + } + confirmToken, err := r.Repository.CreateConfirmToken(ctx, *invitedMember.Email) + if err != nil { + return &InviteProjectMembersPayload{Ok: false}, err + } + invite := utils.EmailInvite{To: *invitedMember.Email, FullName: *invitedMember.Email, ConfirmToken: confirmToken.ConfirmTokenID.String()} + err = utils.SendEmailInvite(r.EmailConfig, invite) + if err != nil { + logger.New(ctx).WithError(err).Error("issue sending email") + return &InviteProjectMembersPayload{Ok: false}, err + } + } else { + return &InviteProjectMembersPayload{Ok: false}, err + } + } + + _, err = r.Repository.CreateInvitedProjectMember(ctx, db.CreateInvitedProjectMemberParams{ + ProjectID: input.ProjectID, + UserAccountInvitedID: invitedUser.UserAccountInvitedID, + }) + if err != nil { + return &InviteProjectMembersPayload{Ok: false}, err + } + logger.New(ctx).Info("adding invited member") + invitedMembers = append(invitedMembers, InvitedMember{Email: *invitedMember.Email, InvitedOn: now}) + + } + } + return &InviteProjectMembersPayload{Ok: false, ProjectID: input.ProjectID, Members: members, InvitedMembers: invitedMembers}, nil +} + +func (r *mutationResolver) DeleteProjectMember(ctx context.Context, input DeleteProjectMember) (*DeleteProjectMemberPayload, error) { + user, err := r.Repository.GetUserAccountByID(ctx, input.UserID) + if err != nil { + return &DeleteProjectMemberPayload{Ok: false}, err + } + var url *string + if user.ProfileAvatarUrl.Valid { + url = &user.ProfileAvatarUrl.String + } + profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor} + role, err := r.Repository.GetRoleForProjectMemberByUserID(ctx, db.GetRoleForProjectMemberByUserIDParams{UserID: input.UserID, ProjectID: input.ProjectID}) + if err != nil { + return &DeleteProjectMemberPayload{Ok: false}, err + } + err = r.Repository.DeleteProjectMember(ctx, db.DeleteProjectMemberParams{UserID: input.UserID, ProjectID: input.ProjectID}) + if err != nil { + return &DeleteProjectMemberPayload{Ok: false}, err + } + return &DeleteProjectMemberPayload{Ok: true, Member: &Member{ + ID: input.UserID, + FullName: user.FullName, + ProfileIcon: profileIcon, + Role: &db.Role{Code: role.Code, Name: role.Name}, + }, ProjectID: input.ProjectID}, nil +} + +func (r *mutationResolver) UpdateProjectMemberRole(ctx context.Context, input UpdateProjectMemberRole) (*UpdateProjectMemberRolePayload, error) { + user, err := r.Repository.GetUserAccountByID(ctx, input.UserID) + if err != nil { + logger.New(ctx).WithError(err).Error("get user account") + return &UpdateProjectMemberRolePayload{Ok: false}, err + } + _, err = r.Repository.UpdateProjectMemberRole(ctx, db.UpdateProjectMemberRoleParams{ProjectID: input.ProjectID, + UserID: input.UserID, RoleCode: input.RoleCode.String()}) + if err != nil { + logger.New(ctx).WithError(err).Error("update project member role") + return &UpdateProjectMemberRolePayload{Ok: false}, err + } + role, err := r.Repository.GetRoleForProjectMemberByUserID(ctx, db.GetRoleForProjectMemberByUserIDParams{UserID: user.UserID, ProjectID: input.ProjectID}) + if err != nil { + logger.New(ctx).WithError(err).Error("get role for project member") + return &UpdateProjectMemberRolePayload{Ok: false}, err + } + var url *string + if user.ProfileAvatarUrl.Valid { + url = &user.ProfileAvatarUrl.String + } + profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor} + if user.ProfileAvatarUrl.Valid { + url = &user.ProfileAvatarUrl.String + } + member := Member{ID: user.UserID, FullName: user.FullName, ProfileIcon: profileIcon, + Role: &db.Role{Code: role.Code, Name: role.Name}, + } + return &UpdateProjectMemberRolePayload{Ok: true, Member: &member}, err +} + +func (r *mutationResolver) DeleteInvitedProjectMember(ctx context.Context, input DeleteInvitedProjectMember) (*DeleteInvitedProjectMemberPayload, error) { + member, err := r.Repository.GetProjectMemberInvitedIDByEmail(ctx, input.Email) + if err != nil { + return &DeleteInvitedProjectMemberPayload{}, err + } + err = r.Repository.DeleteInvitedProjectMemberByID(ctx, member.ProjectMemberInvitedID) + if err != nil { + return &DeleteInvitedProjectMemberPayload{}, err + } + return &DeleteInvitedProjectMemberPayload{ + InvitedMember: &InvitedMember{Email: member.Email, InvitedOn: member.InvitedOn}, + }, nil +} + +func (r *mutationResolver) CreateProject(ctx context.Context, input NewProject) (*db.Project, error) { + userID, ok := GetUserID(ctx) + if !ok { + return &db.Project{}, errors.New("user id is missing") + } + createdAt := time.Now().UTC() + logger.New(ctx).WithFields(log.Fields{"name": input.Name, "teamID": input.TeamID}).Info("creating new project") + var project db.Project + var err error + if input.TeamID == nil { + project, err = r.Repository.CreatePersonalProject(ctx, db.CreatePersonalProjectParams{ + CreatedAt: createdAt, + Name: input.Name, + }) + if err != nil { + logger.New(ctx).WithError(err).Error("error while creating project") + return &db.Project{}, err + } + logger.New(ctx).WithField("projectID", project.ProjectID).Info("creating personal project link") + } else { + project, err = r.Repository.CreateTeamProject(ctx, db.CreateTeamProjectParams{ + CreatedAt: createdAt, + Name: input.Name, + TeamID: *input.TeamID, + }) + if err != nil { + logger.New(ctx).WithError(err).Error("error while creating project") + return &db.Project{}, err + } + } + _, err = r.Repository.CreateProjectMember(ctx, db.CreateProjectMemberParams{ProjectID: project.ProjectID, UserID: userID, AddedAt: createdAt, RoleCode: "admin"}) + if err != nil { + logger.New(ctx).WithError(err).Error("error while creating initial project member") + return &db.Project{}, err + } + return &project, nil +} + +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) (*db.Project, error) { + project, err := r.Repository.UpdateProjectNameByID(ctx, db.UpdateProjectNameByIDParams{ProjectID: input.ProjectID, Name: input.Name}) + if err != nil { + return &db.Project{}, err + } + return &project, nil +} + +func (r *mutationResolver) ToggleProjectVisibility(ctx context.Context, input ToggleProjectVisibility) (*ToggleProjectVisibilityPayload, error) { + if input.IsPublic { + project, err := r.Repository.SetPublicOn(ctx, db.SetPublicOnParams{ProjectID: input.ProjectID, PublicOn: sql.NullTime{Valid: true, Time: time.Now().UTC()}}) + return &ToggleProjectVisibilityPayload{Project: &project}, err + } + project, err := r.Repository.SetPublicOn(ctx, db.SetPublicOnParams{ProjectID: input.ProjectID, PublicOn: sql.NullTime{Valid: false, Time: time.Time{}}}) + return &ToggleProjectVisibilityPayload{Project: &project}, err +} + +func (r *projectResolver) ID(ctx context.Context, obj *db.Project) (uuid.UUID, error) { + return obj.ProjectID, nil +} + +func (r *projectResolver) Team(ctx context.Context, obj *db.Project) (*db.Team, error) { + team, err := r.Repository.GetTeamByID(ctx, obj.TeamID) + if err != nil { + if err == sql.ErrNoRows { + return nil, nil + } + logger.New(ctx).WithFields(log.Fields{"teamID": obj.TeamID, "projectID": obj.ProjectID}).WithError(err).Error("issue while getting team for project") + return &team, err + } + return &team, nil +} + +func (r *projectResolver) TaskGroups(ctx context.Context, obj *db.Project) ([]db.TaskGroup, error) { + return r.Repository.GetTaskGroupsForProject(ctx, obj.ProjectID) +} + +func (r *projectResolver) Members(ctx context.Context, obj *db.Project) ([]Member, error) { + members := []Member{} + projectMembers, err := r.Repository.GetProjectMembersForProjectID(ctx, obj.ProjectID) + if err != nil { + logger.New(ctx).WithError(err).Error("get project members for project id") + return members, err + } + + for _, projectMember := range projectMembers { + user, err := r.Repository.GetUserAccountByID(ctx, projectMember.UserID) + if err != nil { + logger.New(ctx).WithError(err).Error("get user account by ID") + return members, err + } + var url *string + if user.ProfileAvatarUrl.Valid { + url = &user.ProfileAvatarUrl.String + } + role, err := r.Repository.GetRoleForProjectMemberByUserID(ctx, db.GetRoleForProjectMemberByUserIDParams{UserID: user.UserID, ProjectID: obj.ProjectID}) + if err != nil { + logger.New(ctx).WithError(err).Error("get role for projet member by user ID") + return members, err + } + profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor} + members = append(members, Member{ID: user.UserID, FullName: user.FullName, ProfileIcon: profileIcon, + Username: user.Username, Role: &db.Role{Code: role.Code, Name: role.Name}, + }) + } + return members, nil +} + +func (r *projectResolver) InvitedMembers(ctx context.Context, obj *db.Project) ([]InvitedMember, error) { + members, err := r.Repository.GetInvitedMembersForProjectID(ctx, obj.ProjectID) + if err != nil && err == sql.ErrNoRows { + return []InvitedMember{}, nil + } + invited := []InvitedMember{} + for _, member := range members { + invited = append(invited, InvitedMember{Email: member.Email, InvitedOn: member.InvitedOn}) + } + return invited, err +} + +func (r *projectResolver) PublicOn(ctx context.Context, obj *db.Project) (*time.Time, error) { + if obj.PublicOn.Valid { + return &obj.PublicOn.Time, nil + } + return nil, nil +} + +func (r *projectResolver) Permission(ctx context.Context, obj *db.Project) (*ProjectPermission, error) { + panic(fmt.Errorf("not implemented")) +} + +func (r *projectResolver) Labels(ctx context.Context, obj *db.Project) ([]db.ProjectLabel, error) { + labels, err := r.Repository.GetProjectLabelsForProject(ctx, obj.ProjectID) + return labels, err +} + +func (r *projectLabelResolver) ID(ctx context.Context, obj *db.ProjectLabel) (uuid.UUID, error) { + return obj.ProjectLabelID, nil +} + +func (r *projectLabelResolver) LabelColor(ctx context.Context, obj *db.ProjectLabel) (*db.LabelColor, error) { + labelColor, err := r.Repository.GetLabelColorByID(ctx, obj.LabelColorID) + if err != nil { + return &db.LabelColor{}, err + } + return &labelColor, nil +} + +func (r *projectLabelResolver) Name(ctx context.Context, obj *db.ProjectLabel) (*string, error) { + var name *string + if obj.Name.Valid { + name = &obj.Name.String + } + return name, nil +} + +// LabelColor returns LabelColorResolver implementation. +func (r *Resolver) LabelColor() LabelColorResolver { return &labelColorResolver{r} } + +// Project returns ProjectResolver implementation. +func (r *Resolver) Project() ProjectResolver { return &projectResolver{r} } + +// ProjectLabel returns ProjectLabelResolver implementation. +func (r *Resolver) ProjectLabel() ProjectLabelResolver { return &projectLabelResolver{r} } + +type labelColorResolver struct{ *Resolver } +type projectResolver struct{ *Resolver } +type projectLabelResolver struct{ *Resolver } diff --git a/internal/graph/schema.graphqls b/internal/graph/schema.graphqls deleted file mode 100644 index 185f440..0000000 --- a/internal/graph/schema.graphqls +++ /dev/null @@ -1,1002 +0,0 @@ -scalar Time -scalar UUID -scalar Upload - -enum RoleCode { - owner - admin - member - observer -} - -type ProjectLabel { - id: ID! - createdDate: Time! - labelColor: LabelColor! - name: String -} - -type LabelColor { - id: ID! - name: String! - position: Float! - colorHex: String! -} - -type TaskLabel { - id: ID! - projectLabel: ProjectLabel! - assignedDate: Time! -} - -type ProfileIcon { - url: String - initials: String - bgColor: String -} - -type OwnersList { - projects: [UUID!]! - teams: [UUID!]! -} - -type Member { - id: ID! - role: Role! - fullName: String! - username: String! - profileIcon: ProfileIcon! - owned: OwnedList! - member: MemberList! -} - -type Role { - code: String! - name: String! -} - -type OwnedList { - teams: [Team!]! - projects: [Project!]! -} - -type MemberList { - teams: [Team!]! - projects: [Project!]! -} - -type UserAccount { - id: ID! - email: String! - createdAt: Time! - fullName: String! - initials: String! - bio: String! - role: Role! - username: String! - profileIcon: ProfileIcon! - owned: OwnedList! - member: MemberList! -} - -type InvitedUserAccount { - id: ID! - email: String! - invitedOn: Time! - member: MemberList! -} - -type Team { - id: ID! - createdAt: Time! - name: String! - permission: TeamPermission! - members: [Member!]! -} - - -type InvitedMember { - email: String! - invitedOn: Time! -} - -type TeamPermission { - team: RoleCode! - org: RoleCode! -} - -type ProjectPermission { - team: RoleCode! - project: RoleCode! - org: RoleCode! -} - -type Project { - id: ID! - createdAt: Time! - name: String! - team: Team - taskGroups: [TaskGroup!]! - members: [Member!]! - invitedMembers: [InvitedMember!]! - publicOn: Time - permission: ProjectPermission! - labels: [ProjectLabel!]! -} - -type TaskGroup { - id: ID! - projectID: String! - createdAt: Time! - name: String! - position: Float! - tasks: [Task!]! -} - -type ChecklistBadge { - complete: Int! - total: Int! -} - -type CommentsBadge { - total: Int! - unread: Boolean! -} - -type TaskBadges { - checklist: ChecklistBadge - comments: CommentsBadge -} - -type CausedBy { - id: ID! - fullName: String! - profileIcon: ProfileIcon -} - -type TaskActivityData { - name: String! - value: String! -} - -enum ActivityType { - TASK_ADDED - TASK_MOVED - TASK_MARKED_COMPLETE - TASK_MARKED_INCOMPLETE - TASK_DUE_DATE_CHANGED - TASK_DUE_DATE_ADDED - TASK_DUE_DATE_REMOVED - TASK_CHECKLIST_CHANGED - TASK_CHECKLIST_ADDED - TASK_CHECKLIST_REMOVED -} - -type TaskActivity { - id: ID! - type: ActivityType! - data: [TaskActivityData!]! - causedBy: CausedBy! - createdAt: Time! -} - -type Task { - id: ID! - taskGroup: TaskGroup! - createdAt: Time! - name: String! - position: Float! - description: String - dueDate: Time - hasTime: Boolean! - complete: Boolean! - completedAt: Time - assigned: [Member!]! - labels: [TaskLabel!]! - checklists: [TaskChecklist!]! - badges: TaskBadges! - activity: [TaskActivity!]! - comments: [TaskComment!]! -} - -type CreatedBy { - id: ID! - fullName: String! - profileIcon: ProfileIcon! -} - -type TaskComment { - id: ID! - createdAt: Time! - updatedAt: Time - message: String! - createdBy: CreatedBy! - pinned: Boolean! -} - -type Organization { - id: ID! - name: String! -} - -type TaskChecklistItem { - id: ID! - name: String! - taskChecklistID: UUID! - complete: Boolean! - position: Float! - dueDate: Time! -} - -type TaskChecklist { - id: ID! - name: String! - position: Float! - items: [TaskChecklistItem!]! -} - -enum ShareStatus { - INVITED - JOINED -} - -enum RoleLevel { - ADMIN - MEMBER -} - -enum ActionLevel { - ORG - TEAM - PROJECT -} - -enum ObjectType { - ORG - TEAM - PROJECT - TASK - TASK_GROUP - TASK_CHECKLIST - TASK_CHECKLIST_ITEM -} - -directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION -directive @requiresUser on FIELD_DEFINITION - -type Query { - organizations: [Organization!]! - users: [UserAccount!]! - invitedUsers: [InvitedUserAccount!]! - findUser(input: FindUser!): UserAccount! - findProject(input: FindProject!): - Project! - findTask(input: FindTask!): Task! - projects(input: ProjectsFilter): [Project!]! - findTeam(input: FindTeam!): Team! - teams: [Team!]! - myTasks(input: MyTasks!): MyTasksPayload! - labelColors: [LabelColor!]! - taskGroups: [TaskGroup!]! - me: MePayload -} - - -type Mutation - -enum MyTasksStatus { - ALL - INCOMPLETE - COMPLETE_ALL - COMPLETE_TODAY - COMPLETE_YESTERDAY - COMPLETE_ONE_WEEK - COMPLETE_TWO_WEEK - COMPLETE_THREE_WEEK -} - -enum MyTasksSort { - NONE - PROJECT - DUE_DATE -} - -input MyTasks { - status: MyTasksStatus! - sort: MyTasksSort! -} - -type ProjectTaskMapping { - projectID: UUID! - taskID: UUID! -} - -type MyTasksPayload { - tasks: [Task!]! - projects: [ProjectTaskMapping!]! -} - -type TeamRole { - teamID: UUID! - roleCode: RoleCode! -} - -type ProjectRole { - projectID: UUID! - roleCode: RoleCode! -} - -type MePayload { - user: UserAccount! - organization: RoleCode - teamRoles: [TeamRole!]! - projectRoles: [ProjectRole!]! -} - -input ProjectsFilter { - teamID: UUID -} - -input FindUser { - userID: UUID! -} - -input FindProject { - projectID: UUID! -} - -input FindTask { - taskID: UUID! -} - -input FindTeam { - teamID: UUID! -} - -extend type Query { - notifications: [Notification!]! -} - -enum EntityType { - TASK -} - -enum ActorType { - USER -} - -enum ActionType { - TASK_MEMBER_ADDED -} - -type NotificationActor { - id: UUID! - type: ActorType! - name: String! -} - -type NotificationEntity { - id: UUID! - type: EntityType! - name: String! -} - -type Notification { - id: ID! - entity: NotificationEntity! - actionType: ActionType! - actor: NotificationActor! - read: Boolean! - createdAt: Time! -} - -extend type Mutation { - createProject(input: NewProject!): Project! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM) - deleteProject(input: DeleteProject!): - DeleteProjectPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) - updateProjectName(input: UpdateProjectName): - Project! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) - toggleProjectVisibility(input: ToggleProjectVisibility!): ToggleProjectVisibilityPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) -} - -input ToggleProjectVisibility { - projectID: UUID! - isPublic: Boolean! -} - -type ToggleProjectVisibilityPayload { - project: Project! -} - -input NewProject { - teamID: UUID - name: String! -} - -input UpdateProjectName { - projectID: UUID! - name: String! -} - -input DeleteProject { - projectID: UUID! -} - -type DeleteProjectPayload { - ok: Boolean! - project: Project! -} - -extend type Mutation { - createProjectLabel(input: NewProjectLabel!): - ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) - deleteProjectLabel(input: DeleteProjectLabel!): - ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) - updateProjectLabel(input: UpdateProjectLabel!): - ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) - updateProjectLabelName(input: UpdateProjectLabelName!): - ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) - updateProjectLabelColor(input: UpdateProjectLabelColor!): - ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) -} - -input NewProjectLabel { - projectID: UUID! - labelColorID: UUID! - name: String -} - -input DeleteProjectLabel { - projectLabelID: UUID! -} - -input UpdateProjectLabelName { - projectLabelID: UUID! - name: String! -} - -input UpdateProjectLabel { - projectLabelID: UUID! - labelColorID: UUID! - name: String! -} - -input UpdateProjectLabelColor { - projectLabelID: UUID! - labelColorID: UUID! -} - -extend type Mutation { - inviteProjectMembers(input: InviteProjectMembers!): - InviteProjectMembersPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) - deleteProjectMember(input: DeleteProjectMember!): - DeleteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) - updateProjectMemberRole(input: UpdateProjectMemberRole!): - UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) - -deleteInvitedProjectMember(input: DeleteInvitedProjectMember!): - DeleteInvitedProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) -} - -input DeleteInvitedProjectMember { - projectID: UUID! - email: String! -} - -type DeleteInvitedProjectMemberPayload { - invitedMember: InvitedMember! -} - -input MemberInvite { - userID: UUID - email: String -} - -input InviteProjectMembers { - projectID: UUID! - members: [MemberInvite!]! -} - -type InviteProjectMembersPayload { - ok: Boolean! - projectID: UUID! - members: [Member!]! - invitedMembers: [InvitedMember!]! -} - -input DeleteProjectMember { - projectID: UUID! - userID: UUID! -} - -type DeleteProjectMemberPayload { - ok: Boolean! - member: Member! - projectID: UUID! -} - -input UpdateProjectMemberRole { - projectID: UUID! - userID: UUID! - roleCode: RoleCode! -} - -type UpdateProjectMemberRolePayload { - ok: Boolean! - member: Member! -} - -extend type Mutation { - createTask(input: NewTask!): - Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_GROUP) - deleteTask(input: DeleteTaskInput!): - DeleteTaskPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - - updateTaskDescription(input: UpdateTaskDescriptionInput!): - Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - updateTaskLocation(input: NewTaskLocation!): - UpdateTaskLocationPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - updateTaskName(input: UpdateTaskName!): - Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - setTaskComplete(input: SetTaskComplete!): - Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - updateTaskDueDate(input: UpdateTaskDueDate!): - Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - - assignTask(input: AssignTaskInput): - Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - unassignTask(input: UnassignTaskInput): - Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) -} - -input NewTask { - taskGroupID: UUID! - name: String! - position: Float! - assigned: [UUID!] -} - -input AssignTaskInput { - taskID: UUID! - userID: UUID! -} - -input UnassignTaskInput { - taskID: UUID! - userID: UUID! -} - -input UpdateTaskDescriptionInput { - taskID: UUID! - description: String! -} - -type UpdateTaskLocationPayload { - previousTaskGroupID: UUID! - task: Task! -} - -input UpdateTaskDueDate { - taskID: UUID! - hasTime: Boolean! - dueDate: Time -} - -input SetTaskComplete { - taskID: UUID! - complete: Boolean! -} - -input NewTaskLocation { - taskID: UUID! - taskGroupID: UUID! - position: Float! -} - -input DeleteTaskInput { - taskID: UUID! -} - -type DeleteTaskPayload { - taskID: UUID! -} - -input UpdateTaskName { - taskID: UUID! - name: String! -} - -extend type Mutation { - createTaskChecklist(input: CreateTaskChecklist!): - TaskChecklist! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - deleteTaskChecklist(input: DeleteTaskChecklist!): - DeleteTaskChecklistPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_CHECKLIST) - updateTaskChecklistName(input: UpdateTaskChecklistName!): - TaskChecklist! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_CHECKLIST) - createTaskChecklistItem(input: CreateTaskChecklistItem!): - TaskChecklistItem! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_CHECKLIST) - updateTaskChecklistLocation(input: UpdateTaskChecklistLocation!): - UpdateTaskChecklistLocationPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_CHECKLIST) - - updateTaskChecklistItemName(input: UpdateTaskChecklistItemName!): - TaskChecklistItem! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_CHECKLIST_ITEM) - setTaskChecklistItemComplete(input: SetTaskChecklistItemComplete!): - TaskChecklistItem! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_CHECKLIST_ITEM) - deleteTaskChecklistItem(input: DeleteTaskChecklistItem!): - DeleteTaskChecklistItemPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_CHECKLIST_ITEM) - updateTaskChecklistItemLocation(input: UpdateTaskChecklistItemLocation!): - UpdateTaskChecklistItemLocationPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_CHECKLIST_ITEM) - -} - -input UpdateTaskChecklistItemLocation { - taskChecklistID: UUID! - taskChecklistItemID: UUID! - position: Float! -} - -type UpdateTaskChecklistItemLocationPayload { - taskChecklistID: UUID! - prevChecklistID: UUID! - checklistItem: TaskChecklistItem! -} - -input UpdateTaskChecklistLocation { - taskChecklistID: UUID! - position: Float! -} - -type UpdateTaskChecklistLocationPayload { - checklist: TaskChecklist! -} - -input CreateTaskChecklist { - taskID: UUID! - name: String! - position: Float! -} - -type DeleteTaskChecklistItemPayload { - ok: Boolean! - taskChecklistItem: TaskChecklistItem! -} - -input CreateTaskChecklistItem { - taskChecklistID: UUID! - name: String! - position: Float! -} - -input SetTaskChecklistItemComplete { - taskChecklistItemID: UUID! - complete: Boolean! -} - -input DeleteTaskChecklistItem { - taskChecklistItemID: UUID! -} - -input UpdateTaskChecklistItemName { - taskChecklistItemID: UUID! - name: String! -} - -input UpdateTaskChecklistName { - taskChecklistID: UUID! - name: String! -} -input DeleteTaskChecklist { - taskChecklistID: UUID! -} -type DeleteTaskChecklistPayload { - ok: Boolean! - taskChecklist: TaskChecklist! -} - -extend type Mutation { - createTaskComment(input: CreateTaskComment): - CreateTaskCommentPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - deleteTaskComment(input: DeleteTaskComment): - DeleteTaskCommentPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - updateTaskComment(input: UpdateTaskComment): - UpdateTaskCommentPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) -} - -input CreateTaskComment { - taskID: UUID! - message: String! -} - -type CreateTaskCommentPayload { - taskID: UUID! - comment: TaskComment! -} - -input UpdateTaskComment { - commentID: UUID! - message: String! -} - -type UpdateTaskCommentPayload { - taskID: UUID! - comment: TaskComment! -} - -input DeleteTaskComment { - commentID: UUID! -} - -type DeleteTaskCommentPayload { - taskID: UUID! - commentID: UUID! -} - -extend type Mutation { - createTaskGroup(input: NewTaskGroup!): - TaskGroup! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) - updateTaskGroupLocation(input: NewTaskGroupLocation!): - TaskGroup! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_GROUP) - updateTaskGroupName(input: UpdateTaskGroupName!): - TaskGroup! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_GROUP) - deleteTaskGroup(input: DeleteTaskGroupInput!): - DeleteTaskGroupPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_GROUP) - duplicateTaskGroup(input: DuplicateTaskGroup!): - DuplicateTaskGroupPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_GROUP) - sortTaskGroup(input: SortTaskGroup!): - SortTaskGroupPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_GROUP) - deleteTaskGroupTasks(input: DeleteTaskGroupTasks!): - DeleteTaskGroupTasksPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_GROUP) -} - -input DeleteTaskGroupTasks { - taskGroupID: UUID! -} - -type DeleteTaskGroupTasksPayload { - taskGroupID: UUID! - tasks: [UUID!]! -} - -input TaskPositionUpdate { - taskID: UUID! - position: Float! -} - -type SortTaskGroupPayload { - taskGroupID: UUID! - tasks: [Task!]! -} - -input SortTaskGroup { - taskGroupID: UUID! - tasks: [TaskPositionUpdate!]! -} - -input DuplicateTaskGroup { - projectID: UUID! - taskGroupID: UUID! - name: String! - position: Float! -} - -type DuplicateTaskGroupPayload { - taskGroup: TaskGroup! -} - -input NewTaskGroupLocation { - taskGroupID: UUID! - position: Float! -} - -input UpdateTaskGroupName { - taskGroupID: UUID! - name: String! -} - -input DeleteTaskGroupInput { - taskGroupID: UUID! -} - -type DeleteTaskGroupPayload { - ok: Boolean! - affectedRows: Int! - taskGroup: TaskGroup! -} - -input NewTaskGroup { - projectID: UUID! - name: String! - position: Float! -} - -extend type Mutation { - addTaskLabel(input: AddTaskLabelInput): - Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - removeTaskLabel(input: RemoveTaskLabelInput): - Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - toggleTaskLabel(input: ToggleTaskLabelInput!): - ToggleTaskLabelPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) - -} - -input AddTaskLabelInput { - taskID: UUID! - projectLabelID: UUID! -} - -input RemoveTaskLabelInput { - taskID: UUID! - taskLabelID: UUID! -} - -input ToggleTaskLabelInput { - taskID: UUID! - projectLabelID: UUID! -} - -type ToggleTaskLabelPayload { - active: Boolean! - task: Task! -} - -extend type Mutation { - deleteTeam(input: DeleteTeam!): - DeleteTeamPayload! @hasRole(roles:[ ADMIN], level: TEAM, type: TEAM) - createTeam(input: NewTeam!): - Team! @hasRole(roles: [ADMIN], level: ORG, type: ORG) -} - -input NewTeam { - name: String! - organizationID: UUID! -} - -input DeleteTeam { - teamID: UUID! -} - -type DeleteTeamPayload { - ok: Boolean! - team: Team! - projects: [Project!]! -} - -extend type Mutation { - createTeamMember(input: CreateTeamMember!): - CreateTeamMemberPayload! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM) - updateTeamMemberRole(input: UpdateTeamMemberRole!): - UpdateTeamMemberRolePayload! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM) - deleteTeamMember(input: DeleteTeamMember!): - DeleteTeamMemberPayload! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM) - -} - -input DeleteTeamMember { - teamID: UUID! - userID: UUID! - newOwnerID: UUID -} - -type DeleteTeamMemberPayload { - teamID: UUID! - userID: UUID! - affectedProjects: [Project!]! -} - -input CreateTeamMember { - userID: UUID! - teamID: UUID! -} - -type CreateTeamMemberPayload { - team: Team! - teamMember: Member! -} - -input UpdateTeamMemberRole { - teamID: UUID! - userID: UUID! - roleCode: RoleCode! -} - -type UpdateTeamMemberRolePayload { - ok: Boolean! - teamID: UUID! - member: Member! -} - -extend type Mutation { - createUserAccount(input: NewUserAccount!): - UserAccount! @hasRole(roles: [ADMIN], level: ORG, type: ORG) - deleteUserAccount(input: DeleteUserAccount!): - DeleteUserAccountPayload! @hasRole(roles: [ADMIN], level: ORG, type: ORG) - deleteInvitedUserAccount(input: DeleteInvitedUserAccount!): - DeleteInvitedUserAccountPayload! @hasRole(roles: [ADMIN], level: ORG, type: ORG) - - logoutUser(input: LogoutUser!): Boolean! - clearProfileAvatar: UserAccount! - - updateUserPassword(input: UpdateUserPassword!): UpdateUserPasswordPayload! - updateUserRole(input: UpdateUserRole!): - UpdateUserRolePayload! @hasRole(roles: [ADMIN], level: ORG, type: ORG) - updateUserInfo(input: UpdateUserInfo!): - UpdateUserInfoPayload! @hasRole(roles: [ADMIN], level: ORG, type: ORG) -} - -extend type Query { - searchMembers(input: MemberSearchFilter!): [MemberSearchResult!]! -} - -input DeleteInvitedUserAccount { - invitedUserID: UUID! -} - -type DeleteInvitedUserAccountPayload { - invitedUser: InvitedUserAccount! -} - -input MemberSearchFilter { - searchFilter: String! - projectID: UUID -} - - -type MemberSearchResult { - similarity: Int! - id: String! - user: UserAccount - status: ShareStatus! -} - -type UpdateUserInfoPayload { - user: UserAccount! -} - -input UpdateUserInfo { - name: String! - initials: String! - email: String! - bio: String! -} - -input UpdateUserPassword { - userID: UUID! - password: String! -} - -type UpdateUserPasswordPayload { - ok: Boolean! - user: UserAccount! -} - -input UpdateUserRole { - userID: UUID! - roleCode: RoleCode! -} - -type UpdateUserRolePayload { - user: UserAccount! -} - -input NewUserAccount { - username: String! - email: String! - fullName: String! - initials: String! - password: String! - roleCode: String! -} - -input LogoutUser { - userID: UUID! -} - -input DeleteUserAccount { - userID: UUID! - newOwnerID: UUID -} - -type DeleteUserAccountPayload { - ok: Boolean! - userAccount: UserAccount! -} diff --git a/internal/graph/schema.resolvers.go b/internal/graph/schema.resolvers.go index 01c7ba9..3033028 100644 --- a/internal/graph/schema.resolvers.go +++ b/internal/graph/schema.resolvers.go @@ -6,1278 +6,21 @@ package graph import ( "context" "database/sql" - "encoding/json" "errors" - "fmt" "time" "github.com/google/uuid" "github.com/jinzhu/now" "github.com/jordanknott/taskcafe/internal/db" "github.com/jordanknott/taskcafe/internal/logger" - "github.com/jordanknott/taskcafe/internal/utils" - "github.com/lithammer/fuzzysearch/fuzzy" log "github.com/sirupsen/logrus" "github.com/vektah/gqlparser/v2/gqlerror" - "golang.org/x/crypto/bcrypt" ) -func (r *labelColorResolver) ID(ctx context.Context, obj *db.LabelColor) (uuid.UUID, error) { - return obj.LabelColorID, nil -} - -func (r *mutationResolver) CreateProject(ctx context.Context, input NewProject) (*db.Project, error) { - userID, ok := GetUserID(ctx) - if !ok { - return &db.Project{}, errors.New("user id is missing") - } - createdAt := time.Now().UTC() - logger.New(ctx).WithFields(log.Fields{"name": input.Name, "teamID": input.TeamID}).Info("creating new project") - var project db.Project - var err error - if input.TeamID == nil { - project, err = r.Repository.CreatePersonalProject(ctx, db.CreatePersonalProjectParams{ - CreatedAt: createdAt, - Name: input.Name, - }) - if err != nil { - logger.New(ctx).WithError(err).Error("error while creating project") - return &db.Project{}, err - } - logger.New(ctx).WithField("projectID", project.ProjectID).Info("creating personal project link") - } else { - project, err = r.Repository.CreateTeamProject(ctx, db.CreateTeamProjectParams{ - CreatedAt: createdAt, - Name: input.Name, - TeamID: *input.TeamID, - }) - if err != nil { - logger.New(ctx).WithError(err).Error("error while creating project") - return &db.Project{}, err - } - } - _, err = r.Repository.CreateProjectMember(ctx, db.CreateProjectMemberParams{ProjectID: project.ProjectID, UserID: userID, AddedAt: createdAt, RoleCode: "admin"}) - if err != nil { - logger.New(ctx).WithError(err).Error("error while creating initial project member") - return &db.Project{}, err - } - return &project, nil -} - -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) (*db.Project, error) { - project, err := r.Repository.UpdateProjectNameByID(ctx, db.UpdateProjectNameByIDParams{ProjectID: input.ProjectID, Name: input.Name}) - if err != nil { - return &db.Project{}, err - } - return &project, nil -} - -func (r *mutationResolver) ToggleProjectVisibility(ctx context.Context, input ToggleProjectVisibility) (*ToggleProjectVisibilityPayload, error) { - if input.IsPublic { - project, err := r.Repository.SetPublicOn(ctx, db.SetPublicOnParams{ProjectID: input.ProjectID, PublicOn: sql.NullTime{Valid: true, Time: time.Now().UTC()}}) - return &ToggleProjectVisibilityPayload{Project: &project}, err - } - project, err := r.Repository.SetPublicOn(ctx, db.SetPublicOnParams{ProjectID: input.ProjectID, PublicOn: sql.NullTime{Valid: false, Time: time.Time{}}}) - return &ToggleProjectVisibilityPayload{Project: &project}, err -} - -func (r *mutationResolver) CreateProjectLabel(ctx context.Context, input NewProjectLabel) (*db.ProjectLabel, error) { - createdAt := time.Now().UTC() - - var name sql.NullString - if input.Name != nil { - name = sql.NullString{ - *input.Name, - true, - } - } else { - name = sql.NullString{ - "", - false, - } - } - projectLabel, err := r.Repository.CreateProjectLabel(ctx, db.CreateProjectLabelParams{input.ProjectID, input.LabelColorID, createdAt, name}) - return &projectLabel, err -} - -func (r *mutationResolver) DeleteProjectLabel(ctx context.Context, input DeleteProjectLabel) (*db.ProjectLabel, error) { - label, err := r.Repository.GetProjectLabelByID(ctx, input.ProjectLabelID) - if err != nil { - return &db.ProjectLabel{}, err - } - err = r.Repository.DeleteProjectLabelByID(ctx, input.ProjectLabelID) - return &label, err -} - -func (r *mutationResolver) UpdateProjectLabel(ctx context.Context, input UpdateProjectLabel) (*db.ProjectLabel, error) { - label, err := r.Repository.UpdateProjectLabel(ctx, db.UpdateProjectLabelParams{ProjectLabelID: input.ProjectLabelID, LabelColorID: input.LabelColorID, Name: sql.NullString{String: input.Name, Valid: true}}) - return &label, err -} - -func (r *mutationResolver) UpdateProjectLabelName(ctx context.Context, input UpdateProjectLabelName) (*db.ProjectLabel, error) { - label, err := r.Repository.UpdateProjectLabelName(ctx, db.UpdateProjectLabelNameParams{ProjectLabelID: input.ProjectLabelID, Name: sql.NullString{String: input.Name, Valid: true}}) - return &label, err -} - -func (r *mutationResolver) UpdateProjectLabelColor(ctx context.Context, input UpdateProjectLabelColor) (*db.ProjectLabel, error) { - label, err := r.Repository.UpdateProjectLabelColor(ctx, db.UpdateProjectLabelColorParams{ProjectLabelID: input.ProjectLabelID, LabelColorID: input.LabelColorID}) - return &label, err -} - -func (r *mutationResolver) InviteProjectMembers(ctx context.Context, input InviteProjectMembers) (*InviteProjectMembersPayload, error) { - members := []Member{} - invitedMembers := []InvitedMember{} - for _, invitedMember := range input.Members { - if invitedMember.Email != nil && invitedMember.UserID != nil { - return &InviteProjectMembersPayload{Ok: false}, &gqlerror.Error{ - Message: "Both email and userID can not be used to invite a project member", - Extensions: map[string]interface{}{ - "code": "403", - }, - } - } else if invitedMember.Email == nil && invitedMember.UserID == nil { - return &InviteProjectMembersPayload{Ok: false}, &gqlerror.Error{ - Message: "Either email or userID must be set to invite a project member", - Extensions: map[string]interface{}{ - "code": "403", - }, - } - } - if invitedMember.UserID != nil { - // Invite by user ID - addedAt := time.Now().UTC() - _, err := r.Repository.CreateProjectMember(ctx, db.CreateProjectMemberParams{ProjectID: input.ProjectID, UserID: *invitedMember.UserID, AddedAt: addedAt, RoleCode: "member"}) - if err != nil { - return &InviteProjectMembersPayload{Ok: false}, err - } - user, err := r.Repository.GetUserAccountByID(ctx, *invitedMember.UserID) - if err != nil && err != sql.ErrNoRows { - return &InviteProjectMembersPayload{Ok: false}, err - - } - var url *string - if user.ProfileAvatarUrl.Valid { - url = &user.ProfileAvatarUrl.String - } - profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor} - - role, err := r.Repository.GetRoleForProjectMemberByUserID(ctx, db.GetRoleForProjectMemberByUserIDParams{UserID: *invitedMember.UserID, ProjectID: input.ProjectID}) - if err != nil { - return &InviteProjectMembersPayload{Ok: false}, err - } - members = append(members, Member{ - ID: *invitedMember.UserID, - FullName: user.FullName, - Username: user.Username, - ProfileIcon: profileIcon, - Role: &db.Role{Code: role.Code, Name: role.Name}, - }) - } else { - // Invite by email - - // if invited user does not exist, create entry - invitedUser, err := r.Repository.GetInvitedUserByEmail(ctx, *invitedMember.Email) - now := time.Now().UTC() - if err != nil { - if err == sql.ErrNoRows { - invitedUser, err = r.Repository.CreateInvitedUser(ctx, *invitedMember.Email) - if err != nil { - return &InviteProjectMembersPayload{Ok: false}, err - } - confirmToken, err := r.Repository.CreateConfirmToken(ctx, *invitedMember.Email) - if err != nil { - return &InviteProjectMembersPayload{Ok: false}, err - } - invite := utils.EmailInvite{To: *invitedMember.Email, FullName: *invitedMember.Email, ConfirmToken: confirmToken.ConfirmTokenID.String()} - err = utils.SendEmailInvite(r.EmailConfig, invite) - if err != nil { - logger.New(ctx).WithError(err).Error("issue sending email") - return &InviteProjectMembersPayload{Ok: false}, err - } - } else { - return &InviteProjectMembersPayload{Ok: false}, err - } - } - - _, err = r.Repository.CreateInvitedProjectMember(ctx, db.CreateInvitedProjectMemberParams{ - ProjectID: input.ProjectID, - UserAccountInvitedID: invitedUser.UserAccountInvitedID, - }) - if err != nil { - return &InviteProjectMembersPayload{Ok: false}, err - } - logger.New(ctx).Info("adding invited member") - invitedMembers = append(invitedMembers, InvitedMember{Email: *invitedMember.Email, InvitedOn: now}) - - } - } - return &InviteProjectMembersPayload{Ok: false, ProjectID: input.ProjectID, Members: members, InvitedMembers: invitedMembers}, nil -} - -func (r *mutationResolver) DeleteProjectMember(ctx context.Context, input DeleteProjectMember) (*DeleteProjectMemberPayload, error) { - user, err := r.Repository.GetUserAccountByID(ctx, input.UserID) - if err != nil { - return &DeleteProjectMemberPayload{Ok: false}, err - } - var url *string - if user.ProfileAvatarUrl.Valid { - url = &user.ProfileAvatarUrl.String - } - profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor} - role, err := r.Repository.GetRoleForProjectMemberByUserID(ctx, db.GetRoleForProjectMemberByUserIDParams{UserID: input.UserID, ProjectID: input.ProjectID}) - if err != nil { - return &DeleteProjectMemberPayload{Ok: false}, err - } - err = r.Repository.DeleteProjectMember(ctx, db.DeleteProjectMemberParams{UserID: input.UserID, ProjectID: input.ProjectID}) - if err != nil { - return &DeleteProjectMemberPayload{Ok: false}, err - } - return &DeleteProjectMemberPayload{Ok: true, Member: &Member{ - ID: input.UserID, - FullName: user.FullName, - ProfileIcon: profileIcon, - Role: &db.Role{Code: role.Code, Name: role.Name}, - }, ProjectID: input.ProjectID}, nil -} - -func (r *mutationResolver) UpdateProjectMemberRole(ctx context.Context, input UpdateProjectMemberRole) (*UpdateProjectMemberRolePayload, error) { - user, err := r.Repository.GetUserAccountByID(ctx, input.UserID) - if err != nil { - logger.New(ctx).WithError(err).Error("get user account") - return &UpdateProjectMemberRolePayload{Ok: false}, err - } - _, err = r.Repository.UpdateProjectMemberRole(ctx, db.UpdateProjectMemberRoleParams{ProjectID: input.ProjectID, - UserID: input.UserID, RoleCode: input.RoleCode.String()}) - if err != nil { - logger.New(ctx).WithError(err).Error("update project member role") - return &UpdateProjectMemberRolePayload{Ok: false}, err - } - role, err := r.Repository.GetRoleForProjectMemberByUserID(ctx, db.GetRoleForProjectMemberByUserIDParams{UserID: user.UserID, ProjectID: input.ProjectID}) - if err != nil { - logger.New(ctx).WithError(err).Error("get role for project member") - return &UpdateProjectMemberRolePayload{Ok: false}, err - } - var url *string - if user.ProfileAvatarUrl.Valid { - url = &user.ProfileAvatarUrl.String - } - profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor} - if user.ProfileAvatarUrl.Valid { - url = &user.ProfileAvatarUrl.String - } - member := Member{ID: user.UserID, FullName: user.FullName, ProfileIcon: profileIcon, - Role: &db.Role{Code: role.Code, Name: role.Name}, - } - return &UpdateProjectMemberRolePayload{Ok: true, Member: &member}, err -} - -func (r *mutationResolver) DeleteInvitedProjectMember(ctx context.Context, input DeleteInvitedProjectMember) (*DeleteInvitedProjectMemberPayload, error) { - member, err := r.Repository.GetProjectMemberInvitedIDByEmail(ctx, input.Email) - if err != nil { - return &DeleteInvitedProjectMemberPayload{}, err - } - err = r.Repository.DeleteInvitedProjectMemberByID(ctx, member.ProjectMemberInvitedID) - if err != nil { - return &DeleteInvitedProjectMemberPayload{}, err - } - return &DeleteInvitedProjectMemberPayload{ - InvitedMember: &InvitedMember{Email: member.Email, InvitedOn: member.InvitedOn}, - }, nil -} - -func (r *mutationResolver) CreateTask(ctx context.Context, input NewTask) (*db.Task, error) { - createdAt := time.Now().UTC() - logger.New(ctx).WithFields(log.Fields{"positon": input.Position, "taskGroupID": input.TaskGroupID}).Info("creating task") - task, err := r.Repository.CreateTask(ctx, db.CreateTaskParams{input.TaskGroupID, createdAt, input.Name, input.Position}) - if err != nil { - logger.New(ctx).WithError(err).Error("issue while creating task") - return &db.Task{}, err - } - taskGroup, err := r.Repository.GetTaskGroupByID(ctx, input.TaskGroupID) - if err != nil { - logger.New(ctx).WithError(err).Error("issue while creating task") - return &db.Task{}, err - } - data := map[string]string{ - "TaskGroup": taskGroup.Name, - } - userID, _ := GetUserID(ctx) - d, err := json.Marshal(data) - _, err = r.Repository.CreateTaskActivity(ctx, db.CreateTaskActivityParams{ - TaskID: task.TaskID, - Data: d, - CreatedAt: createdAt, - CausedBy: userID, - ActivityTypeID: 1, - }) - - if len(input.Assigned) != 0 { - assignedDate := time.Now().UTC() - for _, assigned := range input.Assigned { - assignedTask, err := r.Repository.CreateTaskAssigned(ctx, db.CreateTaskAssignedParams{TaskID: task.TaskID, UserID: assigned, AssignedDate: assignedDate}) - logger.New(ctx).WithFields(log.Fields{ - "assignedUserID": assignedTask.UserID, - "taskID": assignedTask.TaskID, - "assignedTaskID": assignedTask.TaskAssignedID, - }).Info("assigned task") - if err != nil { - return &db.Task{}, err - } - } - } - - if err != nil { - logger.New(ctx).WithError(err).Error("issue while creating task") - return &db.Task{}, err - } - return &task, nil -} - -func (r *mutationResolver) DeleteTask(ctx context.Context, input DeleteTaskInput) (*DeleteTaskPayload, error) { - logger.New(ctx).WithFields(log.Fields{ - "taskID": input.TaskID, - }).Info("deleting task") - err := r.Repository.DeleteTaskByID(ctx, input.TaskID) - if err != nil { - return &DeleteTaskPayload{}, err - } - return &DeleteTaskPayload{input.TaskID}, nil -} - -func (r *mutationResolver) UpdateTaskDescription(ctx context.Context, input UpdateTaskDescriptionInput) (*db.Task, error) { - task, err := r.Repository.UpdateTaskDescription(ctx, db.UpdateTaskDescriptionParams{input.TaskID, sql.NullString{String: input.Description, Valid: true}}) - return &task, err -} - -func (r *mutationResolver) UpdateTaskLocation(ctx context.Context, input NewTaskLocation) (*UpdateTaskLocationPayload, error) { - userID, _ := GetUserID(ctx) - previousTask, err := r.Repository.GetTaskByID(ctx, input.TaskID) - if err != nil { - return &UpdateTaskLocationPayload{}, err - } - task, _ := r.Repository.UpdateTaskLocation(ctx, db.UpdateTaskLocationParams{TaskID: input.TaskID, TaskGroupID: input.TaskGroupID, Position: input.Position}) - if previousTask.TaskGroupID != input.TaskGroupID { - skipAndDelete := false - lastMove, err := r.Repository.GetLastMoveForTaskID(ctx, input.TaskID) - if err == nil { - if lastMove.Active && lastMove.PrevTaskGroupID == input.TaskGroupID.String() { - skipAndDelete = true - } - } - if skipAndDelete { - _ = r.Repository.SetInactiveLastMoveForTaskID(ctx, input.TaskID) - } else { - prevTaskGroup, _ := r.Repository.GetTaskGroupByID(ctx, previousTask.TaskGroupID) - curTaskGroup, _ := r.Repository.GetTaskGroupByID(ctx, input.TaskGroupID) - - data := map[string]string{ - "PrevTaskGroup": prevTaskGroup.Name, - "PrevTaskGroupID": prevTaskGroup.TaskGroupID.String(), - "CurTaskGroup": curTaskGroup.Name, - "CurTaskGroupID": curTaskGroup.TaskGroupID.String(), - } - - createdAt := time.Now().UTC() - d, _ := json.Marshal(data) - _, err = r.Repository.CreateTaskActivity(ctx, db.CreateTaskActivityParams{ - TaskID: task.TaskID, - Data: d, - CausedBy: userID, - CreatedAt: createdAt, - ActivityTypeID: 2, - }) - } - } - return &UpdateTaskLocationPayload{Task: &task, PreviousTaskGroupID: previousTask.TaskGroupID}, err -} - -func (r *mutationResolver) UpdateTaskName(ctx context.Context, input UpdateTaskName) (*db.Task, error) { - task, err := r.Repository.UpdateTaskName(ctx, db.UpdateTaskNameParams{input.TaskID, input.Name}) - return &task, err -} - -func (r *mutationResolver) SetTaskComplete(ctx context.Context, input SetTaskComplete) (*db.Task, error) { - completedAt := time.Now().UTC() - data := map[string]string{} - activityType := TASK_MARK_INCOMPLETE - if input.Complete { - activityType = TASK_MARK_COMPLETE - } - createdAt := time.Now().UTC() - userID, _ := GetUserID(ctx) - d, err := json.Marshal(data) - _, err = r.Repository.CreateTaskActivity(ctx, db.CreateTaskActivityParams{ - TaskID: input.TaskID, - Data: d, - CausedBy: userID, - CreatedAt: createdAt, - ActivityTypeID: activityType, - }) - task, err := r.Repository.SetTaskComplete(ctx, db.SetTaskCompleteParams{TaskID: input.TaskID, Complete: input.Complete, CompletedAt: sql.NullTime{Time: completedAt, Valid: true}}) - if err != nil { - return &db.Task{}, err - } - return &task, nil -} - -func (r *mutationResolver) UpdateTaskDueDate(ctx context.Context, input UpdateTaskDueDate) (*db.Task, error) { - userID, _ := GetUserID(ctx) - prevTask, err := r.Repository.GetTaskByID(ctx, input.TaskID) - if err != nil { - return &db.Task{}, err - } - data := map[string]string{} - var activityType = TASK_DUE_DATE_ADDED - if input.DueDate == nil && prevTask.DueDate.Valid { - activityType = TASK_DUE_DATE_REMOVED - data["PrevDueDate"] = prevTask.DueDate.Time.String() - } else if prevTask.DueDate.Valid { - activityType = TASK_DUE_DATE_CHANGED - data["PrevDueDate"] = prevTask.DueDate.Time.String() - data["CurDueDate"] = input.DueDate.String() - } else if input.DueDate != nil { - data["DueDate"] = input.DueDate.String() - } - var dueDate sql.NullTime - log.WithField("dueDate", input.DueDate).Info("before ptr!") - if input.DueDate == nil { - dueDate = sql.NullTime{Valid: false, Time: time.Now()} - } else { - dueDate = sql.NullTime{Valid: true, Time: *input.DueDate} - } - var task db.Task - if !(input.DueDate == nil && !prevTask.DueDate.Valid) { - task, err = r.Repository.UpdateTaskDueDate(ctx, db.UpdateTaskDueDateParams{ - TaskID: input.TaskID, - DueDate: dueDate, - HasTime: input.HasTime, - }) - createdAt := time.Now().UTC() - d, _ := json.Marshal(data) - _, err = r.Repository.CreateTaskActivity(ctx, db.CreateTaskActivityParams{ - TaskID: task.TaskID, - Data: d, - CausedBy: userID, - CreatedAt: createdAt, - ActivityTypeID: activityType, - }) - } else { - task, err = r.Repository.GetTaskByID(ctx, input.TaskID) - } - - return &task, err -} - -func (r *mutationResolver) AssignTask(ctx context.Context, input *AssignTaskInput) (*db.Task, error) { - assignedDate := time.Now().UTC() - assignedTask, err := r.Repository.CreateTaskAssigned(ctx, db.CreateTaskAssignedParams{input.TaskID, input.UserID, assignedDate}) - logger.New(ctx).WithFields(log.Fields{ - "assignedUserID": assignedTask.UserID, - "taskID": assignedTask.TaskID, - "assignedTaskID": assignedTask.TaskAssignedID, - }).Info("assigned task") - if err != nil { - return &db.Task{}, err - } - // r.NotificationQueue.TaskMemberWasAdded(assignedTask.TaskID, userID, assignedTask.UserID) - task, err := r.Repository.GetTaskByID(ctx, input.TaskID) - return &task, err -} - -func (r *mutationResolver) UnassignTask(ctx context.Context, input *UnassignTaskInput) (*db.Task, error) { - task, err := r.Repository.GetTaskByID(ctx, input.TaskID) - if err != nil { - return &db.Task{}, err - } - _, err = r.Repository.DeleteTaskAssignedByID(ctx, db.DeleteTaskAssignedByIDParams{input.TaskID, input.UserID}) - if err != nil { - return &db.Task{}, err - } - return &task, nil -} - -func (r *mutationResolver) CreateTaskChecklist(ctx context.Context, input CreateTaskChecklist) (*db.TaskChecklist, error) { - createdAt := time.Now().UTC() - taskChecklist, err := r.Repository.CreateTaskChecklist(ctx, db.CreateTaskChecklistParams{ - TaskID: input.TaskID, - CreatedAt: createdAt, - Name: input.Name, - Position: input.Position, - }) - if err != nil { - return &db.TaskChecklist{}, err - } - - return &taskChecklist, nil -} - -func (r *mutationResolver) DeleteTaskChecklist(ctx context.Context, input DeleteTaskChecklist) (*DeleteTaskChecklistPayload, error) { - taskChecklist, err := r.Repository.GetTaskChecklistByID(ctx, input.TaskChecklistID) - if err != nil { - return &DeleteTaskChecklistPayload{Ok: false}, err - } - err = r.Repository.DeleteTaskChecklistByID(ctx, input.TaskChecklistID) - if err != nil { - return &DeleteTaskChecklistPayload{Ok: false}, err - } - return &DeleteTaskChecklistPayload{Ok: true, TaskChecklist: &taskChecklist}, nil -} - -func (r *mutationResolver) UpdateTaskChecklistName(ctx context.Context, input UpdateTaskChecklistName) (*db.TaskChecklist, error) { - checklist, err := r.Repository.UpdateTaskChecklistName(ctx, db.UpdateTaskChecklistNameParams{TaskChecklistID: input.TaskChecklistID, Name: input.Name}) - if err != nil { - return &db.TaskChecklist{}, err - } - return &checklist, nil -} - -func (r *mutationResolver) CreateTaskChecklistItem(ctx context.Context, input CreateTaskChecklistItem) (*db.TaskChecklistItem, error) { - createdAt := time.Now().UTC() - taskChecklistItem, err := r.Repository.CreateTaskChecklistItem(ctx, db.CreateTaskChecklistItemParams{ - TaskChecklistID: input.TaskChecklistID, - CreatedAt: createdAt, - Name: input.Name, - Position: input.Position, - }) - if err != nil { - return &db.TaskChecklistItem{}, err - } - - return &taskChecklistItem, nil -} - -func (r *mutationResolver) UpdateTaskChecklistLocation(ctx context.Context, input UpdateTaskChecklistLocation) (*UpdateTaskChecklistLocationPayload, error) { - checklist, err := r.Repository.UpdateTaskChecklistPosition(ctx, db.UpdateTaskChecklistPositionParams{Position: input.Position, TaskChecklistID: input.TaskChecklistID}) - - if err != nil { - return &UpdateTaskChecklistLocationPayload{}, err - } - - return &UpdateTaskChecklistLocationPayload{Checklist: &checklist}, nil -} - -func (r *mutationResolver) UpdateTaskChecklistItemName(ctx context.Context, input UpdateTaskChecklistItemName) (*db.TaskChecklistItem, error) { - task, err := r.Repository.UpdateTaskChecklistItemName(ctx, db.UpdateTaskChecklistItemNameParams{TaskChecklistItemID: input.TaskChecklistItemID, - Name: input.Name, - }) - if err != nil { - return &db.TaskChecklistItem{}, err - } - return &task, nil -} - -func (r *mutationResolver) SetTaskChecklistItemComplete(ctx context.Context, input SetTaskChecklistItemComplete) (*db.TaskChecklistItem, error) { - item, err := r.Repository.SetTaskChecklistItemComplete(ctx, db.SetTaskChecklistItemCompleteParams{TaskChecklistItemID: input.TaskChecklistItemID, Complete: input.Complete}) - if err != nil { - return &db.TaskChecklistItem{}, err - } - return &item, nil -} - -func (r *mutationResolver) DeleteTaskChecklistItem(ctx context.Context, input DeleteTaskChecklistItem) (*DeleteTaskChecklistItemPayload, error) { - item, err := r.Repository.GetTaskChecklistItemByID(ctx, input.TaskChecklistItemID) - if err != nil { - return &DeleteTaskChecklistItemPayload{ - Ok: false, - TaskChecklistItem: &db.TaskChecklistItem{}, - }, err - } - err = r.Repository.DeleteTaskChecklistItem(ctx, input.TaskChecklistItemID) - if err != nil { - return &DeleteTaskChecklistItemPayload{ - Ok: false, - TaskChecklistItem: &db.TaskChecklistItem{}, - }, err - } - return &DeleteTaskChecklistItemPayload{ - Ok: true, - TaskChecklistItem: &item, - }, err -} - -func (r *mutationResolver) UpdateTaskChecklistItemLocation(ctx context.Context, input UpdateTaskChecklistItemLocation) (*UpdateTaskChecklistItemLocationPayload, error) { - currentChecklistItem, err := r.Repository.GetTaskChecklistItemByID(ctx, input.TaskChecklistItemID) - - checklistItem, err := r.Repository.UpdateTaskChecklistItemLocation(ctx, db.UpdateTaskChecklistItemLocationParams{TaskChecklistID: input.TaskChecklistID, TaskChecklistItemID: input.TaskChecklistItemID, Position: input.Position}) - if err != nil { - return &UpdateTaskChecklistItemLocationPayload{}, err - } - return &UpdateTaskChecklistItemLocationPayload{PrevChecklistID: currentChecklistItem.TaskChecklistID, TaskChecklistID: input.TaskChecklistID, ChecklistItem: &checklistItem}, err -} - -func (r *mutationResolver) CreateTaskComment(ctx context.Context, input *CreateTaskComment) (*CreateTaskCommentPayload, error) { - userID, _ := GetUserID(ctx) - createdAt := time.Now().UTC() - comment, err := r.Repository.CreateTaskComment(ctx, db.CreateTaskCommentParams{ - TaskID: input.TaskID, - CreatedAt: createdAt, - CreatedBy: userID, - Message: input.Message, - }) - return &CreateTaskCommentPayload{Comment: &comment, TaskID: input.TaskID}, err -} - -func (r *mutationResolver) DeleteTaskComment(ctx context.Context, input *DeleteTaskComment) (*DeleteTaskCommentPayload, error) { - task, err := r.Repository.DeleteTaskCommentByID(ctx, input.CommentID) - return &DeleteTaskCommentPayload{TaskID: task.TaskID, CommentID: input.CommentID}, err -} - -func (r *mutationResolver) UpdateTaskComment(ctx context.Context, input *UpdateTaskComment) (*UpdateTaskCommentPayload, error) { - updatedAt := time.Now().UTC() - comment, err := r.Repository.UpdateTaskComment(ctx, db.UpdateTaskCommentParams{ - TaskCommentID: input.CommentID, - UpdatedAt: sql.NullTime{Valid: true, Time: updatedAt}, - Message: input.Message, - }) - return &UpdateTaskCommentPayload{Comment: &comment}, err -} - -func (r *mutationResolver) CreateTaskGroup(ctx context.Context, input NewTaskGroup) (*db.TaskGroup, error) { - createdAt := time.Now().UTC() - project, err := r.Repository.CreateTaskGroup(ctx, - db.CreateTaskGroupParams{input.ProjectID, createdAt, input.Name, input.Position}) - return &project, err -} - -func (r *mutationResolver) UpdateTaskGroupLocation(ctx context.Context, input NewTaskGroupLocation) (*db.TaskGroup, error) { - taskGroup, err := r.Repository.UpdateTaskGroupLocation(ctx, db.UpdateTaskGroupLocationParams{ - input.TaskGroupID, - input.Position, - }) - return &taskGroup, err -} - -func (r *mutationResolver) UpdateTaskGroupName(ctx context.Context, input UpdateTaskGroupName) (*db.TaskGroup, error) { - taskGroup, err := r.Repository.SetTaskGroupName(ctx, db.SetTaskGroupNameParams{TaskGroupID: input.TaskGroupID, Name: input.Name}) - if err != nil { - return &db.TaskGroup{}, err - } - return &taskGroup, nil -} - -func (r *mutationResolver) DeleteTaskGroup(ctx context.Context, input DeleteTaskGroupInput) (*DeleteTaskGroupPayload, error) { - deletedTasks, err := r.Repository.DeleteTasksByTaskGroupID(ctx, input.TaskGroupID) - if err != nil { - return &DeleteTaskGroupPayload{}, err - } - taskGroup, err := r.Repository.GetTaskGroupByID(ctx, input.TaskGroupID) - if err != nil { - return &DeleteTaskGroupPayload{}, err - } - deletedTaskGroups, err := r.Repository.DeleteTaskGroupByID(ctx, input.TaskGroupID) - if err != nil { - return &DeleteTaskGroupPayload{}, err - } - return &DeleteTaskGroupPayload{true, int(deletedTasks + deletedTaskGroups), &taskGroup}, nil -} - -func (r *mutationResolver) DuplicateTaskGroup(ctx context.Context, input DuplicateTaskGroup) (*DuplicateTaskGroupPayload, error) { - createdAt := time.Now().UTC() - taskGroup, err := r.Repository.CreateTaskGroup(ctx, db.CreateTaskGroupParams{ProjectID: input.ProjectID, Position: input.Position, Name: input.Name, CreatedAt: createdAt}) - if err != nil { - return &DuplicateTaskGroupPayload{}, err - } - - originalTasks, err := r.Repository.GetTasksForTaskGroupID(ctx, input.TaskGroupID) - if err != nil && err != sql.ErrNoRows { - return &DuplicateTaskGroupPayload{}, err - } - for _, originalTask := range originalTasks { - task, err := r.Repository.CreateTaskAll(ctx, db.CreateTaskAllParams{ - TaskGroupID: taskGroup.TaskGroupID, CreatedAt: createdAt, Name: originalTask.Name, Position: originalTask.Position, - Complete: originalTask.Complete, DueDate: originalTask.DueDate, Description: originalTask.Description}) - if err != nil { - return &DuplicateTaskGroupPayload{}, err - } - members, err := r.Repository.GetAssignedMembersForTask(ctx, originalTask.TaskID) - if err != nil { - return &DuplicateTaskGroupPayload{}, err - } - for _, member := range members { - _, err := r.Repository.CreateTaskAssigned(ctx, db.CreateTaskAssignedParams{ - TaskID: task.TaskID, UserID: member.UserID, AssignedDate: member.AssignedDate}) - if err != nil { - return &DuplicateTaskGroupPayload{}, err - } - } - labels, err := r.Repository.GetTaskLabelsForTaskID(ctx, originalTask.TaskID) - if err != nil { - return &DuplicateTaskGroupPayload{}, err - } - for _, label := range labels { - _, err := r.Repository.CreateTaskLabelForTask(ctx, db.CreateTaskLabelForTaskParams{ - TaskID: task.TaskID, ProjectLabelID: label.ProjectLabelID, AssignedDate: label.AssignedDate}) - if err != nil { - return &DuplicateTaskGroupPayload{}, err - } - } - checklists, err := r.Repository.GetTaskChecklistsForTask(ctx, originalTask.TaskID) - if err != nil { - return &DuplicateTaskGroupPayload{}, err - } - for _, checklist := range checklists { - newChecklist, err := r.Repository.CreateTaskChecklist(ctx, db.CreateTaskChecklistParams{ - TaskID: task.TaskID, Name: checklist.Name, CreatedAt: createdAt, Position: checklist.Position}) - if err != nil { - return &DuplicateTaskGroupPayload{}, err - } - checklistItems, err := r.Repository.GetTaskChecklistItemsForTaskChecklist(ctx, checklist.TaskChecklistID) - if err != nil { - return &DuplicateTaskGroupPayload{}, err - } - for _, checklistItem := range checklistItems { - item, err := r.Repository.CreateTaskChecklistItem(ctx, db.CreateTaskChecklistItemParams{ - TaskChecklistID: newChecklist.TaskChecklistID, - CreatedAt: createdAt, - Name: checklistItem.Name, - Position: checklist.Position, - }) - if checklistItem.Complete { - r.Repository.SetTaskChecklistItemComplete(ctx, db.SetTaskChecklistItemCompleteParams{TaskChecklistItemID: item.TaskChecklistItemID, Complete: true}) - } - if err != nil { - return &DuplicateTaskGroupPayload{}, err - } - } - - } - } - if err != nil { - return &DuplicateTaskGroupPayload{}, err - } - return &DuplicateTaskGroupPayload{TaskGroup: &taskGroup}, err -} - -func (r *mutationResolver) SortTaskGroup(ctx context.Context, input SortTaskGroup) (*SortTaskGroupPayload, error) { - tasks := []db.Task{} - for _, task := range input.Tasks { - t, err := r.Repository.UpdateTaskPosition(ctx, db.UpdateTaskPositionParams{TaskID: task.TaskID, Position: task.Position}) - if err != nil { - return &SortTaskGroupPayload{}, err - } - tasks = append(tasks, t) - } - return &SortTaskGroupPayload{Tasks: tasks, TaskGroupID: input.TaskGroupID}, nil -} - -func (r *mutationResolver) DeleteTaskGroupTasks(ctx context.Context, input DeleteTaskGroupTasks) (*DeleteTaskGroupTasksPayload, error) { - tasks, err := r.Repository.GetTasksForTaskGroupID(ctx, input.TaskGroupID) - if err != nil && err != sql.ErrNoRows { - return &DeleteTaskGroupTasksPayload{}, err - } - removedTasks := []uuid.UUID{} - for _, task := range tasks { - err = r.Repository.DeleteTaskByID(ctx, task.TaskID) - if err != nil { - return &DeleteTaskGroupTasksPayload{}, err - } - removedTasks = append(removedTasks, task.TaskID) - } - return &DeleteTaskGroupTasksPayload{TaskGroupID: input.TaskGroupID, Tasks: removedTasks}, nil -} - -func (r *mutationResolver) AddTaskLabel(ctx context.Context, input *AddTaskLabelInput) (*db.Task, error) { - assignedDate := time.Now().UTC() - _, err := r.Repository.CreateTaskLabelForTask(ctx, db.CreateTaskLabelForTaskParams{input.TaskID, input.ProjectLabelID, assignedDate}) - if err != nil { - return &db.Task{}, err - } - task, err := r.Repository.GetTaskByID(ctx, input.TaskID) - return &task, nil -} - -func (r *mutationResolver) RemoveTaskLabel(ctx context.Context, input *RemoveTaskLabelInput) (*db.Task, error) { - taskLabel, err := r.Repository.GetTaskLabelByID(ctx, input.TaskLabelID) - if err != nil { - return &db.Task{}, err - } - task, err := r.Repository.GetTaskByID(ctx, taskLabel.TaskID) - if err != nil { - return &db.Task{}, err - } - err = r.Repository.DeleteTaskLabelByID(ctx, input.TaskLabelID) - return &task, err -} - -func (r *mutationResolver) ToggleTaskLabel(ctx context.Context, input ToggleTaskLabelInput) (*ToggleTaskLabelPayload, error) { - task, err := r.Repository.GetTaskByID(ctx, input.TaskID) - if err != nil { - return &ToggleTaskLabelPayload{}, err - } - - _, err = r.Repository.GetTaskLabelForTaskByProjectLabelID(ctx, db.GetTaskLabelForTaskByProjectLabelIDParams{TaskID: input.TaskID, ProjectLabelID: input.ProjectLabelID}) - createdAt := time.Now().UTC() - - if err == sql.ErrNoRows { - logger.New(ctx).WithFields(log.Fields{"err": err}).Warning("no rows") - _, err := r.Repository.CreateTaskLabelForTask(ctx, db.CreateTaskLabelForTaskParams{ - TaskID: input.TaskID, - ProjectLabelID: input.ProjectLabelID, - AssignedDate: createdAt, - }) - if err != nil { - return &ToggleTaskLabelPayload{}, err - } - payload := ToggleTaskLabelPayload{Active: true, Task: &task} - return &payload, nil - } - - if err != nil { - return &ToggleTaskLabelPayload{}, err - } - - err = r.Repository.DeleteTaskLabelForTaskByProjectLabelID(ctx, db.DeleteTaskLabelForTaskByProjectLabelIDParams{ - TaskID: input.TaskID, - ProjectLabelID: input.ProjectLabelID, - }) - - if err != nil { - return &ToggleTaskLabelPayload{}, err - } - - payload := ToggleTaskLabelPayload{Active: false, Task: &task} - return &payload, nil -} - -func (r *mutationResolver) DeleteTeam(ctx context.Context, input DeleteTeam) (*DeleteTeamPayload, error) { - team, err := r.Repository.GetTeamByID(ctx, input.TeamID) - if err != nil { - logger.New(ctx).Error(err) - return &DeleteTeamPayload{Ok: false}, err - } - projects, err := r.Repository.GetAllProjectsForTeam(ctx, input.TeamID) - if err != nil { - logger.New(ctx).Error(err) - return &DeleteTeamPayload{Ok: false}, err - } - err = r.Repository.DeleteTeamByID(ctx, input.TeamID) - if err != nil { - logger.New(ctx).Error(err) - return &DeleteTeamPayload{Ok: false}, err - } - - return &DeleteTeamPayload{Ok: true, Team: &team, Projects: projects}, nil -} - -func (r *mutationResolver) CreateTeam(ctx context.Context, input NewTeam) (*db.Team, error) { - userID, ok := GetUserID(ctx) - if !ok { - return &db.Team{}, nil - } - role, err := r.Repository.GetRoleForUserID(ctx, userID) - if err != nil { - log.WithError(err).Error("while creating team") - return &db.Team{}, nil - } - if ConvertToRoleCode(role.Code) != RoleCodeAdmin { - return &db.Team{}, &gqlerror.Error{ - Message: "Must be an organization admin", - Extensions: map[string]interface{}{ - "code": "0-400", - }, - } - } - createdAt := time.Now().UTC() - team, err := r.Repository.CreateTeam(ctx, db.CreateTeamParams{OrganizationID: input.OrganizationID, CreatedAt: createdAt, Name: input.Name}) - return &team, err -} - -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, db.CreateTeamMemberParams{TeamID: input.TeamID, UserID: input.UserID, Addeddate: addedDate, RoleCode: RoleCodeMember.String()}) - 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: &Member{ - ID: user.UserID, - Username: user.Username, - FullName: user.FullName, - ProfileIcon: profileIcon, - Role: &db.Role{Code: "member", Name: "Member"}, - }}, nil -} - -func (r *mutationResolver) UpdateTeamMemberRole(ctx context.Context, input UpdateTeamMemberRole) (*UpdateTeamMemberRolePayload, error) { - user, err := r.Repository.GetUserAccountByID(ctx, input.UserID) - if err != nil { - logger.New(ctx).WithError(err).Error("get user account") - return &UpdateTeamMemberRolePayload{Ok: false}, err - } - _, err = r.Repository.UpdateTeamMemberRole(ctx, db.UpdateTeamMemberRoleParams{TeamID: input.TeamID, - UserID: input.UserID, RoleCode: input.RoleCode.String()}) - if err != nil { - logger.New(ctx).WithError(err).Error("update project member role") - return &UpdateTeamMemberRolePayload{Ok: false}, err - } - role, err := r.Repository.GetRoleForTeamMember(ctx, db.GetRoleForTeamMemberParams{UserID: user.UserID, TeamID: input.TeamID}) - if err != nil { - logger.New(ctx).WithError(err).Error("get role for project member") - return &UpdateTeamMemberRolePayload{Ok: false}, err - } - var url *string - if user.ProfileAvatarUrl.Valid { - url = &user.ProfileAvatarUrl.String - } - profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor} - if user.ProfileAvatarUrl.Valid { - url = &user.ProfileAvatarUrl.String - } - member := Member{ID: user.UserID, FullName: user.FullName, ProfileIcon: profileIcon, - Role: &db.Role{Code: role.Code, Name: role.Name}, - } - return &UpdateTeamMemberRolePayload{Ok: true, Member: &member, TeamID: input.TeamID}, err -} - -func (r *mutationResolver) DeleteTeamMember(ctx context.Context, input DeleteTeamMember) (*DeleteTeamMemberPayload, error) { - err := r.Repository.DeleteTeamMember(ctx, db.DeleteTeamMemberParams{TeamID: input.TeamID, UserID: input.UserID}) - return &DeleteTeamMemberPayload{TeamID: input.TeamID, UserID: input.UserID}, err -} - -func (r *mutationResolver) CreateUserAccount(ctx context.Context, input NewUserAccount) (*db.UserAccount, error) { - userID, ok := GetUserID(ctx) - if !ok { - return &db.UserAccount{}, nil - } - role, err := r.Repository.GetRoleForUserID(ctx, userID) - if err != nil { - log.WithError(err).Error("while creating user account") - return &db.UserAccount{}, nil - } - if ConvertToRoleCode(role.Code) != RoleCodeAdmin { - return &db.UserAccount{}, &gqlerror.Error{ - Message: "Must be an organization admin", - Extensions: map[string]interface{}{ - "code": "0-400", - }, - } - } - createdAt := time.Now().UTC() - hashedPwd, err := bcrypt.GenerateFromPassword([]byte(input.Password), 14) - if err != nil { - return &db.UserAccount{}, err - } - - userExists, err := r.Repository.DoesUserExist(ctx, db.DoesUserExistParams{Username: input.Username, Email: input.Email}) - if err != nil { - return &db.UserAccount{}, err - } - if userExists { - return &db.UserAccount{}, &gqlerror.Error{ - Message: "User with that username or email already exists", - Extensions: map[string]interface{}{ - "code": "0-300", - }, - } - } - userAccount, err := r.Repository.CreateUserAccount(ctx, db.CreateUserAccountParams{ - FullName: input.FullName, - RoleCode: input.RoleCode, - Initials: input.Initials, - Email: input.Email, - Username: input.Username, - CreatedAt: createdAt, - Active: true, - PasswordHash: string(hashedPwd), - }) - return &userAccount, err -} - -func (r *mutationResolver) DeleteUserAccount(ctx context.Context, input DeleteUserAccount) (*DeleteUserAccountPayload, error) { - userID, ok := GetUserID(ctx) - if !ok { - return &DeleteUserAccountPayload{Ok: false}, nil - } - role, err := r.Repository.GetRoleForUserID(ctx, userID) - if err != nil { - log.WithError(err).Error("while deleting user account") - return &DeleteUserAccountPayload{}, nil - } - if ConvertToRoleCode(role.Code) != RoleCodeAdmin { - return &DeleteUserAccountPayload{}, &gqlerror.Error{ - Message: "Must be an organization admin", - Extensions: map[string]interface{}{ - "code": "0-400", - }, - } - } - user, err := r.Repository.GetUserAccountByID(ctx, input.UserID) - if err != nil { - return &DeleteUserAccountPayload{Ok: false}, err - } - - err = r.Repository.DeleteUserAccountByID(ctx, input.UserID) - if err != nil { - return &DeleteUserAccountPayload{Ok: false}, err - } - return &DeleteUserAccountPayload{UserAccount: &user, Ok: true}, nil -} - -func (r *mutationResolver) DeleteInvitedUserAccount(ctx context.Context, input DeleteInvitedUserAccount) (*DeleteInvitedUserAccountPayload, error) { - user, err := r.Repository.DeleteInvitedUserAccount(ctx, input.InvitedUserID) - if err != nil { - return &DeleteInvitedUserAccountPayload{}, err - } - err = r.Repository.DeleteConfirmTokenForEmail(ctx, user.Email) - if err != nil { - logger.New(ctx).WithError(err).Error("issue deleting confirm token") - return &DeleteInvitedUserAccountPayload{}, err - } - return &DeleteInvitedUserAccountPayload{ - InvitedUser: &InvitedUserAccount{ - Email: user.Email, - ID: user.UserAccountInvitedID, - InvitedOn: user.InvitedOn, - }, - }, err -} - -func (r *mutationResolver) LogoutUser(ctx context.Context, input LogoutUser) (bool, error) { - err := r.Repository.DeleteAuthTokenByUserID(ctx, input.UserID) - return true, err -} - -func (r *mutationResolver) ClearProfileAvatar(ctx context.Context) (*db.UserAccount, error) { - userID, ok := GetUserID(ctx) - if !ok { - return &db.UserAccount{}, fmt.Errorf("internal server error") - } - user, err := r.Repository.UpdateUserAccountProfileAvatarURL(ctx, db.UpdateUserAccountProfileAvatarURLParams{UserID: userID, ProfileAvatarUrl: sql.NullString{Valid: false, String: ""}}) - if err != nil { - return &db.UserAccount{}, err - } - return &user, nil -} - -func (r *mutationResolver) UpdateUserPassword(ctx context.Context, input UpdateUserPassword) (*UpdateUserPasswordPayload, error) { - hashedPwd, err := bcrypt.GenerateFromPassword([]byte(input.Password), 14) - if err != nil { - return &UpdateUserPasswordPayload{}, err - } - user, err := r.Repository.SetUserPassword(ctx, db.SetUserPasswordParams{UserID: input.UserID, PasswordHash: string(hashedPwd)}) - if err != nil { - return &UpdateUserPasswordPayload{}, err - } - return &UpdateUserPasswordPayload{Ok: true, User: &user}, err -} - -func (r *mutationResolver) UpdateUserRole(ctx context.Context, input UpdateUserRole) (*UpdateUserRolePayload, error) { - userID, ok := GetUserID(ctx) - if !ok { - return &UpdateUserRolePayload{}, nil - } - role, err := r.Repository.GetRoleForUserID(ctx, userID) - if err != nil { - log.WithError(err).Error("while updating user role") - return &UpdateUserRolePayload{}, nil - } - if ConvertToRoleCode(role.Code) != RoleCodeAdmin { - return &UpdateUserRolePayload{}, &gqlerror.Error{ - Message: "Must be an organization admin", - Extensions: map[string]interface{}{ - "code": "0-400", - }, - } - } - user, err := r.Repository.UpdateUserRole(ctx, db.UpdateUserRoleParams{RoleCode: input.RoleCode.String(), UserID: input.UserID}) - if err != nil { - return &UpdateUserRolePayload{}, err - } - return &UpdateUserRolePayload{User: &user}, nil -} - -func (r *mutationResolver) UpdateUserInfo(ctx context.Context, input UpdateUserInfo) (*UpdateUserInfoPayload, error) { - userID, ok := GetUserID(ctx) - if !ok { - return &UpdateUserInfoPayload{}, errors.New("invalid user ID") - } - user, err := r.Repository.UpdateUserAccountInfo(ctx, db.UpdateUserAccountInfoParams{ - Bio: input.Bio, FullName: input.Name, Initials: input.Initials, Email: input.Email, UserID: userID, - }) - return &UpdateUserInfoPayload{User: &user}, err -} - -func (r *notificationResolver) ID(ctx context.Context, obj *db.Notification) (uuid.UUID, error) { - return obj.NotificationID, nil -} - -func (r *notificationResolver) Entity(ctx context.Context, obj *db.Notification) (*NotificationEntity, error) { - logger.New(ctx).WithFields(log.Fields{"notificationID": obj.NotificationID}).Info("fetching entity for notification") - entity, err := r.Repository.GetEntityForNotificationID(ctx, obj.NotificationID) - logger.New(ctx).WithFields(log.Fields{"entityID": entity.EntityID}).Info("fetched entity") - if err != nil { - return &NotificationEntity{}, err - } - entityType := GetEntityType(entity.EntityType) - switch entityType { - case EntityTypeTask: - task, err := r.Repository.GetTaskByID(ctx, entity.EntityID) - if err != nil { - return &NotificationEntity{}, err - } - return &NotificationEntity{Type: entityType, ID: entity.EntityID, Name: task.Name}, err - - default: - panic(fmt.Errorf("not implemented")) - } -} - -func (r *notificationResolver) ActionType(ctx context.Context, obj *db.Notification) (ActionType, error) { - entity, err := r.Repository.GetEntityForNotificationID(ctx, obj.NotificationID) - if err != nil { - return ActionTypeTaskMemberAdded, err - } - actionType := GetActionType(entity.ActionType) - return actionType, nil -} - -func (r *notificationResolver) Actor(ctx context.Context, obj *db.Notification) (*NotificationActor, error) { - entity, err := r.Repository.GetEntityForNotificationID(ctx, obj.NotificationID) - if err != nil { - return &NotificationActor{}, err - } - logger.New(ctx).WithFields(log.Fields{"entityID": entity.ActorID}).Info("fetching actor") - user, err := r.Repository.GetUserAccountByID(ctx, entity.ActorID) - if err != nil { - return &NotificationActor{}, err - } - return &NotificationActor{ID: entity.ActorID, Name: user.FullName, Type: ActorTypeUser}, nil -} - -func (r *notificationResolver) CreatedAt(ctx context.Context, obj *db.Notification) (*time.Time, error) { - entity, err := r.Repository.GetEntityForNotificationID(ctx, obj.NotificationID) - if err != nil { - return &time.Time{}, err - } - return &entity.CreatedOn, nil -} - func (r *organizationResolver) ID(ctx context.Context, obj *db.Organization) (uuid.UUID, error) { return obj.OrganizationID, nil } -func (r *projectResolver) ID(ctx context.Context, obj *db.Project) (uuid.UUID, error) { - return obj.ProjectID, nil -} - -func (r *projectResolver) Team(ctx context.Context, obj *db.Project) (*db.Team, error) { - team, err := r.Repository.GetTeamByID(ctx, obj.TeamID) - if err != nil { - if err == sql.ErrNoRows { - return nil, nil - } - logger.New(ctx).WithFields(log.Fields{"teamID": obj.TeamID, "projectID": obj.ProjectID}).WithError(err).Error("issue while getting team for project") - return &team, err - } - return &team, nil -} - -func (r *projectResolver) TaskGroups(ctx context.Context, obj *db.Project) ([]db.TaskGroup, error) { - return r.Repository.GetTaskGroupsForProject(ctx, obj.ProjectID) -} - -func (r *projectResolver) Members(ctx context.Context, obj *db.Project) ([]Member, error) { - members := []Member{} - projectMembers, err := r.Repository.GetProjectMembersForProjectID(ctx, obj.ProjectID) - if err != nil { - logger.New(ctx).WithError(err).Error("get project members for project id") - return members, err - } - - for _, projectMember := range projectMembers { - user, err := r.Repository.GetUserAccountByID(ctx, projectMember.UserID) - if err != nil { - logger.New(ctx).WithError(err).Error("get user account by ID") - return members, err - } - var url *string - if user.ProfileAvatarUrl.Valid { - url = &user.ProfileAvatarUrl.String - } - role, err := r.Repository.GetRoleForProjectMemberByUserID(ctx, db.GetRoleForProjectMemberByUserIDParams{UserID: user.UserID, ProjectID: obj.ProjectID}) - if err != nil { - logger.New(ctx).WithError(err).Error("get role for projet member by user ID") - return members, err - } - profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor} - members = append(members, Member{ID: user.UserID, FullName: user.FullName, ProfileIcon: profileIcon, - Username: user.Username, Role: &db.Role{Code: role.Code, Name: role.Name}, - }) - } - return members, nil -} - -func (r *projectResolver) InvitedMembers(ctx context.Context, obj *db.Project) ([]InvitedMember, error) { - members, err := r.Repository.GetInvitedMembersForProjectID(ctx, obj.ProjectID) - if err != nil && err == sql.ErrNoRows { - return []InvitedMember{}, nil - } - invited := []InvitedMember{} - for _, member := range members { - invited = append(invited, InvitedMember{Email: member.Email, InvitedOn: member.InvitedOn}) - } - return invited, err -} - -func (r *projectResolver) PublicOn(ctx context.Context, obj *db.Project) (*time.Time, error) { - if obj.PublicOn.Valid { - return &obj.PublicOn.Time, nil - } - return nil, nil -} - -func (r *projectResolver) Permission(ctx context.Context, obj *db.Project) (*ProjectPermission, error) { - panic(fmt.Errorf("not implemented")) -} - -func (r *projectResolver) Labels(ctx context.Context, obj *db.Project) ([]db.ProjectLabel, error) { - labels, err := r.Repository.GetProjectLabelsForProject(ctx, obj.ProjectID) - return labels, err -} - -func (r *projectLabelResolver) ID(ctx context.Context, obj *db.ProjectLabel) (uuid.UUID, error) { - return obj.ProjectLabelID, nil -} - -func (r *projectLabelResolver) LabelColor(ctx context.Context, obj *db.ProjectLabel) (*db.LabelColor, error) { - labelColor, err := r.Repository.GetLabelColorByID(ctx, obj.LabelColorID) - if err != nil { - return &db.LabelColor{}, err - } - return &labelColor, nil -} - -func (r *projectLabelResolver) Name(ctx context.Context, obj *db.ProjectLabel) (*string, error) { - var name *string - if obj.Name.Valid { - name = &obj.Name.String - } - return name, nil -} - func (r *queryResolver) Organizations(ctx context.Context) ([]db.Organization, error) { return r.Repository.GetAllOrganizations(ctx) } @@ -1563,498 +306,15 @@ func (r *queryResolver) Me(ctx context.Context) (*MePayload, error) { return &MePayload{User: &user, TeamRoles: teamRoles, ProjectRoles: projectRoles}, err } -func (r *queryResolver) Notifications(ctx context.Context) ([]db.Notification, error) { - userID, ok := GetUserID(ctx) - logger.New(ctx).Info("fetching notifications") - if !ok { - return []db.Notification{}, errors.New("user id is missing") - } - notifications, err := r.Repository.GetAllNotificationsForUserID(ctx, userID) - if err == sql.ErrNoRows { - return []db.Notification{}, nil - } else if err != nil { - return []db.Notification{}, err - } - return notifications, nil -} - -func (r *queryResolver) SearchMembers(ctx context.Context, input MemberSearchFilter) ([]MemberSearchResult, error) { - availableMembers, err := r.Repository.GetMemberData(ctx, *input.ProjectID) - if err != nil { - logger.New(ctx).WithField("projectID", input.ProjectID).WithError(err).Error("error while getting member data") - return []MemberSearchResult{}, err - } - - invitedMembers, err := r.Repository.GetInvitedMembersForProjectID(ctx, *input.ProjectID) - if err != nil { - logger.New(ctx).WithField("projectID", input.ProjectID).WithError(err).Error("error while getting member data") - return []MemberSearchResult{}, err - } - - sortList := []string{} - masterList := map[string]MasterEntry{} - for _, member := range availableMembers { - sortList = append(sortList, member.Username) - sortList = append(sortList, member.Email) - masterList[member.Username] = MasterEntry{ID: member.UserID, MemberType: MemberTypeJoined} - masterList[member.Email] = MasterEntry{ID: member.UserID, MemberType: MemberTypeJoined} - } - for _, member := range invitedMembers { - sortList = append(sortList, member.Email) - logger.New(ctx).WithField("Email", member.Email).Info("adding member") - masterList[member.Email] = MasterEntry{ID: member.UserAccountInvitedID, MemberType: MemberTypeInvited} - } - - logger.New(ctx).WithField("searchFilter", input.SearchFilter).Info(sortList) - rankedList := fuzzy.RankFind(input.SearchFilter, sortList) - logger.New(ctx).Info(rankedList) - results := []MemberSearchResult{} - memberList := map[uuid.UUID]bool{} - for _, rank := range rankedList { - entry, _ := masterList[rank.Target] - _, ok := memberList[entry.ID] - logger.New(ctx).WithField("ok", ok).WithField("target", rank.Target).Info("checking rank") - if !ok { - if entry.MemberType == MemberTypeJoined { - logger.New(ctx).WithFields(log.Fields{"source": rank.Source, "target": rank.Target}).Info("searching") - entry := masterList[rank.Target] - user, err := r.Repository.GetUserAccountByID(ctx, entry.ID) - if err != nil { - if err == sql.ErrNoRows { - continue - } - return []MemberSearchResult{}, err - } - results = append(results, MemberSearchResult{ID: user.UserID.String(), User: &user, Status: ShareStatusJoined, Similarity: rank.Distance}) - } else { - logger.New(ctx).WithField("id", rank.Target).Info("adding target") - results = append(results, MemberSearchResult{ID: rank.Target, Status: ShareStatusInvited, Similarity: rank.Distance}) - - } - memberList[entry.ID] = true - } - } - return results, nil -} - -func (r *taskResolver) ID(ctx context.Context, obj *db.Task) (uuid.UUID, error) { - return obj.TaskID, nil -} - -func (r *taskResolver) TaskGroup(ctx context.Context, obj *db.Task) (*db.TaskGroup, error) { - taskGroup, err := r.Repository.GetTaskGroupByID(ctx, obj.TaskGroupID) - return &taskGroup, err -} - -func (r *taskResolver) Description(ctx context.Context, obj *db.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) DueDate(ctx context.Context, obj *db.Task) (*time.Time, error) { - if obj.DueDate.Valid { - return &obj.DueDate.Time, nil - } - return nil, nil -} - -func (r *taskResolver) CompletedAt(ctx context.Context, obj *db.Task) (*time.Time, error) { - if obj.CompletedAt.Valid { - return &obj.CompletedAt.Time, nil - } - return nil, nil -} - -func (r *taskResolver) Assigned(ctx context.Context, obj *db.Task) ([]Member, error) { - taskMemberLinks, err := r.Repository.GetAssignedMembersForTask(ctx, obj.TaskID) - taskMembers := []Member{} - if err != nil { - return taskMembers, err - } - for _, taskMemberLink := range taskMemberLinks { - user, err := r.Repository.GetUserAccountByID(ctx, taskMemberLink.UserID) - if err != nil { - return taskMembers, err - } - var url *string - if user.ProfileAvatarUrl.Valid { - url = &user.ProfileAvatarUrl.String - } - profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor} - projectID, err := r.Repository.GetProjectIDForTask(ctx, obj.TaskID) - if err != nil { - return taskMembers, err - } - role, err := r.Repository.GetRoleForProjectMemberByUserID(ctx, db.GetRoleForProjectMemberByUserIDParams{UserID: user.UserID, ProjectID: projectID}) - - if err != nil { - if err == sql.ErrNoRows { - role = db.Role{Code: "owner", Name: "Owner"} - } else { - logger.New(ctx).WithError(err).Error("get role for project member") - return taskMembers, err - } - } - taskMembers = append(taskMembers, Member{ID: taskMemberLink.UserID, FullName: user.FullName, ProfileIcon: profileIcon, - Role: &role, - }) - } - return taskMembers, nil -} - -func (r *taskResolver) Labels(ctx context.Context, obj *db.Task) ([]db.TaskLabel, error) { - return r.Repository.GetTaskLabelsForTaskID(ctx, obj.TaskID) -} - -func (r *taskResolver) Checklists(ctx context.Context, obj *db.Task) ([]db.TaskChecklist, error) { - return r.Repository.GetTaskChecklistsForTask(ctx, obj.TaskID) -} - -func (r *taskResolver) Badges(ctx context.Context, obj *db.Task) (*TaskBadges, error) { - checklists, err := r.Repository.GetTaskChecklistsForTask(ctx, obj.TaskID) - if err != nil { - return &TaskBadges{}, err - } - comments, err := r.Repository.GetCommentCountForTask(ctx, obj.TaskID) - if err != nil { - return &TaskBadges{}, err - } - complete := 0 - total := 0 - for _, checklist := range checklists { - items, err := r.Repository.GetTaskChecklistItemsForTaskChecklist(ctx, checklist.TaskChecklistID) - if err != nil { - return &TaskBadges{}, err - } - for _, item := range items { - total++ - if item.Complete { - complete++ - } - } - } - var taskChecklist *ChecklistBadge - if total != 0 { - taskChecklist = &ChecklistBadge{Total: total, Complete: complete} - } - var taskComments *CommentsBadge - if comments != 0 { - taskComments = &CommentsBadge{Total: int(comments), Unread: false} - } - return &TaskBadges{Checklist: taskChecklist, Comments: taskComments}, nil -} - -func (r *taskResolver) Activity(ctx context.Context, obj *db.Task) ([]db.TaskActivity, error) { - activity, err := r.Repository.GetActivityForTaskID(ctx, obj.TaskID) - if err == sql.ErrNoRows { - return []db.TaskActivity{}, nil - } - return activity, err -} - -func (r *taskResolver) Comments(ctx context.Context, obj *db.Task) ([]db.TaskComment, error) { - comments, err := r.Repository.GetCommentsForTaskID(ctx, obj.TaskID) - if err == sql.ErrNoRows { - return []db.TaskComment{}, nil - } - return comments, err -} - -func (r *taskActivityResolver) ID(ctx context.Context, obj *db.TaskActivity) (uuid.UUID, error) { - return obj.TaskActivityID, nil -} - -func (r *taskActivityResolver) Type(ctx context.Context, obj *db.TaskActivity) (ActivityType, error) { - switch obj.ActivityTypeID { - case 1: - return ActivityTypeTaskAdded, nil - case 2: - return ActivityTypeTaskMoved, nil - case 3: - return ActivityTypeTaskMarkedComplete, nil - case 4: - return ActivityTypeTaskMarkedIncomplete, nil - case 5: - return ActivityTypeTaskDueDateChanged, nil - case 6: - return ActivityTypeTaskDueDateAdded, nil - case 7: - return ActivityTypeTaskDueDateRemoved, nil - case 8: - return ActivityTypeTaskChecklistChanged, nil - case 9: - return ActivityTypeTaskChecklistAdded, nil - case 10: - return ActivityTypeTaskChecklistRemoved, nil - default: - return ActivityTypeTaskAdded, errors.New("unknown type") - } -} - -func (r *taskActivityResolver) Data(ctx context.Context, obj *db.TaskActivity) ([]TaskActivityData, error) { - var data map[string]string - _ = json.Unmarshal(obj.Data, &data) - activity := []TaskActivityData{} - for name, value := range data { - activity = append(activity, TaskActivityData{ - Name: name, - Value: value, - }) - } - return activity, nil -} - -func (r *taskActivityResolver) CausedBy(ctx context.Context, obj *db.TaskActivity) (*CausedBy, error) { - user, err := r.Repository.GetUserAccountByID(ctx, obj.CausedBy) - var url *string - if user.ProfileAvatarUrl.Valid { - url = &user.ProfileAvatarUrl.String - } - profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor} - return &CausedBy{ - ID: obj.CausedBy, - FullName: user.FullName, - ProfileIcon: profileIcon, - }, err -} - -func (r *taskChecklistResolver) ID(ctx context.Context, obj *db.TaskChecklist) (uuid.UUID, error) { - return obj.TaskChecklistID, nil -} - -func (r *taskChecklistResolver) Items(ctx context.Context, obj *db.TaskChecklist) ([]db.TaskChecklistItem, error) { - return r.Repository.GetTaskChecklistItemsForTaskChecklist(ctx, obj.TaskChecklistID) -} - -func (r *taskChecklistItemResolver) ID(ctx context.Context, obj *db.TaskChecklistItem) (uuid.UUID, error) { - return obj.TaskChecklistItemID, nil -} - -func (r *taskChecklistItemResolver) DueDate(ctx context.Context, obj *db.TaskChecklistItem) (*time.Time, error) { - panic(fmt.Errorf("not implemented")) -} - -func (r *taskCommentResolver) ID(ctx context.Context, obj *db.TaskComment) (uuid.UUID, error) { - return obj.TaskCommentID, nil -} - -func (r *taskCommentResolver) UpdatedAt(ctx context.Context, obj *db.TaskComment) (*time.Time, error) { - if obj.UpdatedAt.Valid { - return &obj.UpdatedAt.Time, nil - } - return nil, nil -} - -func (r *taskCommentResolver) CreatedBy(ctx context.Context, obj *db.TaskComment) (*CreatedBy, error) { - user, err := r.Repository.GetUserAccountByID(ctx, obj.CreatedBy) - var url *string - if user.ProfileAvatarUrl.Valid { - url = &user.ProfileAvatarUrl.String - } - profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor} - return &CreatedBy{ - ID: obj.CreatedBy, - FullName: user.FullName, - ProfileIcon: profileIcon, - }, err -} - -func (r *taskGroupResolver) ID(ctx context.Context, obj *db.TaskGroup) (uuid.UUID, error) { - return obj.TaskGroupID, nil -} - -func (r *taskGroupResolver) ProjectID(ctx context.Context, obj *db.TaskGroup) (string, error) { - return obj.ProjectID.String(), nil -} - -func (r *taskGroupResolver) Tasks(ctx context.Context, obj *db.TaskGroup) ([]db.Task, error) { - tasks, err := r.Repository.GetTasksForTaskGroupID(ctx, obj.TaskGroupID) - return tasks, err -} - -func (r *taskLabelResolver) ID(ctx context.Context, obj *db.TaskLabel) (uuid.UUID, error) { - return obj.TaskLabelID, nil -} - -func (r *taskLabelResolver) ProjectLabel(ctx context.Context, obj *db.TaskLabel) (*db.ProjectLabel, error) { - projectLabel, err := r.Repository.GetProjectLabelByID(ctx, obj.ProjectLabelID) - return &projectLabel, err -} - -func (r *teamResolver) ID(ctx context.Context, obj *db.Team) (uuid.UUID, error) { - return obj.TeamID, nil -} - -func (r *teamResolver) Permission(ctx context.Context, obj *db.Team) (*TeamPermission, error) { - panic(fmt.Errorf("not implemented")) -} - -func (r *teamResolver) Members(ctx context.Context, obj *db.Team) ([]Member, error) { - members := []Member{} - - teamMembers, err := r.Repository.GetTeamMembersForTeamID(ctx, obj.TeamID) - if err != nil { - logger.New(ctx).Error("get project members for project id") - return members, err - } - - for _, teamMember := range teamMembers { - user, err := r.Repository.GetUserAccountByID(ctx, teamMember.UserID) - if err != nil { - logger.New(ctx).WithError(err).Error("get user account by ID") - return members, err - } - var url *string - if user.ProfileAvatarUrl.Valid { - url = &user.ProfileAvatarUrl.String - } - profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor} - role, err := r.Repository.GetRoleForTeamMember(ctx, db.GetRoleForTeamMemberParams{UserID: user.UserID, TeamID: obj.TeamID}) - if err != nil { - logger.New(ctx).WithError(err).Error("get role for projet member by user ID") - return members, err - } - - ownedList, err := GetOwnedList(ctx, r.Repository, user) - if err != nil { - return members, err - } - memberList, err := GetMemberList(ctx, r.Repository, user) - if err != nil { - return members, err - } - - members = append(members, Member{ID: user.UserID, FullName: user.FullName, ProfileIcon: profileIcon, - Username: user.Username, Owned: ownedList, Member: memberList, Role: &db.Role{Code: role.Code, Name: role.Name}, - }) - } - return members, nil -} - -func (r *userAccountResolver) ID(ctx context.Context, obj *db.UserAccount) (uuid.UUID, error) { - return obj.UserID, nil -} - -func (r *userAccountResolver) Role(ctx context.Context, obj *db.UserAccount) (*db.Role, error) { - role, err := r.Repository.GetRoleForUserID(ctx, obj.UserID) - if err != nil { - logger.New(ctx).WithError(err).Error("get role for user id") - return &db.Role{}, err - } - return &db.Role{Code: role.Code, Name: role.Name}, nil -} - -func (r *userAccountResolver) ProfileIcon(ctx context.Context, obj *db.UserAccount) (*ProfileIcon, error) { - var url *string - if obj.ProfileAvatarUrl.Valid { - url = &obj.ProfileAvatarUrl.String - } - profileIcon := &ProfileIcon{url, &obj.Initials, &obj.ProfileBgColor} - return profileIcon, nil -} - -func (r *userAccountResolver) Owned(ctx context.Context, obj *db.UserAccount) (*OwnedList, error) { - return &OwnedList{}, nil // TODO(jordanknott) -} - -func (r *userAccountResolver) Member(ctx context.Context, obj *db.UserAccount) (*MemberList, error) { - projectMemberIDs, err := r.Repository.GetMemberProjectIDsForUserID(ctx, obj.UserID) - if err != sql.ErrNoRows && err != nil { - return &MemberList{}, err - } - var projects []db.Project - for _, projectID := range projectMemberIDs { - project, err := r.Repository.GetProjectByID(ctx, projectID) - if err != nil { - return &MemberList{}, err - } - projects = append(projects, project) - } - teamMemberIDs, err := r.Repository.GetMemberTeamIDsForUserID(ctx, obj.UserID) - if err != sql.ErrNoRows && err != nil { - return &MemberList{}, err - } - var teams []db.Team - for _, teamID := range teamMemberIDs { - team, err := r.Repository.GetTeamByID(ctx, teamID) - if err != nil { - return &MemberList{}, err - } - teams = append(teams, team) - } - - return &MemberList{Teams: teams, Projects: projects}, err -} - -// LabelColor returns LabelColorResolver implementation. -func (r *Resolver) LabelColor() LabelColorResolver { return &labelColorResolver{r} } - // Mutation returns MutationResolver implementation. func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} } -// Notification returns NotificationResolver implementation. -func (r *Resolver) Notification() NotificationResolver { return ¬ificationResolver{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} } - -// ProjectLabel returns ProjectLabelResolver implementation. -func (r *Resolver) ProjectLabel() ProjectLabelResolver { return &projectLabelResolver{r} } - // Query returns QueryResolver implementation. func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } -// Task returns TaskResolver implementation. -func (r *Resolver) Task() TaskResolver { return &taskResolver{r} } - -// TaskActivity returns TaskActivityResolver implementation. -func (r *Resolver) TaskActivity() TaskActivityResolver { return &taskActivityResolver{r} } - -// TaskChecklist returns TaskChecklistResolver implementation. -func (r *Resolver) TaskChecklist() TaskChecklistResolver { return &taskChecklistResolver{r} } - -// TaskChecklistItem returns TaskChecklistItemResolver implementation. -func (r *Resolver) TaskChecklistItem() TaskChecklistItemResolver { - return &taskChecklistItemResolver{r} -} - -// TaskComment returns TaskCommentResolver implementation. -func (r *Resolver) TaskComment() TaskCommentResolver { return &taskCommentResolver{r} } - -// TaskGroup returns TaskGroupResolver implementation. -func (r *Resolver) TaskGroup() TaskGroupResolver { return &taskGroupResolver{r} } - -// TaskLabel returns TaskLabelResolver implementation. -func (r *Resolver) TaskLabel() TaskLabelResolver { return &taskLabelResolver{r} } - -// Team returns TeamResolver implementation. -func (r *Resolver) Team() TeamResolver { return &teamResolver{r} } - -// UserAccount returns UserAccountResolver implementation. -func (r *Resolver) UserAccount() UserAccountResolver { return &userAccountResolver{r} } - -type labelColorResolver struct{ *Resolver } type mutationResolver struct{ *Resolver } -type notificationResolver struct{ *Resolver } type organizationResolver struct{ *Resolver } -type projectResolver struct{ *Resolver } -type projectLabelResolver struct{ *Resolver } type queryResolver struct{ *Resolver } -type taskResolver struct{ *Resolver } -type taskActivityResolver struct{ *Resolver } -type taskChecklistResolver struct{ *Resolver } -type taskChecklistItemResolver struct{ *Resolver } -type taskCommentResolver struct{ *Resolver } -type taskGroupResolver struct{ *Resolver } -type taskLabelResolver struct{ *Resolver } -type teamResolver struct{ *Resolver } -type userAccountResolver struct{ *Resolver } diff --git a/internal/graph/schema/_models.gql b/internal/graph/schema/_models.gql deleted file mode 100644 index 5307c75..0000000 --- a/internal/graph/schema/_models.gql +++ /dev/null @@ -1,236 +0,0 @@ -scalar Time -scalar UUID -scalar Upload - -enum RoleCode { - owner - admin - member - observer -} - -type ProjectLabel { - id: ID! - createdDate: Time! - labelColor: LabelColor! - name: String -} - -type LabelColor { - id: ID! - name: String! - position: Float! - colorHex: String! -} - -type TaskLabel { - id: ID! - projectLabel: ProjectLabel! - assignedDate: Time! -} - -type ProfileIcon { - url: String - initials: String - bgColor: String -} - -type OwnersList { - projects: [UUID!]! - teams: [UUID!]! -} - -type Member { - id: ID! - role: Role! - fullName: String! - username: String! - profileIcon: ProfileIcon! - owned: OwnedList! - member: MemberList! -} - -type Role { - code: String! - name: String! -} - -type OwnedList { - teams: [Team!]! - projects: [Project!]! -} - -type MemberList { - teams: [Team!]! - projects: [Project!]! -} - -type UserAccount { - id: ID! - email: String! - createdAt: Time! - fullName: String! - initials: String! - bio: String! - role: Role! - username: String! - profileIcon: ProfileIcon! - owned: OwnedList! - member: MemberList! -} - -type InvitedUserAccount { - id: ID! - email: String! - invitedOn: Time! - member: MemberList! -} - -type Team { - id: ID! - createdAt: Time! - name: String! - permission: TeamPermission! - members: [Member!]! -} - - -type InvitedMember { - email: String! - invitedOn: Time! -} - -type TeamPermission { - team: RoleCode! - org: RoleCode! -} - -type ProjectPermission { - team: RoleCode! - project: RoleCode! - org: RoleCode! -} - -type Project { - id: ID! - createdAt: Time! - name: String! - team: Team - taskGroups: [TaskGroup!]! - members: [Member!]! - invitedMembers: [InvitedMember!]! - publicOn: Time - permission: ProjectPermission! - labels: [ProjectLabel!]! -} - -type TaskGroup { - id: ID! - projectID: String! - createdAt: Time! - name: String! - position: Float! - tasks: [Task!]! -} - -type ChecklistBadge { - complete: Int! - total: Int! -} - -type CommentsBadge { - total: Int! - unread: Boolean! -} - -type TaskBadges { - checklist: ChecklistBadge - comments: CommentsBadge -} - -type CausedBy { - id: ID! - fullName: String! - profileIcon: ProfileIcon -} - -type TaskActivityData { - name: String! - value: String! -} - -enum ActivityType { - TASK_ADDED - TASK_MOVED - TASK_MARKED_COMPLETE - TASK_MARKED_INCOMPLETE - TASK_DUE_DATE_CHANGED - TASK_DUE_DATE_ADDED - TASK_DUE_DATE_REMOVED - TASK_CHECKLIST_CHANGED - TASK_CHECKLIST_ADDED - TASK_CHECKLIST_REMOVED -} - -type TaskActivity { - id: ID! - type: ActivityType! - data: [TaskActivityData!]! - causedBy: CausedBy! - createdAt: Time! -} - -type Task { - id: ID! - taskGroup: TaskGroup! - createdAt: Time! - name: String! - position: Float! - description: String - dueDate: Time - hasTime: Boolean! - complete: Boolean! - completedAt: Time - assigned: [Member!]! - labels: [TaskLabel!]! - checklists: [TaskChecklist!]! - badges: TaskBadges! - activity: [TaskActivity!]! - comments: [TaskComment!]! -} - -type CreatedBy { - id: ID! - fullName: String! - profileIcon: ProfileIcon! -} - -type TaskComment { - id: ID! - createdAt: Time! - updatedAt: Time - message: String! - createdBy: CreatedBy! - pinned: Boolean! -} - -type Organization { - id: ID! - name: String! -} - -type TaskChecklistItem { - id: ID! - name: String! - taskChecklistID: UUID! - complete: Boolean! - position: Float! - dueDate: Time! -} - -type TaskChecklist { - id: ID! - name: String! - position: Float! - items: [TaskChecklistItem!]! -} diff --git a/internal/graph/schema/notification.gql b/internal/graph/schema/notification.gql old mode 100644 new mode 100755 diff --git a/internal/graph/schema/notification/notification.gql b/internal/graph/schema/notification/notification.gql new file mode 100644 index 0000000..3cdc748 --- /dev/null +++ b/internal/graph/schema/notification/notification.gql @@ -0,0 +1,36 @@ +extend type Query { + notifications: [Notification!]! +} + +enum EntityType { + TASK +} + +enum ActorType { + USER +} + +enum ActionType { + TASK_MEMBER_ADDED +} + +type NotificationActor { + id: UUID! + type: ActorType! + name: String! +} + +type NotificationEntity { + id: UUID! + type: EntityType! + name: String! +} + +type Notification { + id: ID! + entity: NotificationEntity! + actionType: ActionType! + actor: NotificationActor! + read: Boolean! + createdAt: Time! +} diff --git a/internal/graph/schema/project.gql b/internal/graph/schema/project.gql old mode 100644 new mode 100755 index 9135fae..c600d18 --- a/internal/graph/schema/project.gql +++ b/internal/graph/schema/project.gql @@ -1,3 +1,150 @@ +type ProjectPermission { + team: RoleCode! + project: RoleCode! + org: RoleCode! +} + +type Project { + id: ID! + createdAt: Time! + name: String! + team: Team + taskGroups: [TaskGroup!]! + members: [Member!]! + invitedMembers: [InvitedMember!]! + publicOn: Time + permission: ProjectPermission! + labels: [ProjectLabel!]! +} + +type ProjectLabel { + id: ID! + createdDate: Time! + labelColor: LabelColor! + name: String +} + +type LabelColor { + id: ID! + name: String! + position: Float! + colorHex: String! +} + +type Member { + id: ID! + role: Role! + fullName: String! + username: String! + profileIcon: ProfileIcon! + owned: OwnedList! + member: MemberList! +} + +type InvitedMember { + email: String! + invitedOn: Time! +} + +extend type Mutation { + createProjectLabel(input: NewProjectLabel!): + ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) + deleteProjectLabel(input: DeleteProjectLabel!): + ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) + updateProjectLabel(input: UpdateProjectLabel!): + ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) + updateProjectLabelName(input: UpdateProjectLabelName!): + ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) + updateProjectLabelColor(input: UpdateProjectLabelColor!): + ProjectLabel! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) +} + +input NewProjectLabel { + projectID: UUID! + labelColorID: UUID! + name: String +} + +input DeleteProjectLabel { + projectLabelID: UUID! +} + +input UpdateProjectLabelName { + projectLabelID: UUID! + name: String! +} + +input UpdateProjectLabel { + projectLabelID: UUID! + labelColorID: UUID! + name: String! +} + +input UpdateProjectLabelColor { + projectLabelID: UUID! + labelColorID: UUID! +} + +extend type Mutation { + inviteProjectMembers(input: InviteProjectMembers!): + InviteProjectMembersPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + deleteProjectMember(input: DeleteProjectMember!): + DeleteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + updateProjectMemberRole(input: UpdateProjectMemberRole!): + UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + + deleteInvitedProjectMember(input: DeleteInvitedProjectMember!): + DeleteInvitedProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) +} + +input DeleteInvitedProjectMember { + projectID: UUID! + email: String! +} + +type DeleteInvitedProjectMemberPayload { + invitedMember: InvitedMember! +} + +input MemberInvite { + userID: UUID + email: String +} + +input InviteProjectMembers { + projectID: UUID! + members: [MemberInvite!]! +} + +type InviteProjectMembersPayload { + ok: Boolean! + projectID: UUID! + members: [Member!]! + invitedMembers: [InvitedMember!]! +} + +input DeleteProjectMember { + projectID: UUID! + userID: UUID! +} + +type DeleteProjectMemberPayload { + ok: Boolean! + member: Member! + projectID: UUID! +} + +input UpdateProjectMemberRole { + projectID: UUID! + userID: UUID! + roleCode: RoleCode! +} + +type UpdateProjectMemberRolePayload { + ok: Boolean! + member: Member! +} + extend type Mutation { createProject(input: NewProject!): Project! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM) deleteProject(input: DeleteProject!): diff --git a/internal/graph/schema/project/_model.gql b/internal/graph/schema/project/_model.gql new file mode 100644 index 0000000..eecc5bc --- /dev/null +++ b/internal/graph/schema/project/_model.gql @@ -0,0 +1,47 @@ +type ProjectPermission { + team: RoleCode! + project: RoleCode! + org: RoleCode! +} + +type Project { + id: ID! + createdAt: Time! + name: String! + team: Team + taskGroups: [TaskGroup!]! + members: [Member!]! + invitedMembers: [InvitedMember!]! + publicOn: Time + permission: ProjectPermission! + labels: [ProjectLabel!]! +} + +type ProjectLabel { + id: ID! + createdDate: Time! + labelColor: LabelColor! + name: String +} + +type LabelColor { + id: ID! + name: String! + position: Float! + colorHex: String! +} + +type Member { + id: ID! + role: Role! + fullName: String! + username: String! + profileIcon: ProfileIcon! + owned: OwnedList! + member: MemberList! +} + +type InvitedMember { + email: String! + invitedOn: Time! +} diff --git a/internal/graph/schema/project_label.gql b/internal/graph/schema/project/label.gql similarity index 100% rename from internal/graph/schema/project_label.gql rename to internal/graph/schema/project/label.gql diff --git a/internal/graph/schema/project_member.gql b/internal/graph/schema/project/member.gql similarity index 88% rename from internal/graph/schema/project_member.gql rename to internal/graph/schema/project/member.gql index 4c2edb8..1a7e4b7 100644 --- a/internal/graph/schema/project_member.gql +++ b/internal/graph/schema/project/member.gql @@ -6,8 +6,8 @@ extend type Mutation { updateProjectMemberRole(input: UpdateProjectMemberRole!): UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) -deleteInvitedProjectMember(input: DeleteInvitedProjectMember!): - DeleteInvitedProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + deleteInvitedProjectMember(input: DeleteInvitedProjectMember!): + DeleteInvitedProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) } input DeleteInvitedProjectMember { diff --git a/internal/graph/schema/project/project.gql b/internal/graph/schema/project/project.gql new file mode 100644 index 0000000..9135fae --- /dev/null +++ b/internal/graph/schema/project/project.gql @@ -0,0 +1,36 @@ +extend type Mutation { + createProject(input: NewProject!): Project! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM) + deleteProject(input: DeleteProject!): + DeleteProjectPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + updateProjectName(input: UpdateProjectName): + Project! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + toggleProjectVisibility(input: ToggleProjectVisibility!): ToggleProjectVisibilityPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) +} + +input ToggleProjectVisibility { + projectID: UUID! + isPublic: Boolean! +} + +type ToggleProjectVisibilityPayload { + project: Project! +} + +input NewProject { + teamID: UUID + name: String! +} + +input UpdateProjectName { + projectID: UUID! + name: String! +} + +input DeleteProject { + projectID: UUID! +} + +type DeleteProjectPayload { + ok: Boolean! + project: Project! +} diff --git a/internal/graph/schema/_root.gql b/internal/graph/schema/schema.gql similarity index 79% rename from internal/graph/schema/_root.gql rename to internal/graph/schema/schema.gql index 01fc379..31505db 100644 --- a/internal/graph/schema/_root.gql +++ b/internal/graph/schema/schema.gql @@ -1,3 +1,45 @@ +scalar Time +scalar UUID +scalar Upload + +enum RoleCode { + owner + admin + member + observer +} + +type Role { + code: String! + name: String! +} + +type ProfileIcon { + url: String + initials: String + bgColor: String +} + +type OwnersList { + projects: [UUID!]! + teams: [UUID!]! +} + +type OwnedList { + teams: [Team!]! + projects: [Project!]! +} + +type MemberList { + teams: [Team!]! + projects: [Project!]! +} + +type Organization { + id: ID! + name: String! +} + enum ShareStatus { INVITED JOINED @@ -25,6 +67,7 @@ enum ObjectType { } directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION + directive @requiresUser on FIELD_DEFINITION type Query { diff --git a/internal/graph/schema/task.gql b/internal/graph/schema/task.gql old mode 100644 new mode 100755 index ded5c21..997ac4d --- a/internal/graph/schema/task.gql +++ b/internal/graph/schema/task.gql @@ -1,3 +1,341 @@ + +type TaskLabel { + id: ID! + projectLabel: ProjectLabel! + assignedDate: Time! +} + +type ChecklistBadge { + complete: Int! + total: Int! +} + +type CommentsBadge { + total: Int! + unread: Boolean! +} + +type TaskBadges { + checklist: ChecklistBadge + comments: CommentsBadge +} + +type Task { + id: ID! + taskGroup: TaskGroup! + createdAt: Time! + name: String! + position: Float! + description: String + dueDate: Time + hasTime: Boolean! + complete: Boolean! + completedAt: Time + assigned: [Member!]! + labels: [TaskLabel!]! + checklists: [TaskChecklist!]! + badges: TaskBadges! + activity: [TaskActivity!]! + comments: [TaskComment!]! +} + +type TaskActivityData { + name: String! + value: String! +} + +enum ActivityType { + TASK_ADDED + TASK_MOVED + TASK_MARKED_COMPLETE + TASK_MARKED_INCOMPLETE + TASK_DUE_DATE_CHANGED + TASK_DUE_DATE_ADDED + TASK_DUE_DATE_REMOVED + TASK_CHECKLIST_CHANGED + TASK_CHECKLIST_ADDED + TASK_CHECKLIST_REMOVED +} + +type TaskActivity { + id: ID! + type: ActivityType! + data: [TaskActivityData!]! + causedBy: CausedBy! + createdAt: Time! +} + +type CausedBy { + id: ID! + fullName: String! + profileIcon: ProfileIcon +} + + +type CreatedBy { + id: ID! + fullName: String! + profileIcon: ProfileIcon! +} + +type TaskComment { + id: ID! + createdAt: Time! + updatedAt: Time + message: String! + createdBy: CreatedBy! + pinned: Boolean! +} + +type TaskChecklistItem { + id: ID! + name: String! + taskChecklistID: UUID! + complete: Boolean! + position: Float! + dueDate: Time! +} + +type TaskChecklist { + id: ID! + name: String! + position: Float! + items: [TaskChecklistItem!]! +} + +extend type Mutation { + createTaskChecklist(input: CreateTaskChecklist!): + TaskChecklist! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + deleteTaskChecklist(input: DeleteTaskChecklist!): + DeleteTaskChecklistPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_CHECKLIST) + updateTaskChecklistName(input: UpdateTaskChecklistName!): + TaskChecklist! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_CHECKLIST) + createTaskChecklistItem(input: CreateTaskChecklistItem!): + TaskChecklistItem! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_CHECKLIST) + updateTaskChecklistLocation(input: UpdateTaskChecklistLocation!): + UpdateTaskChecklistLocationPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_CHECKLIST) + + updateTaskChecklistItemName(input: UpdateTaskChecklistItemName!): + TaskChecklistItem! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_CHECKLIST_ITEM) + setTaskChecklistItemComplete(input: SetTaskChecklistItemComplete!): + TaskChecklistItem! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_CHECKLIST_ITEM) + deleteTaskChecklistItem(input: DeleteTaskChecklistItem!): + DeleteTaskChecklistItemPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_CHECKLIST_ITEM) + updateTaskChecklistItemLocation(input: UpdateTaskChecklistItemLocation!): + UpdateTaskChecklistItemLocationPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_CHECKLIST_ITEM) + +} + +input UpdateTaskChecklistItemLocation { + taskChecklistID: UUID! + taskChecklistItemID: UUID! + position: Float! +} + +type UpdateTaskChecklistItemLocationPayload { + taskChecklistID: UUID! + prevChecklistID: UUID! + checklistItem: TaskChecklistItem! +} + +input UpdateTaskChecklistLocation { + taskChecklistID: UUID! + position: Float! +} + +type UpdateTaskChecklistLocationPayload { + checklist: TaskChecklist! +} + +input CreateTaskChecklist { + taskID: UUID! + name: String! + position: Float! +} + +type DeleteTaskChecklistItemPayload { + ok: Boolean! + taskChecklistItem: TaskChecklistItem! +} + +input CreateTaskChecklistItem { + taskChecklistID: UUID! + name: String! + position: Float! +} + +input SetTaskChecklistItemComplete { + taskChecklistItemID: UUID! + complete: Boolean! +} + +input DeleteTaskChecklistItem { + taskChecklistItemID: UUID! +} + +input UpdateTaskChecklistItemName { + taskChecklistItemID: UUID! + name: String! +} + +input UpdateTaskChecklistName { + taskChecklistID: UUID! + name: String! +} +input DeleteTaskChecklist { + taskChecklistID: UUID! +} +type DeleteTaskChecklistPayload { + ok: Boolean! + taskChecklist: TaskChecklist! +} + +extend type Mutation { + createTaskComment(input: CreateTaskComment): + CreateTaskCommentPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + deleteTaskComment(input: DeleteTaskComment): + DeleteTaskCommentPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + updateTaskComment(input: UpdateTaskComment): + UpdateTaskCommentPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) +} + +input CreateTaskComment { + taskID: UUID! + message: String! +} + +type CreateTaskCommentPayload { + taskID: UUID! + comment: TaskComment! +} + +input UpdateTaskComment { + commentID: UUID! + message: String! +} + +type UpdateTaskCommentPayload { + taskID: UUID! + comment: TaskComment! +} + +input DeleteTaskComment { + commentID: UUID! +} + +type DeleteTaskCommentPayload { + taskID: UUID! + commentID: UUID! +} + +extend type Mutation { + createTaskGroup(input: NewTaskGroup!): + TaskGroup! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) + updateTaskGroupLocation(input: NewTaskGroupLocation!): + TaskGroup! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_GROUP) + updateTaskGroupName(input: UpdateTaskGroupName!): + TaskGroup! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_GROUP) + deleteTaskGroup(input: DeleteTaskGroupInput!): + DeleteTaskGroupPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_GROUP) + duplicateTaskGroup(input: DuplicateTaskGroup!): + DuplicateTaskGroupPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_GROUP) + sortTaskGroup(input: SortTaskGroup!): + SortTaskGroupPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_GROUP) + deleteTaskGroupTasks(input: DeleteTaskGroupTasks!): + DeleteTaskGroupTasksPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_GROUP) +} + +input DeleteTaskGroupTasks { + taskGroupID: UUID! +} + +type DeleteTaskGroupTasksPayload { + taskGroupID: UUID! + tasks: [UUID!]! +} + +input TaskPositionUpdate { + taskID: UUID! + position: Float! +} + +type SortTaskGroupPayload { + taskGroupID: UUID! + tasks: [Task!]! +} + +input SortTaskGroup { + taskGroupID: UUID! + tasks: [TaskPositionUpdate!]! +} + +input DuplicateTaskGroup { + projectID: UUID! + taskGroupID: UUID! + name: String! + position: Float! +} + +type DuplicateTaskGroupPayload { + taskGroup: TaskGroup! +} + +input NewTaskGroupLocation { + taskGroupID: UUID! + position: Float! +} + +input UpdateTaskGroupName { + taskGroupID: UUID! + name: String! +} + +input DeleteTaskGroupInput { + taskGroupID: UUID! +} + +type DeleteTaskGroupPayload { + ok: Boolean! + affectedRows: Int! + taskGroup: TaskGroup! +} + +input NewTaskGroup { + projectID: UUID! + name: String! + position: Float! +} + +extend type Mutation { + addTaskLabel(input: AddTaskLabelInput): + Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + removeTaskLabel(input: RemoveTaskLabelInput): + Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + toggleTaskLabel(input: ToggleTaskLabelInput!): + ToggleTaskLabelPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + +} + +input AddTaskLabelInput { + taskID: UUID! + projectLabelID: UUID! +} + +input RemoveTaskLabelInput { + taskID: UUID! + taskLabelID: UUID! +} + +input ToggleTaskLabelInput { + taskID: UUID! + projectLabelID: UUID! +} + +type ToggleTaskLabelPayload { + active: Boolean! + task: Task! +} + extend type Mutation { createTask(input: NewTask!): Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_GROUP) diff --git a/internal/graph/schema/task/_model.gql b/internal/graph/schema/task/_model.gql new file mode 100644 index 0000000..e8d4f9e --- /dev/null +++ b/internal/graph/schema/task/_model.gql @@ -0,0 +1,104 @@ + +type TaskLabel { + id: ID! + projectLabel: ProjectLabel! + assignedDate: Time! +} + +type ChecklistBadge { + complete: Int! + total: Int! +} + +type CommentsBadge { + total: Int! + unread: Boolean! +} + +type TaskBadges { + checklist: ChecklistBadge + comments: CommentsBadge +} + +type Task { + id: ID! + taskGroup: TaskGroup! + createdAt: Time! + name: String! + position: Float! + description: String + dueDate: Time + hasTime: Boolean! + complete: Boolean! + completedAt: Time + assigned: [Member!]! + labels: [TaskLabel!]! + checklists: [TaskChecklist!]! + badges: TaskBadges! + activity: [TaskActivity!]! + comments: [TaskComment!]! +} + +type TaskActivityData { + name: String! + value: String! +} + +enum ActivityType { + TASK_ADDED + TASK_MOVED + TASK_MARKED_COMPLETE + TASK_MARKED_INCOMPLETE + TASK_DUE_DATE_CHANGED + TASK_DUE_DATE_ADDED + TASK_DUE_DATE_REMOVED + TASK_CHECKLIST_CHANGED + TASK_CHECKLIST_ADDED + TASK_CHECKLIST_REMOVED +} + +type TaskActivity { + id: ID! + type: ActivityType! + data: [TaskActivityData!]! + causedBy: CausedBy! + createdAt: Time! +} + +type CausedBy { + id: ID! + fullName: String! + profileIcon: ProfileIcon +} + + +type CreatedBy { + id: ID! + fullName: String! + profileIcon: ProfileIcon! +} + +type TaskComment { + id: ID! + createdAt: Time! + updatedAt: Time + message: String! + createdBy: CreatedBy! + pinned: Boolean! +} + +type TaskChecklistItem { + id: ID! + name: String! + taskChecklistID: UUID! + complete: Boolean! + position: Float! + dueDate: Time! +} + +type TaskChecklist { + id: ID! + name: String! + position: Float! + items: [TaskChecklistItem!]! +} diff --git a/internal/graph/schema/task_checklist.gql b/internal/graph/schema/task/checklist.gql similarity index 100% rename from internal/graph/schema/task_checklist.gql rename to internal/graph/schema/task/checklist.gql diff --git a/internal/graph/schema/task_comment.gql b/internal/graph/schema/task/comment.gql similarity index 100% rename from internal/graph/schema/task_comment.gql rename to internal/graph/schema/task/comment.gql diff --git a/internal/graph/schema/task_group.gql b/internal/graph/schema/task/group.gql similarity index 100% rename from internal/graph/schema/task_group.gql rename to internal/graph/schema/task/group.gql diff --git a/internal/graph/schema/task_label.gql b/internal/graph/schema/task/label.gql similarity index 100% rename from internal/graph/schema/task_label.gql rename to internal/graph/schema/task/label.gql diff --git a/internal/graph/schema/task/task.gql b/internal/graph/schema/task/task.gql new file mode 100644 index 0000000..ded5c21 --- /dev/null +++ b/internal/graph/schema/task/task.gql @@ -0,0 +1,79 @@ +extend type Mutation { + createTask(input: NewTask!): + Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_GROUP) + deleteTask(input: DeleteTaskInput!): + DeleteTaskPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + + updateTaskDescription(input: UpdateTaskDescriptionInput!): + Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + updateTaskLocation(input: NewTaskLocation!): + UpdateTaskLocationPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + updateTaskName(input: UpdateTaskName!): + Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + setTaskComplete(input: SetTaskComplete!): + Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + updateTaskDueDate(input: UpdateTaskDueDate!): + Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + + assignTask(input: AssignTaskInput): + Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) + unassignTask(input: UnassignTaskInput): + Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK) +} + +input NewTask { + taskGroupID: UUID! + name: String! + position: Float! + assigned: [UUID!] +} + +input AssignTaskInput { + taskID: UUID! + userID: UUID! +} + +input UnassignTaskInput { + taskID: UUID! + userID: UUID! +} + +input UpdateTaskDescriptionInput { + taskID: UUID! + description: String! +} + +type UpdateTaskLocationPayload { + previousTaskGroupID: UUID! + task: Task! +} + +input UpdateTaskDueDate { + taskID: UUID! + hasTime: Boolean! + dueDate: Time +} + +input SetTaskComplete { + taskID: UUID! + complete: Boolean! +} + +input NewTaskLocation { + taskID: UUID! + taskGroupID: UUID! + position: Float! +} + +input DeleteTaskInput { + taskID: UUID! +} + +type DeleteTaskPayload { + taskID: UUID! +} + +input UpdateTaskName { + taskID: UUID! + name: String! +} diff --git a/internal/graph/schema/taskList.gql b/internal/graph/schema/taskList.gql new file mode 100755 index 0000000..2bf5b7c --- /dev/null +++ b/internal/graph/schema/taskList.gql @@ -0,0 +1,8 @@ +type TaskGroup { + id: ID! + projectID: String! + createdAt: Time! + name: String! + position: Float! + tasks: [Task!]! +} diff --git a/internal/graph/schema/taskList/_model.gql b/internal/graph/schema/taskList/_model.gql new file mode 100644 index 0000000..2bf5b7c --- /dev/null +++ b/internal/graph/schema/taskList/_model.gql @@ -0,0 +1,8 @@ +type TaskGroup { + id: ID! + projectID: String! + createdAt: Time! + name: String! + position: Float! + tasks: [Task!]! +} diff --git a/internal/graph/schema/taskList/taskList.gql b/internal/graph/schema/taskList/taskList.gql new file mode 100644 index 0000000..e69de29 diff --git a/internal/graph/schema/team.gql b/internal/graph/schema/team.gql old mode 100644 new mode 100755 index 3647134..96a4e80 --- a/internal/graph/schema/team.gql +++ b/internal/graph/schema/team.gql @@ -1,3 +1,61 @@ +type Team { + id: ID! + createdAt: Time! + name: String! + permission: TeamPermission! + members: [Member!]! +} + +type TeamPermission { + team: RoleCode! + org: RoleCode! +} + + +extend type Mutation { + createTeamMember(input: CreateTeamMember!): + CreateTeamMemberPayload! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM) + updateTeamMemberRole(input: UpdateTeamMemberRole!): + UpdateTeamMemberRolePayload! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM) + deleteTeamMember(input: DeleteTeamMember!): + DeleteTeamMemberPayload! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM) + +} + +input DeleteTeamMember { + teamID: UUID! + userID: UUID! + newOwnerID: UUID +} + +type DeleteTeamMemberPayload { + teamID: UUID! + userID: UUID! + affectedProjects: [Project!]! +} + +input CreateTeamMember { + userID: UUID! + teamID: UUID! +} + +type CreateTeamMemberPayload { + team: Team! + teamMember: Member! +} + +input UpdateTeamMemberRole { + teamID: UUID! + userID: UUID! + roleCode: RoleCode! +} + +type UpdateTeamMemberRolePayload { + ok: Boolean! + teamID: UUID! + member: Member! +} + extend type Mutation { deleteTeam(input: DeleteTeam!): DeleteTeamPayload! @hasRole(roles:[ ADMIN], level: TEAM, type: TEAM) diff --git a/internal/graph/schema/team/_model.gql b/internal/graph/schema/team/_model.gql new file mode 100644 index 0000000..816f9bc --- /dev/null +++ b/internal/graph/schema/team/_model.gql @@ -0,0 +1,12 @@ +type Team { + id: ID! + createdAt: Time! + name: String! + permission: TeamPermission! + members: [Member!]! +} + +type TeamPermission { + team: RoleCode! + org: RoleCode! +} diff --git a/internal/graph/schema/team_member.gql b/internal/graph/schema/team/member.gql similarity index 100% rename from internal/graph/schema/team_member.gql rename to internal/graph/schema/team/member.gql diff --git a/internal/graph/schema/team/team.gql b/internal/graph/schema/team/team.gql new file mode 100644 index 0000000..3647134 --- /dev/null +++ b/internal/graph/schema/team/team.gql @@ -0,0 +1,21 @@ +extend type Mutation { + deleteTeam(input: DeleteTeam!): + DeleteTeamPayload! @hasRole(roles:[ ADMIN], level: TEAM, type: TEAM) + createTeam(input: NewTeam!): + Team! @hasRole(roles: [ADMIN], level: ORG, type: ORG) +} + +input NewTeam { + name: String! + organizationID: UUID! +} + +input DeleteTeam { + teamID: UUID! +} + +type DeleteTeamPayload { + ok: Boolean! + team: Team! + projects: [Project!]! +} diff --git a/internal/graph/schema/user.gql b/internal/graph/schema/user.gql old mode 100644 new mode 100755 index b064d61..4bd4a85 --- a/internal/graph/schema/user.gql +++ b/internal/graph/schema/user.gql @@ -1,3 +1,26 @@ +type UserAccount { + id: ID! + email: String! + createdAt: Time! + fullName: String! + initials: String! + bio: String! + role: Role! + username: String! + profileIcon: ProfileIcon! + owned: OwnedList! + member: MemberList! +} + +type InvitedUserAccount { + id: ID! + email: String! + invitedOn: Time! + member: MemberList! +} + + + extend type Mutation { createUserAccount(input: NewUserAccount!): UserAccount! @hasRole(roles: [ADMIN], level: ORG, type: ORG) diff --git a/internal/graph/schema/user/_model.gql b/internal/graph/schema/user/_model.gql new file mode 100644 index 0000000..16d8366 --- /dev/null +++ b/internal/graph/schema/user/_model.gql @@ -0,0 +1,20 @@ +type UserAccount { + id: ID! + email: String! + createdAt: Time! + fullName: String! + initials: String! + bio: String! + role: Role! + username: String! + profileIcon: ProfileIcon! + owned: OwnedList! + member: MemberList! +} + +type InvitedUserAccount { + id: ID! + email: String! + invitedOn: Time! + member: MemberList! +} diff --git a/internal/graph/schema/user/user.gql b/internal/graph/schema/user/user.gql new file mode 100644 index 0000000..b064d61 --- /dev/null +++ b/internal/graph/schema/user/user.gql @@ -0,0 +1,95 @@ +extend type Mutation { + createUserAccount(input: NewUserAccount!): + UserAccount! @hasRole(roles: [ADMIN], level: ORG, type: ORG) + deleteUserAccount(input: DeleteUserAccount!): + DeleteUserAccountPayload! @hasRole(roles: [ADMIN], level: ORG, type: ORG) + deleteInvitedUserAccount(input: DeleteInvitedUserAccount!): + DeleteInvitedUserAccountPayload! @hasRole(roles: [ADMIN], level: ORG, type: ORG) + + logoutUser(input: LogoutUser!): Boolean! + clearProfileAvatar: UserAccount! + + updateUserPassword(input: UpdateUserPassword!): UpdateUserPasswordPayload! + updateUserRole(input: UpdateUserRole!): + UpdateUserRolePayload! @hasRole(roles: [ADMIN], level: ORG, type: ORG) + updateUserInfo(input: UpdateUserInfo!): + UpdateUserInfoPayload! @hasRole(roles: [ADMIN], level: ORG, type: ORG) +} + +extend type Query { + searchMembers(input: MemberSearchFilter!): [MemberSearchResult!]! +} + +input DeleteInvitedUserAccount { + invitedUserID: UUID! +} + +type DeleteInvitedUserAccountPayload { + invitedUser: InvitedUserAccount! +} + +input MemberSearchFilter { + searchFilter: String! + projectID: UUID +} + + +type MemberSearchResult { + similarity: Int! + id: String! + user: UserAccount + status: ShareStatus! +} + +type UpdateUserInfoPayload { + user: UserAccount! +} + +input UpdateUserInfo { + name: String! + initials: String! + email: String! + bio: String! +} + +input UpdateUserPassword { + userID: UUID! + password: String! +} + +type UpdateUserPasswordPayload { + ok: Boolean! + user: UserAccount! +} + +input UpdateUserRole { + userID: UUID! + roleCode: RoleCode! +} + +type UpdateUserRolePayload { + user: UserAccount! +} + +input NewUserAccount { + username: String! + email: String! + fullName: String! + initials: String! + password: String! + roleCode: String! +} + +input LogoutUser { + userID: UUID! +} + +input DeleteUserAccount { + userID: UUID! + newOwnerID: UUID +} + +type DeleteUserAccountPayload { + ok: Boolean! + userAccount: UserAccount! +} diff --git a/internal/graph/task.resolvers.go b/internal/graph/task.resolvers.go new file mode 100644 index 0000000..930f76f --- /dev/null +++ b/internal/graph/task.resolvers.go @@ -0,0 +1,836 @@ +package graph + +// This file will be automatically regenerated based on the schema, any resolver implementations +// will be copied through when generating and any unknown code will be moved to the end. + +import ( + "context" + "database/sql" + "encoding/json" + "errors" + "fmt" + "time" + + "github.com/google/uuid" + "github.com/jordanknott/taskcafe/internal/db" + "github.com/jordanknott/taskcafe/internal/logger" + log "github.com/sirupsen/logrus" +) + +func (r *mutationResolver) CreateTaskChecklist(ctx context.Context, input CreateTaskChecklist) (*db.TaskChecklist, error) { + createdAt := time.Now().UTC() + taskChecklist, err := r.Repository.CreateTaskChecklist(ctx, db.CreateTaskChecklistParams{ + TaskID: input.TaskID, + CreatedAt: createdAt, + Name: input.Name, + Position: input.Position, + }) + if err != nil { + return &db.TaskChecklist{}, err + } + + return &taskChecklist, nil +} + +func (r *mutationResolver) DeleteTaskChecklist(ctx context.Context, input DeleteTaskChecklist) (*DeleteTaskChecklistPayload, error) { + taskChecklist, err := r.Repository.GetTaskChecklistByID(ctx, input.TaskChecklistID) + if err != nil { + return &DeleteTaskChecklistPayload{Ok: false}, err + } + err = r.Repository.DeleteTaskChecklistByID(ctx, input.TaskChecklistID) + if err != nil { + return &DeleteTaskChecklistPayload{Ok: false}, err + } + return &DeleteTaskChecklistPayload{Ok: true, TaskChecklist: &taskChecklist}, nil +} + +func (r *mutationResolver) UpdateTaskChecklistName(ctx context.Context, input UpdateTaskChecklistName) (*db.TaskChecklist, error) { + checklist, err := r.Repository.UpdateTaskChecklistName(ctx, db.UpdateTaskChecklistNameParams{TaskChecklistID: input.TaskChecklistID, Name: input.Name}) + if err != nil { + return &db.TaskChecklist{}, err + } + return &checklist, nil +} + +func (r *mutationResolver) CreateTaskChecklistItem(ctx context.Context, input CreateTaskChecklistItem) (*db.TaskChecklistItem, error) { + createdAt := time.Now().UTC() + taskChecklistItem, err := r.Repository.CreateTaskChecklistItem(ctx, db.CreateTaskChecklistItemParams{ + TaskChecklistID: input.TaskChecklistID, + CreatedAt: createdAt, + Name: input.Name, + Position: input.Position, + }) + if err != nil { + return &db.TaskChecklistItem{}, err + } + + return &taskChecklistItem, nil +} + +func (r *mutationResolver) UpdateTaskChecklistLocation(ctx context.Context, input UpdateTaskChecklistLocation) (*UpdateTaskChecklistLocationPayload, error) { + checklist, err := r.Repository.UpdateTaskChecklistPosition(ctx, db.UpdateTaskChecklistPositionParams{Position: input.Position, TaskChecklistID: input.TaskChecklistID}) + + if err != nil { + return &UpdateTaskChecklistLocationPayload{}, err + } + + return &UpdateTaskChecklistLocationPayload{Checklist: &checklist}, nil +} + +func (r *mutationResolver) UpdateTaskChecklistItemName(ctx context.Context, input UpdateTaskChecklistItemName) (*db.TaskChecklistItem, error) { + task, err := r.Repository.UpdateTaskChecklistItemName(ctx, db.UpdateTaskChecklistItemNameParams{TaskChecklistItemID: input.TaskChecklistItemID, + Name: input.Name, + }) + if err != nil { + return &db.TaskChecklistItem{}, err + } + return &task, nil +} + +func (r *mutationResolver) SetTaskChecklistItemComplete(ctx context.Context, input SetTaskChecklistItemComplete) (*db.TaskChecklistItem, error) { + item, err := r.Repository.SetTaskChecklistItemComplete(ctx, db.SetTaskChecklistItemCompleteParams{TaskChecklistItemID: input.TaskChecklistItemID, Complete: input.Complete}) + if err != nil { + return &db.TaskChecklistItem{}, err + } + return &item, nil +} + +func (r *mutationResolver) DeleteTaskChecklistItem(ctx context.Context, input DeleteTaskChecklistItem) (*DeleteTaskChecklistItemPayload, error) { + item, err := r.Repository.GetTaskChecklistItemByID(ctx, input.TaskChecklistItemID) + if err != nil { + return &DeleteTaskChecklistItemPayload{ + Ok: false, + TaskChecklistItem: &db.TaskChecklistItem{}, + }, err + } + err = r.Repository.DeleteTaskChecklistItem(ctx, input.TaskChecklistItemID) + if err != nil { + return &DeleteTaskChecklistItemPayload{ + Ok: false, + TaskChecklistItem: &db.TaskChecklistItem{}, + }, err + } + return &DeleteTaskChecklistItemPayload{ + Ok: true, + TaskChecklistItem: &item, + }, err +} + +func (r *mutationResolver) UpdateTaskChecklistItemLocation(ctx context.Context, input UpdateTaskChecklistItemLocation) (*UpdateTaskChecklistItemLocationPayload, error) { + currentChecklistItem, err := r.Repository.GetTaskChecklistItemByID(ctx, input.TaskChecklistItemID) + + checklistItem, err := r.Repository.UpdateTaskChecklistItemLocation(ctx, db.UpdateTaskChecklistItemLocationParams{TaskChecklistID: input.TaskChecklistID, TaskChecklistItemID: input.TaskChecklistItemID, Position: input.Position}) + if err != nil { + return &UpdateTaskChecklistItemLocationPayload{}, err + } + return &UpdateTaskChecklistItemLocationPayload{PrevChecklistID: currentChecklistItem.TaskChecklistID, TaskChecklistID: input.TaskChecklistID, ChecklistItem: &checklistItem}, err +} + +func (r *mutationResolver) CreateTaskComment(ctx context.Context, input *CreateTaskComment) (*CreateTaskCommentPayload, error) { + userID, _ := GetUserID(ctx) + createdAt := time.Now().UTC() + comment, err := r.Repository.CreateTaskComment(ctx, db.CreateTaskCommentParams{ + TaskID: input.TaskID, + CreatedAt: createdAt, + CreatedBy: userID, + Message: input.Message, + }) + return &CreateTaskCommentPayload{Comment: &comment, TaskID: input.TaskID}, err +} + +func (r *mutationResolver) DeleteTaskComment(ctx context.Context, input *DeleteTaskComment) (*DeleteTaskCommentPayload, error) { + task, err := r.Repository.DeleteTaskCommentByID(ctx, input.CommentID) + return &DeleteTaskCommentPayload{TaskID: task.TaskID, CommentID: input.CommentID}, err +} + +func (r *mutationResolver) UpdateTaskComment(ctx context.Context, input *UpdateTaskComment) (*UpdateTaskCommentPayload, error) { + updatedAt := time.Now().UTC() + comment, err := r.Repository.UpdateTaskComment(ctx, db.UpdateTaskCommentParams{ + TaskCommentID: input.CommentID, + UpdatedAt: sql.NullTime{Valid: true, Time: updatedAt}, + Message: input.Message, + }) + return &UpdateTaskCommentPayload{Comment: &comment}, err +} + +func (r *mutationResolver) CreateTaskGroup(ctx context.Context, input NewTaskGroup) (*db.TaskGroup, error) { + createdAt := time.Now().UTC() + project, err := r.Repository.CreateTaskGroup(ctx, + db.CreateTaskGroupParams{input.ProjectID, createdAt, input.Name, input.Position}) + return &project, err +} + +func (r *mutationResolver) UpdateTaskGroupLocation(ctx context.Context, input NewTaskGroupLocation) (*db.TaskGroup, error) { + taskGroup, err := r.Repository.UpdateTaskGroupLocation(ctx, db.UpdateTaskGroupLocationParams{ + input.TaskGroupID, + input.Position, + }) + return &taskGroup, err +} + +func (r *mutationResolver) UpdateTaskGroupName(ctx context.Context, input UpdateTaskGroupName) (*db.TaskGroup, error) { + taskGroup, err := r.Repository.SetTaskGroupName(ctx, db.SetTaskGroupNameParams{TaskGroupID: input.TaskGroupID, Name: input.Name}) + if err != nil { + return &db.TaskGroup{}, err + } + return &taskGroup, nil +} + +func (r *mutationResolver) DeleteTaskGroup(ctx context.Context, input DeleteTaskGroupInput) (*DeleteTaskGroupPayload, error) { + deletedTasks, err := r.Repository.DeleteTasksByTaskGroupID(ctx, input.TaskGroupID) + if err != nil { + return &DeleteTaskGroupPayload{}, err + } + taskGroup, err := r.Repository.GetTaskGroupByID(ctx, input.TaskGroupID) + if err != nil { + return &DeleteTaskGroupPayload{}, err + } + deletedTaskGroups, err := r.Repository.DeleteTaskGroupByID(ctx, input.TaskGroupID) + if err != nil { + return &DeleteTaskGroupPayload{}, err + } + return &DeleteTaskGroupPayload{true, int(deletedTasks + deletedTaskGroups), &taskGroup}, nil +} + +func (r *mutationResolver) DuplicateTaskGroup(ctx context.Context, input DuplicateTaskGroup) (*DuplicateTaskGroupPayload, error) { + createdAt := time.Now().UTC() + taskGroup, err := r.Repository.CreateTaskGroup(ctx, db.CreateTaskGroupParams{ProjectID: input.ProjectID, Position: input.Position, Name: input.Name, CreatedAt: createdAt}) + if err != nil { + return &DuplicateTaskGroupPayload{}, err + } + + originalTasks, err := r.Repository.GetTasksForTaskGroupID(ctx, input.TaskGroupID) + if err != nil && err != sql.ErrNoRows { + return &DuplicateTaskGroupPayload{}, err + } + for _, originalTask := range originalTasks { + task, err := r.Repository.CreateTaskAll(ctx, db.CreateTaskAllParams{ + TaskGroupID: taskGroup.TaskGroupID, CreatedAt: createdAt, Name: originalTask.Name, Position: originalTask.Position, + Complete: originalTask.Complete, DueDate: originalTask.DueDate, Description: originalTask.Description}) + if err != nil { + return &DuplicateTaskGroupPayload{}, err + } + members, err := r.Repository.GetAssignedMembersForTask(ctx, originalTask.TaskID) + if err != nil { + return &DuplicateTaskGroupPayload{}, err + } + for _, member := range members { + _, err := r.Repository.CreateTaskAssigned(ctx, db.CreateTaskAssignedParams{ + TaskID: task.TaskID, UserID: member.UserID, AssignedDate: member.AssignedDate}) + if err != nil { + return &DuplicateTaskGroupPayload{}, err + } + } + labels, err := r.Repository.GetTaskLabelsForTaskID(ctx, originalTask.TaskID) + if err != nil { + return &DuplicateTaskGroupPayload{}, err + } + for _, label := range labels { + _, err := r.Repository.CreateTaskLabelForTask(ctx, db.CreateTaskLabelForTaskParams{ + TaskID: task.TaskID, ProjectLabelID: label.ProjectLabelID, AssignedDate: label.AssignedDate}) + if err != nil { + return &DuplicateTaskGroupPayload{}, err + } + } + checklists, err := r.Repository.GetTaskChecklistsForTask(ctx, originalTask.TaskID) + if err != nil { + return &DuplicateTaskGroupPayload{}, err + } + for _, checklist := range checklists { + newChecklist, err := r.Repository.CreateTaskChecklist(ctx, db.CreateTaskChecklistParams{ + TaskID: task.TaskID, Name: checklist.Name, CreatedAt: createdAt, Position: checklist.Position}) + if err != nil { + return &DuplicateTaskGroupPayload{}, err + } + checklistItems, err := r.Repository.GetTaskChecklistItemsForTaskChecklist(ctx, checklist.TaskChecklistID) + if err != nil { + return &DuplicateTaskGroupPayload{}, err + } + for _, checklistItem := range checklistItems { + item, err := r.Repository.CreateTaskChecklistItem(ctx, db.CreateTaskChecklistItemParams{ + TaskChecklistID: newChecklist.TaskChecklistID, + CreatedAt: createdAt, + Name: checklistItem.Name, + Position: checklist.Position, + }) + if checklistItem.Complete { + r.Repository.SetTaskChecklistItemComplete(ctx, db.SetTaskChecklistItemCompleteParams{TaskChecklistItemID: item.TaskChecklistItemID, Complete: true}) + } + if err != nil { + return &DuplicateTaskGroupPayload{}, err + } + } + + } + } + if err != nil { + return &DuplicateTaskGroupPayload{}, err + } + return &DuplicateTaskGroupPayload{TaskGroup: &taskGroup}, err +} + +func (r *mutationResolver) SortTaskGroup(ctx context.Context, input SortTaskGroup) (*SortTaskGroupPayload, error) { + tasks := []db.Task{} + for _, task := range input.Tasks { + t, err := r.Repository.UpdateTaskPosition(ctx, db.UpdateTaskPositionParams{TaskID: task.TaskID, Position: task.Position}) + if err != nil { + return &SortTaskGroupPayload{}, err + } + tasks = append(tasks, t) + } + return &SortTaskGroupPayload{Tasks: tasks, TaskGroupID: input.TaskGroupID}, nil +} + +func (r *mutationResolver) DeleteTaskGroupTasks(ctx context.Context, input DeleteTaskGroupTasks) (*DeleteTaskGroupTasksPayload, error) { + tasks, err := r.Repository.GetTasksForTaskGroupID(ctx, input.TaskGroupID) + if err != nil && err != sql.ErrNoRows { + return &DeleteTaskGroupTasksPayload{}, err + } + removedTasks := []uuid.UUID{} + for _, task := range tasks { + err = r.Repository.DeleteTaskByID(ctx, task.TaskID) + if err != nil { + return &DeleteTaskGroupTasksPayload{}, err + } + removedTasks = append(removedTasks, task.TaskID) + } + return &DeleteTaskGroupTasksPayload{TaskGroupID: input.TaskGroupID, Tasks: removedTasks}, nil +} + +func (r *mutationResolver) AddTaskLabel(ctx context.Context, input *AddTaskLabelInput) (*db.Task, error) { + assignedDate := time.Now().UTC() + _, err := r.Repository.CreateTaskLabelForTask(ctx, db.CreateTaskLabelForTaskParams{input.TaskID, input.ProjectLabelID, assignedDate}) + if err != nil { + return &db.Task{}, err + } + task, err := r.Repository.GetTaskByID(ctx, input.TaskID) + return &task, nil +} + +func (r *mutationResolver) RemoveTaskLabel(ctx context.Context, input *RemoveTaskLabelInput) (*db.Task, error) { + taskLabel, err := r.Repository.GetTaskLabelByID(ctx, input.TaskLabelID) + if err != nil { + return &db.Task{}, err + } + task, err := r.Repository.GetTaskByID(ctx, taskLabel.TaskID) + if err != nil { + return &db.Task{}, err + } + err = r.Repository.DeleteTaskLabelByID(ctx, input.TaskLabelID) + return &task, err +} + +func (r *mutationResolver) ToggleTaskLabel(ctx context.Context, input ToggleTaskLabelInput) (*ToggleTaskLabelPayload, error) { + task, err := r.Repository.GetTaskByID(ctx, input.TaskID) + if err != nil { + return &ToggleTaskLabelPayload{}, err + } + + _, err = r.Repository.GetTaskLabelForTaskByProjectLabelID(ctx, db.GetTaskLabelForTaskByProjectLabelIDParams{TaskID: input.TaskID, ProjectLabelID: input.ProjectLabelID}) + createdAt := time.Now().UTC() + + if err == sql.ErrNoRows { + logger.New(ctx).WithFields(log.Fields{"err": err}).Warning("no rows") + _, err := r.Repository.CreateTaskLabelForTask(ctx, db.CreateTaskLabelForTaskParams{ + TaskID: input.TaskID, + ProjectLabelID: input.ProjectLabelID, + AssignedDate: createdAt, + }) + if err != nil { + return &ToggleTaskLabelPayload{}, err + } + payload := ToggleTaskLabelPayload{Active: true, Task: &task} + return &payload, nil + } + + if err != nil { + return &ToggleTaskLabelPayload{}, err + } + + err = r.Repository.DeleteTaskLabelForTaskByProjectLabelID(ctx, db.DeleteTaskLabelForTaskByProjectLabelIDParams{ + TaskID: input.TaskID, + ProjectLabelID: input.ProjectLabelID, + }) + + if err != nil { + return &ToggleTaskLabelPayload{}, err + } + + payload := ToggleTaskLabelPayload{Active: false, Task: &task} + return &payload, nil +} + +func (r *mutationResolver) CreateTask(ctx context.Context, input NewTask) (*db.Task, error) { + createdAt := time.Now().UTC() + logger.New(ctx).WithFields(log.Fields{"positon": input.Position, "taskGroupID": input.TaskGroupID}).Info("creating task") + task, err := r.Repository.CreateTask(ctx, db.CreateTaskParams{input.TaskGroupID, createdAt, input.Name, input.Position}) + if err != nil { + logger.New(ctx).WithError(err).Error("issue while creating task") + return &db.Task{}, err + } + taskGroup, err := r.Repository.GetTaskGroupByID(ctx, input.TaskGroupID) + if err != nil { + logger.New(ctx).WithError(err).Error("issue while creating task") + return &db.Task{}, err + } + data := map[string]string{ + "TaskGroup": taskGroup.Name, + } + userID, _ := GetUserID(ctx) + d, err := json.Marshal(data) + _, err = r.Repository.CreateTaskActivity(ctx, db.CreateTaskActivityParams{ + TaskID: task.TaskID, + Data: d, + CreatedAt: createdAt, + CausedBy: userID, + ActivityTypeID: 1, + }) + + if len(input.Assigned) != 0 { + assignedDate := time.Now().UTC() + for _, assigned := range input.Assigned { + assignedTask, err := r.Repository.CreateTaskAssigned(ctx, db.CreateTaskAssignedParams{TaskID: task.TaskID, UserID: assigned, AssignedDate: assignedDate}) + logger.New(ctx).WithFields(log.Fields{ + "assignedUserID": assignedTask.UserID, + "taskID": assignedTask.TaskID, + "assignedTaskID": assignedTask.TaskAssignedID, + }).Info("assigned task") + if err != nil { + return &db.Task{}, err + } + } + } + + if err != nil { + logger.New(ctx).WithError(err).Error("issue while creating task") + return &db.Task{}, err + } + return &task, nil +} + +func (r *mutationResolver) DeleteTask(ctx context.Context, input DeleteTaskInput) (*DeleteTaskPayload, error) { + logger.New(ctx).WithFields(log.Fields{ + "taskID": input.TaskID, + }).Info("deleting task") + err := r.Repository.DeleteTaskByID(ctx, input.TaskID) + if err != nil { + return &DeleteTaskPayload{}, err + } + return &DeleteTaskPayload{input.TaskID}, nil +} + +func (r *mutationResolver) UpdateTaskDescription(ctx context.Context, input UpdateTaskDescriptionInput) (*db.Task, error) { + task, err := r.Repository.UpdateTaskDescription(ctx, db.UpdateTaskDescriptionParams{input.TaskID, sql.NullString{String: input.Description, Valid: true}}) + return &task, err +} + +func (r *mutationResolver) UpdateTaskLocation(ctx context.Context, input NewTaskLocation) (*UpdateTaskLocationPayload, error) { + userID, _ := GetUserID(ctx) + previousTask, err := r.Repository.GetTaskByID(ctx, input.TaskID) + if err != nil { + return &UpdateTaskLocationPayload{}, err + } + task, _ := r.Repository.UpdateTaskLocation(ctx, db.UpdateTaskLocationParams{TaskID: input.TaskID, TaskGroupID: input.TaskGroupID, Position: input.Position}) + if previousTask.TaskGroupID != input.TaskGroupID { + skipAndDelete := false + lastMove, err := r.Repository.GetLastMoveForTaskID(ctx, input.TaskID) + if err == nil { + if lastMove.Active && lastMove.PrevTaskGroupID == input.TaskGroupID.String() { + skipAndDelete = true + } + } + if skipAndDelete { + _ = r.Repository.SetInactiveLastMoveForTaskID(ctx, input.TaskID) + } else { + prevTaskGroup, _ := r.Repository.GetTaskGroupByID(ctx, previousTask.TaskGroupID) + curTaskGroup, _ := r.Repository.GetTaskGroupByID(ctx, input.TaskGroupID) + + data := map[string]string{ + "PrevTaskGroup": prevTaskGroup.Name, + "PrevTaskGroupID": prevTaskGroup.TaskGroupID.String(), + "CurTaskGroup": curTaskGroup.Name, + "CurTaskGroupID": curTaskGroup.TaskGroupID.String(), + } + + createdAt := time.Now().UTC() + d, _ := json.Marshal(data) + _, err = r.Repository.CreateTaskActivity(ctx, db.CreateTaskActivityParams{ + TaskID: task.TaskID, + Data: d, + CausedBy: userID, + CreatedAt: createdAt, + ActivityTypeID: 2, + }) + } + } + return &UpdateTaskLocationPayload{Task: &task, PreviousTaskGroupID: previousTask.TaskGroupID}, err +} + +func (r *mutationResolver) UpdateTaskName(ctx context.Context, input UpdateTaskName) (*db.Task, error) { + task, err := r.Repository.UpdateTaskName(ctx, db.UpdateTaskNameParams{input.TaskID, input.Name}) + return &task, err +} + +func (r *mutationResolver) SetTaskComplete(ctx context.Context, input SetTaskComplete) (*db.Task, error) { + completedAt := time.Now().UTC() + data := map[string]string{} + activityType := TASK_MARK_INCOMPLETE + if input.Complete { + activityType = TASK_MARK_COMPLETE + } + createdAt := time.Now().UTC() + userID, _ := GetUserID(ctx) + d, err := json.Marshal(data) + _, err = r.Repository.CreateTaskActivity(ctx, db.CreateTaskActivityParams{ + TaskID: input.TaskID, + Data: d, + CausedBy: userID, + CreatedAt: createdAt, + ActivityTypeID: activityType, + }) + task, err := r.Repository.SetTaskComplete(ctx, db.SetTaskCompleteParams{TaskID: input.TaskID, Complete: input.Complete, CompletedAt: sql.NullTime{Time: completedAt, Valid: true}}) + if err != nil { + return &db.Task{}, err + } + return &task, nil +} + +func (r *mutationResolver) UpdateTaskDueDate(ctx context.Context, input UpdateTaskDueDate) (*db.Task, error) { + userID, _ := GetUserID(ctx) + prevTask, err := r.Repository.GetTaskByID(ctx, input.TaskID) + if err != nil { + return &db.Task{}, err + } + data := map[string]string{} + var activityType = TASK_DUE_DATE_ADDED + if input.DueDate == nil && prevTask.DueDate.Valid { + activityType = TASK_DUE_DATE_REMOVED + data["PrevDueDate"] = prevTask.DueDate.Time.String() + } else if prevTask.DueDate.Valid { + activityType = TASK_DUE_DATE_CHANGED + data["PrevDueDate"] = prevTask.DueDate.Time.String() + data["CurDueDate"] = input.DueDate.String() + } else if input.DueDate != nil { + data["DueDate"] = input.DueDate.String() + } + var dueDate sql.NullTime + log.WithField("dueDate", input.DueDate).Info("before ptr!") + if input.DueDate == nil { + dueDate = sql.NullTime{Valid: false, Time: time.Now()} + } else { + dueDate = sql.NullTime{Valid: true, Time: *input.DueDate} + } + var task db.Task + if !(input.DueDate == nil && !prevTask.DueDate.Valid) { + task, err = r.Repository.UpdateTaskDueDate(ctx, db.UpdateTaskDueDateParams{ + TaskID: input.TaskID, + DueDate: dueDate, + HasTime: input.HasTime, + }) + createdAt := time.Now().UTC() + d, _ := json.Marshal(data) + _, err = r.Repository.CreateTaskActivity(ctx, db.CreateTaskActivityParams{ + TaskID: task.TaskID, + Data: d, + CausedBy: userID, + CreatedAt: createdAt, + ActivityTypeID: activityType, + }) + } else { + task, err = r.Repository.GetTaskByID(ctx, input.TaskID) + } + + return &task, err +} + +func (r *mutationResolver) AssignTask(ctx context.Context, input *AssignTaskInput) (*db.Task, error) { + assignedDate := time.Now().UTC() + assignedTask, err := r.Repository.CreateTaskAssigned(ctx, db.CreateTaskAssignedParams{input.TaskID, input.UserID, assignedDate}) + logger.New(ctx).WithFields(log.Fields{ + "assignedUserID": assignedTask.UserID, + "taskID": assignedTask.TaskID, + "assignedTaskID": assignedTask.TaskAssignedID, + }).Info("assigned task") + if err != nil { + return &db.Task{}, err + } + // r.NotificationQueue.TaskMemberWasAdded(assignedTask.TaskID, userID, assignedTask.UserID) + task, err := r.Repository.GetTaskByID(ctx, input.TaskID) + return &task, err +} + +func (r *mutationResolver) UnassignTask(ctx context.Context, input *UnassignTaskInput) (*db.Task, error) { + task, err := r.Repository.GetTaskByID(ctx, input.TaskID) + if err != nil { + return &db.Task{}, err + } + _, err = r.Repository.DeleteTaskAssignedByID(ctx, db.DeleteTaskAssignedByIDParams{input.TaskID, input.UserID}) + if err != nil { + return &db.Task{}, err + } + return &task, nil +} + +func (r *taskResolver) ID(ctx context.Context, obj *db.Task) (uuid.UUID, error) { + return obj.TaskID, nil +} + +func (r *taskResolver) TaskGroup(ctx context.Context, obj *db.Task) (*db.TaskGroup, error) { + taskGroup, err := r.Repository.GetTaskGroupByID(ctx, obj.TaskGroupID) + return &taskGroup, err +} + +func (r *taskResolver) Description(ctx context.Context, obj *db.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) DueDate(ctx context.Context, obj *db.Task) (*time.Time, error) { + if obj.DueDate.Valid { + return &obj.DueDate.Time, nil + } + return nil, nil +} + +func (r *taskResolver) CompletedAt(ctx context.Context, obj *db.Task) (*time.Time, error) { + if obj.CompletedAt.Valid { + return &obj.CompletedAt.Time, nil + } + return nil, nil +} + +func (r *taskResolver) Assigned(ctx context.Context, obj *db.Task) ([]Member, error) { + taskMemberLinks, err := r.Repository.GetAssignedMembersForTask(ctx, obj.TaskID) + taskMembers := []Member{} + if err != nil { + return taskMembers, err + } + for _, taskMemberLink := range taskMemberLinks { + user, err := r.Repository.GetUserAccountByID(ctx, taskMemberLink.UserID) + if err != nil { + return taskMembers, err + } + var url *string + if user.ProfileAvatarUrl.Valid { + url = &user.ProfileAvatarUrl.String + } + profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor} + projectID, err := r.Repository.GetProjectIDForTask(ctx, obj.TaskID) + if err != nil { + return taskMembers, err + } + role, err := r.Repository.GetRoleForProjectMemberByUserID(ctx, db.GetRoleForProjectMemberByUserIDParams{UserID: user.UserID, ProjectID: projectID}) + + if err != nil { + if err == sql.ErrNoRows { + role = db.Role{Code: "owner", Name: "Owner"} + } else { + logger.New(ctx).WithError(err).Error("get role for project member") + return taskMembers, err + } + } + taskMembers = append(taskMembers, Member{ID: taskMemberLink.UserID, FullName: user.FullName, ProfileIcon: profileIcon, + Role: &role, + }) + } + return taskMembers, nil +} + +func (r *taskResolver) Labels(ctx context.Context, obj *db.Task) ([]db.TaskLabel, error) { + return r.Repository.GetTaskLabelsForTaskID(ctx, obj.TaskID) +} + +func (r *taskResolver) Checklists(ctx context.Context, obj *db.Task) ([]db.TaskChecklist, error) { + return r.Repository.GetTaskChecklistsForTask(ctx, obj.TaskID) +} + +func (r *taskResolver) Badges(ctx context.Context, obj *db.Task) (*TaskBadges, error) { + checklists, err := r.Repository.GetTaskChecklistsForTask(ctx, obj.TaskID) + if err != nil { + return &TaskBadges{}, err + } + comments, err := r.Repository.GetCommentCountForTask(ctx, obj.TaskID) + if err != nil { + return &TaskBadges{}, err + } + complete := 0 + total := 0 + for _, checklist := range checklists { + items, err := r.Repository.GetTaskChecklistItemsForTaskChecklist(ctx, checklist.TaskChecklistID) + if err != nil { + return &TaskBadges{}, err + } + for _, item := range items { + total++ + if item.Complete { + complete++ + } + } + } + var taskChecklist *ChecklistBadge + if total != 0 { + taskChecklist = &ChecklistBadge{Total: total, Complete: complete} + } + var taskComments *CommentsBadge + if comments != 0 { + taskComments = &CommentsBadge{Total: int(comments), Unread: false} + } + return &TaskBadges{Checklist: taskChecklist, Comments: taskComments}, nil +} + +func (r *taskResolver) Activity(ctx context.Context, obj *db.Task) ([]db.TaskActivity, error) { + activity, err := r.Repository.GetActivityForTaskID(ctx, obj.TaskID) + if err == sql.ErrNoRows { + return []db.TaskActivity{}, nil + } + return activity, err +} + +func (r *taskResolver) Comments(ctx context.Context, obj *db.Task) ([]db.TaskComment, error) { + comments, err := r.Repository.GetCommentsForTaskID(ctx, obj.TaskID) + if err == sql.ErrNoRows { + return []db.TaskComment{}, nil + } + return comments, err +} + +func (r *taskActivityResolver) ID(ctx context.Context, obj *db.TaskActivity) (uuid.UUID, error) { + return obj.TaskActivityID, nil +} + +func (r *taskActivityResolver) Type(ctx context.Context, obj *db.TaskActivity) (ActivityType, error) { + switch obj.ActivityTypeID { + case 1: + return ActivityTypeTaskAdded, nil + case 2: + return ActivityTypeTaskMoved, nil + case 3: + return ActivityTypeTaskMarkedComplete, nil + case 4: + return ActivityTypeTaskMarkedIncomplete, nil + case 5: + return ActivityTypeTaskDueDateChanged, nil + case 6: + return ActivityTypeTaskDueDateAdded, nil + case 7: + return ActivityTypeTaskDueDateRemoved, nil + case 8: + return ActivityTypeTaskChecklistChanged, nil + case 9: + return ActivityTypeTaskChecklistAdded, nil + case 10: + return ActivityTypeTaskChecklistRemoved, nil + default: + return ActivityTypeTaskAdded, errors.New("unknown type") + } +} + +func (r *taskActivityResolver) Data(ctx context.Context, obj *db.TaskActivity) ([]TaskActivityData, error) { + var data map[string]string + _ = json.Unmarshal(obj.Data, &data) + activity := []TaskActivityData{} + for name, value := range data { + activity = append(activity, TaskActivityData{ + Name: name, + Value: value, + }) + } + return activity, nil +} + +func (r *taskActivityResolver) CausedBy(ctx context.Context, obj *db.TaskActivity) (*CausedBy, error) { + user, err := r.Repository.GetUserAccountByID(ctx, obj.CausedBy) + var url *string + if user.ProfileAvatarUrl.Valid { + url = &user.ProfileAvatarUrl.String + } + profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor} + return &CausedBy{ + ID: obj.CausedBy, + FullName: user.FullName, + ProfileIcon: profileIcon, + }, err +} + +func (r *taskChecklistResolver) ID(ctx context.Context, obj *db.TaskChecklist) (uuid.UUID, error) { + return obj.TaskChecklistID, nil +} + +func (r *taskChecklistResolver) Items(ctx context.Context, obj *db.TaskChecklist) ([]db.TaskChecklistItem, error) { + return r.Repository.GetTaskChecklistItemsForTaskChecklist(ctx, obj.TaskChecklistID) +} + +func (r *taskChecklistItemResolver) ID(ctx context.Context, obj *db.TaskChecklistItem) (uuid.UUID, error) { + return obj.TaskChecklistItemID, nil +} + +func (r *taskChecklistItemResolver) DueDate(ctx context.Context, obj *db.TaskChecklistItem) (*time.Time, error) { + panic(fmt.Errorf("not implemented")) +} + +func (r *taskCommentResolver) ID(ctx context.Context, obj *db.TaskComment) (uuid.UUID, error) { + return obj.TaskCommentID, nil +} + +func (r *taskCommentResolver) UpdatedAt(ctx context.Context, obj *db.TaskComment) (*time.Time, error) { + if obj.UpdatedAt.Valid { + return &obj.UpdatedAt.Time, nil + } + return nil, nil +} + +func (r *taskCommentResolver) CreatedBy(ctx context.Context, obj *db.TaskComment) (*CreatedBy, error) { + user, err := r.Repository.GetUserAccountByID(ctx, obj.CreatedBy) + var url *string + if user.ProfileAvatarUrl.Valid { + url = &user.ProfileAvatarUrl.String + } + profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor} + return &CreatedBy{ + ID: obj.CreatedBy, + FullName: user.FullName, + ProfileIcon: profileIcon, + }, err +} + +func (r *taskLabelResolver) ID(ctx context.Context, obj *db.TaskLabel) (uuid.UUID, error) { + return obj.TaskLabelID, nil +} + +func (r *taskLabelResolver) ProjectLabel(ctx context.Context, obj *db.TaskLabel) (*db.ProjectLabel, error) { + projectLabel, err := r.Repository.GetProjectLabelByID(ctx, obj.ProjectLabelID) + return &projectLabel, err +} + +// Task returns TaskResolver implementation. +func (r *Resolver) Task() TaskResolver { return &taskResolver{r} } + +// TaskActivity returns TaskActivityResolver implementation. +func (r *Resolver) TaskActivity() TaskActivityResolver { return &taskActivityResolver{r} } + +// TaskChecklist returns TaskChecklistResolver implementation. +func (r *Resolver) TaskChecklist() TaskChecklistResolver { return &taskChecklistResolver{r} } + +// TaskChecklistItem returns TaskChecklistItemResolver implementation. +func (r *Resolver) TaskChecklistItem() TaskChecklistItemResolver { + return &taskChecklistItemResolver{r} +} + +// TaskComment returns TaskCommentResolver implementation. +func (r *Resolver) TaskComment() TaskCommentResolver { return &taskCommentResolver{r} } + +// TaskLabel returns TaskLabelResolver implementation. +func (r *Resolver) TaskLabel() TaskLabelResolver { return &taskLabelResolver{r} } + +type taskResolver struct{ *Resolver } +type taskActivityResolver struct{ *Resolver } +type taskChecklistResolver struct{ *Resolver } +type taskChecklistItemResolver struct{ *Resolver } +type taskCommentResolver struct{ *Resolver } +type taskLabelResolver struct{ *Resolver } diff --git a/internal/graph/taskList.resolvers.go b/internal/graph/taskList.resolvers.go new file mode 100644 index 0000000..875792d --- /dev/null +++ b/internal/graph/taskList.resolvers.go @@ -0,0 +1,29 @@ +package graph + +// This file will be automatically regenerated based on the schema, any resolver implementations +// will be copied through when generating and any unknown code will be moved to the end. + +import ( + "context" + + "github.com/google/uuid" + "github.com/jordanknott/taskcafe/internal/db" +) + +func (r *taskGroupResolver) ID(ctx context.Context, obj *db.TaskGroup) (uuid.UUID, error) { + return obj.TaskGroupID, nil +} + +func (r *taskGroupResolver) ProjectID(ctx context.Context, obj *db.TaskGroup) (string, error) { + return obj.ProjectID.String(), nil +} + +func (r *taskGroupResolver) Tasks(ctx context.Context, obj *db.TaskGroup) ([]db.Task, error) { + tasks, err := r.Repository.GetTasksForTaskGroupID(ctx, obj.TaskGroupID) + return tasks, err +} + +// TaskGroup returns TaskGroupResolver implementation. +func (r *Resolver) TaskGroup() TaskGroupResolver { return &taskGroupResolver{r} } + +type taskGroupResolver struct{ *Resolver } diff --git a/internal/graph/team.resolvers.go b/internal/graph/team.resolvers.go new file mode 100644 index 0000000..44769c5 --- /dev/null +++ b/internal/graph/team.resolvers.go @@ -0,0 +1,177 @@ +package graph + +// This file will be automatically regenerated based on the schema, any resolver implementations +// will be copied through when generating and any unknown code will be moved to the end. + +import ( + "context" + "fmt" + "time" + + "github.com/google/uuid" + "github.com/jordanknott/taskcafe/internal/db" + "github.com/jordanknott/taskcafe/internal/logger" + log "github.com/sirupsen/logrus" + "github.com/vektah/gqlparser/v2/gqlerror" +) + +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, db.CreateTeamMemberParams{TeamID: input.TeamID, UserID: input.UserID, Addeddate: addedDate, RoleCode: RoleCodeMember.String()}) + 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: &Member{ + ID: user.UserID, + Username: user.Username, + FullName: user.FullName, + ProfileIcon: profileIcon, + Role: &db.Role{Code: "member", Name: "Member"}, + }}, nil +} + +func (r *mutationResolver) UpdateTeamMemberRole(ctx context.Context, input UpdateTeamMemberRole) (*UpdateTeamMemberRolePayload, error) { + user, err := r.Repository.GetUserAccountByID(ctx, input.UserID) + if err != nil { + logger.New(ctx).WithError(err).Error("get user account") + return &UpdateTeamMemberRolePayload{Ok: false}, err + } + _, err = r.Repository.UpdateTeamMemberRole(ctx, db.UpdateTeamMemberRoleParams{TeamID: input.TeamID, + UserID: input.UserID, RoleCode: input.RoleCode.String()}) + if err != nil { + logger.New(ctx).WithError(err).Error("update project member role") + return &UpdateTeamMemberRolePayload{Ok: false}, err + } + role, err := r.Repository.GetRoleForTeamMember(ctx, db.GetRoleForTeamMemberParams{UserID: user.UserID, TeamID: input.TeamID}) + if err != nil { + logger.New(ctx).WithError(err).Error("get role for project member") + return &UpdateTeamMemberRolePayload{Ok: false}, err + } + var url *string + if user.ProfileAvatarUrl.Valid { + url = &user.ProfileAvatarUrl.String + } + profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor} + if user.ProfileAvatarUrl.Valid { + url = &user.ProfileAvatarUrl.String + } + member := Member{ID: user.UserID, FullName: user.FullName, ProfileIcon: profileIcon, + Role: &db.Role{Code: role.Code, Name: role.Name}, + } + return &UpdateTeamMemberRolePayload{Ok: true, Member: &member, TeamID: input.TeamID}, err +} + +func (r *mutationResolver) DeleteTeamMember(ctx context.Context, input DeleteTeamMember) (*DeleteTeamMemberPayload, error) { + err := r.Repository.DeleteTeamMember(ctx, db.DeleteTeamMemberParams{TeamID: input.TeamID, UserID: input.UserID}) + return &DeleteTeamMemberPayload{TeamID: input.TeamID, UserID: input.UserID}, err +} + +func (r *mutationResolver) DeleteTeam(ctx context.Context, input DeleteTeam) (*DeleteTeamPayload, error) { + team, err := r.Repository.GetTeamByID(ctx, input.TeamID) + if err != nil { + logger.New(ctx).Error(err) + return &DeleteTeamPayload{Ok: false}, err + } + projects, err := r.Repository.GetAllProjectsForTeam(ctx, input.TeamID) + if err != nil { + logger.New(ctx).Error(err) + return &DeleteTeamPayload{Ok: false}, err + } + err = r.Repository.DeleteTeamByID(ctx, input.TeamID) + if err != nil { + logger.New(ctx).Error(err) + return &DeleteTeamPayload{Ok: false}, err + } + + return &DeleteTeamPayload{Ok: true, Team: &team, Projects: projects}, nil +} + +func (r *mutationResolver) CreateTeam(ctx context.Context, input NewTeam) (*db.Team, error) { + userID, ok := GetUserID(ctx) + if !ok { + return &db.Team{}, nil + } + role, err := r.Repository.GetRoleForUserID(ctx, userID) + if err != nil { + log.WithError(err).Error("while creating team") + return &db.Team{}, nil + } + if ConvertToRoleCode(role.Code) != RoleCodeAdmin { + return &db.Team{}, &gqlerror.Error{ + Message: "Must be an organization admin", + Extensions: map[string]interface{}{ + "code": "0-400", + }, + } + } + createdAt := time.Now().UTC() + team, err := r.Repository.CreateTeam(ctx, db.CreateTeamParams{OrganizationID: input.OrganizationID, CreatedAt: createdAt, Name: input.Name}) + return &team, err +} + +func (r *teamResolver) ID(ctx context.Context, obj *db.Team) (uuid.UUID, error) { + return obj.TeamID, nil +} + +func (r *teamResolver) Permission(ctx context.Context, obj *db.Team) (*TeamPermission, error) { + panic(fmt.Errorf("not implemented")) +} + +func (r *teamResolver) Members(ctx context.Context, obj *db.Team) ([]Member, error) { + members := []Member{} + + teamMembers, err := r.Repository.GetTeamMembersForTeamID(ctx, obj.TeamID) + if err != nil { + logger.New(ctx).Error("get project members for project id") + return members, err + } + + for _, teamMember := range teamMembers { + user, err := r.Repository.GetUserAccountByID(ctx, teamMember.UserID) + if err != nil { + logger.New(ctx).WithError(err).Error("get user account by ID") + return members, err + } + var url *string + if user.ProfileAvatarUrl.Valid { + url = &user.ProfileAvatarUrl.String + } + profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor} + role, err := r.Repository.GetRoleForTeamMember(ctx, db.GetRoleForTeamMemberParams{UserID: user.UserID, TeamID: obj.TeamID}) + if err != nil { + logger.New(ctx).WithError(err).Error("get role for projet member by user ID") + return members, err + } + + ownedList, err := GetOwnedList(ctx, r.Repository, user) + if err != nil { + return members, err + } + memberList, err := GetMemberList(ctx, r.Repository, user) + if err != nil { + return members, err + } + + members = append(members, Member{ID: user.UserID, FullName: user.FullName, ProfileIcon: profileIcon, + Username: user.Username, Owned: ownedList, Member: memberList, Role: &db.Role{Code: role.Code, Name: role.Name}, + }) + } + return members, nil +} + +// Team returns TeamResolver implementation. +func (r *Resolver) Team() TeamResolver { return &teamResolver{r} } + +type teamResolver struct{ *Resolver } diff --git a/internal/graph/user.resolvers.go b/internal/graph/user.resolvers.go new file mode 100644 index 0000000..9e1e761 --- /dev/null +++ b/internal/graph/user.resolvers.go @@ -0,0 +1,302 @@ +package graph + +// This file will be automatically regenerated based on the schema, any resolver implementations +// will be copied through when generating and any unknown code will be moved to the end. + +import ( + "context" + "database/sql" + "errors" + "fmt" + "time" + + "github.com/google/uuid" + "github.com/jordanknott/taskcafe/internal/db" + "github.com/jordanknott/taskcafe/internal/logger" + "github.com/lithammer/fuzzysearch/fuzzy" + log "github.com/sirupsen/logrus" + "github.com/vektah/gqlparser/v2/gqlerror" + "golang.org/x/crypto/bcrypt" +) + +func (r *mutationResolver) CreateUserAccount(ctx context.Context, input NewUserAccount) (*db.UserAccount, error) { + userID, ok := GetUserID(ctx) + if !ok { + return &db.UserAccount{}, nil + } + role, err := r.Repository.GetRoleForUserID(ctx, userID) + if err != nil { + log.WithError(err).Error("while creating user account") + return &db.UserAccount{}, nil + } + if ConvertToRoleCode(role.Code) != RoleCodeAdmin { + return &db.UserAccount{}, &gqlerror.Error{ + Message: "Must be an organization admin", + Extensions: map[string]interface{}{ + "code": "0-400", + }, + } + } + createdAt := time.Now().UTC() + hashedPwd, err := bcrypt.GenerateFromPassword([]byte(input.Password), 14) + if err != nil { + return &db.UserAccount{}, err + } + + userExists, err := r.Repository.DoesUserExist(ctx, db.DoesUserExistParams{Username: input.Username, Email: input.Email}) + if err != nil { + return &db.UserAccount{}, err + } + if userExists { + return &db.UserAccount{}, &gqlerror.Error{ + Message: "User with that username or email already exists", + Extensions: map[string]interface{}{ + "code": "0-300", + }, + } + } + userAccount, err := r.Repository.CreateUserAccount(ctx, db.CreateUserAccountParams{ + FullName: input.FullName, + RoleCode: input.RoleCode, + Initials: input.Initials, + Email: input.Email, + Username: input.Username, + CreatedAt: createdAt, + Active: true, + PasswordHash: string(hashedPwd), + }) + return &userAccount, err +} + +func (r *mutationResolver) DeleteUserAccount(ctx context.Context, input DeleteUserAccount) (*DeleteUserAccountPayload, error) { + userID, ok := GetUserID(ctx) + if !ok { + return &DeleteUserAccountPayload{Ok: false}, nil + } + role, err := r.Repository.GetRoleForUserID(ctx, userID) + if err != nil { + log.WithError(err).Error("while deleting user account") + return &DeleteUserAccountPayload{}, nil + } + if ConvertToRoleCode(role.Code) != RoleCodeAdmin { + return &DeleteUserAccountPayload{}, &gqlerror.Error{ + Message: "Must be an organization admin", + Extensions: map[string]interface{}{ + "code": "0-400", + }, + } + } + user, err := r.Repository.GetUserAccountByID(ctx, input.UserID) + if err != nil { + return &DeleteUserAccountPayload{Ok: false}, err + } + + err = r.Repository.DeleteUserAccountByID(ctx, input.UserID) + if err != nil { + return &DeleteUserAccountPayload{Ok: false}, err + } + return &DeleteUserAccountPayload{UserAccount: &user, Ok: true}, nil +} + +func (r *mutationResolver) DeleteInvitedUserAccount(ctx context.Context, input DeleteInvitedUserAccount) (*DeleteInvitedUserAccountPayload, error) { + user, err := r.Repository.DeleteInvitedUserAccount(ctx, input.InvitedUserID) + if err != nil { + return &DeleteInvitedUserAccountPayload{}, err + } + err = r.Repository.DeleteConfirmTokenForEmail(ctx, user.Email) + if err != nil { + logger.New(ctx).WithError(err).Error("issue deleting confirm token") + return &DeleteInvitedUserAccountPayload{}, err + } + return &DeleteInvitedUserAccountPayload{ + InvitedUser: &InvitedUserAccount{ + Email: user.Email, + ID: user.UserAccountInvitedID, + InvitedOn: user.InvitedOn, + }, + }, err +} + +func (r *mutationResolver) LogoutUser(ctx context.Context, input LogoutUser) (bool, error) { + err := r.Repository.DeleteAuthTokenByUserID(ctx, input.UserID) + return true, err +} + +func (r *mutationResolver) ClearProfileAvatar(ctx context.Context) (*db.UserAccount, error) { + userID, ok := GetUserID(ctx) + if !ok { + return &db.UserAccount{}, fmt.Errorf("internal server error") + } + user, err := r.Repository.UpdateUserAccountProfileAvatarURL(ctx, db.UpdateUserAccountProfileAvatarURLParams{UserID: userID, ProfileAvatarUrl: sql.NullString{Valid: false, String: ""}}) + if err != nil { + return &db.UserAccount{}, err + } + return &user, nil +} + +func (r *mutationResolver) UpdateUserPassword(ctx context.Context, input UpdateUserPassword) (*UpdateUserPasswordPayload, error) { + hashedPwd, err := bcrypt.GenerateFromPassword([]byte(input.Password), 14) + if err != nil { + return &UpdateUserPasswordPayload{}, err + } + user, err := r.Repository.SetUserPassword(ctx, db.SetUserPasswordParams{UserID: input.UserID, PasswordHash: string(hashedPwd)}) + if err != nil { + return &UpdateUserPasswordPayload{}, err + } + return &UpdateUserPasswordPayload{Ok: true, User: &user}, err +} + +func (r *mutationResolver) UpdateUserRole(ctx context.Context, input UpdateUserRole) (*UpdateUserRolePayload, error) { + userID, ok := GetUserID(ctx) + if !ok { + return &UpdateUserRolePayload{}, nil + } + role, err := r.Repository.GetRoleForUserID(ctx, userID) + if err != nil { + log.WithError(err).Error("while updating user role") + return &UpdateUserRolePayload{}, nil + } + if ConvertToRoleCode(role.Code) != RoleCodeAdmin { + return &UpdateUserRolePayload{}, &gqlerror.Error{ + Message: "Must be an organization admin", + Extensions: map[string]interface{}{ + "code": "0-400", + }, + } + } + user, err := r.Repository.UpdateUserRole(ctx, db.UpdateUserRoleParams{RoleCode: input.RoleCode.String(), UserID: input.UserID}) + if err != nil { + return &UpdateUserRolePayload{}, err + } + return &UpdateUserRolePayload{User: &user}, nil +} + +func (r *mutationResolver) UpdateUserInfo(ctx context.Context, input UpdateUserInfo) (*UpdateUserInfoPayload, error) { + userID, ok := GetUserID(ctx) + if !ok { + return &UpdateUserInfoPayload{}, errors.New("invalid user ID") + } + user, err := r.Repository.UpdateUserAccountInfo(ctx, db.UpdateUserAccountInfoParams{ + Bio: input.Bio, FullName: input.Name, Initials: input.Initials, Email: input.Email, UserID: userID, + }) + return &UpdateUserInfoPayload{User: &user}, err +} + +func (r *queryResolver) SearchMembers(ctx context.Context, input MemberSearchFilter) ([]MemberSearchResult, error) { + availableMembers, err := r.Repository.GetMemberData(ctx, *input.ProjectID) + if err != nil { + logger.New(ctx).WithField("projectID", input.ProjectID).WithError(err).Error("error while getting member data") + return []MemberSearchResult{}, err + } + + invitedMembers, err := r.Repository.GetInvitedMembersForProjectID(ctx, *input.ProjectID) + if err != nil { + logger.New(ctx).WithField("projectID", input.ProjectID).WithError(err).Error("error while getting member data") + return []MemberSearchResult{}, err + } + + sortList := []string{} + masterList := map[string]MasterEntry{} + for _, member := range availableMembers { + sortList = append(sortList, member.Username) + sortList = append(sortList, member.Email) + masterList[member.Username] = MasterEntry{ID: member.UserID, MemberType: MemberTypeJoined} + masterList[member.Email] = MasterEntry{ID: member.UserID, MemberType: MemberTypeJoined} + } + for _, member := range invitedMembers { + sortList = append(sortList, member.Email) + logger.New(ctx).WithField("Email", member.Email).Info("adding member") + masterList[member.Email] = MasterEntry{ID: member.UserAccountInvitedID, MemberType: MemberTypeInvited} + } + + logger.New(ctx).WithField("searchFilter", input.SearchFilter).Info(sortList) + rankedList := fuzzy.RankFind(input.SearchFilter, sortList) + logger.New(ctx).Info(rankedList) + results := []MemberSearchResult{} + memberList := map[uuid.UUID]bool{} + for _, rank := range rankedList { + entry, _ := masterList[rank.Target] + _, ok := memberList[entry.ID] + logger.New(ctx).WithField("ok", ok).WithField("target", rank.Target).Info("checking rank") + if !ok { + if entry.MemberType == MemberTypeJoined { + logger.New(ctx).WithFields(log.Fields{"source": rank.Source, "target": rank.Target}).Info("searching") + entry := masterList[rank.Target] + user, err := r.Repository.GetUserAccountByID(ctx, entry.ID) + if err != nil { + if err == sql.ErrNoRows { + continue + } + return []MemberSearchResult{}, err + } + results = append(results, MemberSearchResult{ID: user.UserID.String(), User: &user, Status: ShareStatusJoined, Similarity: rank.Distance}) + } else { + logger.New(ctx).WithField("id", rank.Target).Info("adding target") + results = append(results, MemberSearchResult{ID: rank.Target, Status: ShareStatusInvited, Similarity: rank.Distance}) + + } + memberList[entry.ID] = true + } + } + return results, nil +} + +func (r *userAccountResolver) ID(ctx context.Context, obj *db.UserAccount) (uuid.UUID, error) { + return obj.UserID, nil +} + +func (r *userAccountResolver) Role(ctx context.Context, obj *db.UserAccount) (*db.Role, error) { + role, err := r.Repository.GetRoleForUserID(ctx, obj.UserID) + if err != nil { + logger.New(ctx).WithError(err).Error("get role for user id") + return &db.Role{}, err + } + return &db.Role{Code: role.Code, Name: role.Name}, nil +} + +func (r *userAccountResolver) ProfileIcon(ctx context.Context, obj *db.UserAccount) (*ProfileIcon, error) { + var url *string + if obj.ProfileAvatarUrl.Valid { + url = &obj.ProfileAvatarUrl.String + } + profileIcon := &ProfileIcon{url, &obj.Initials, &obj.ProfileBgColor} + return profileIcon, nil +} + +func (r *userAccountResolver) Owned(ctx context.Context, obj *db.UserAccount) (*OwnedList, error) { + return &OwnedList{}, nil // TODO(jordanknott) +} + +func (r *userAccountResolver) Member(ctx context.Context, obj *db.UserAccount) (*MemberList, error) { + projectMemberIDs, err := r.Repository.GetMemberProjectIDsForUserID(ctx, obj.UserID) + if err != sql.ErrNoRows && err != nil { + return &MemberList{}, err + } + var projects []db.Project + for _, projectID := range projectMemberIDs { + project, err := r.Repository.GetProjectByID(ctx, projectID) + if err != nil { + return &MemberList{}, err + } + projects = append(projects, project) + } + teamMemberIDs, err := r.Repository.GetMemberTeamIDsForUserID(ctx, obj.UserID) + if err != sql.ErrNoRows && err != nil { + return &MemberList{}, err + } + var teams []db.Team + for _, teamID := range teamMemberIDs { + team, err := r.Repository.GetTeamByID(ctx, teamID) + if err != nil { + return &MemberList{}, err + } + teams = append(teams, team) + } + + return &MemberList{Teams: teams, Projects: projects}, err +} + +// UserAccount returns UserAccountResolver implementation. +func (r *Resolver) UserAccount() UserAccountResolver { return &userAccountResolver{r} } + +type userAccountResolver struct{ *Resolver } diff --git a/magefile.go b/magefile.go index 42b3c38..5aae11b 100644 --- a/magefile.go +++ b/magefile.go @@ -126,25 +126,33 @@ func (Backend) Build() error { // Schema merges GraphQL schema files into single schema & runs gqlgen func (Backend) Schema() error { - files, err := ioutil.ReadDir("internal/graph/schema/") + folders, err := ioutil.ReadDir("internal/graph/schema/") if err != nil { panic(err) } - var schema strings.Builder - for _, file := range files { - filename := "internal/graph/schema/" + file.Name() - fmt.Println(filename) - f, err := os.Open(filename) + for _, folder := range folders { + if !folder.IsDir() { + continue + } + var schema strings.Builder + filename := "internal/graph/schema/" + folder.Name() + files, err := ioutil.ReadDir(filename) if err != nil { panic(err) } - content, err := ioutil.ReadAll(f) - if err != nil { - panic(err) + for _, file := range files { + f, err := os.Open(filename + "/" + file.Name()) + if err != nil { + panic(err) + } + content, err := ioutil.ReadAll(f) + if err != nil { + panic(err) + } + fmt.Fprintln(&schema, string(content)) } - fmt.Fprintln(&schema, string(content)) + err = ioutil.WriteFile("internal/graph/schema/"+folder.Name()+".gql", []byte(schema.String()), os.FileMode(0755)) } - err = ioutil.WriteFile("internal/graph/schema.graphqls", []byte(schema.String()), os.FileMode(0755)) if err != nil { panic(err) }