refactor: split resolver into multiple files based on domain
This commit is contained in:
		@@ -1,6 +1,6 @@
 | 
				
			|||||||
# Where are all the schema files located? globs are supported eg  src/**/*.graphqls
 | 
					# Where are all the schema files located? globs are supported eg  src/**/*.graphqls
 | 
				
			||||||
schema:
 | 
					schema:
 | 
				
			||||||
  - internal/graph/*.graphqls
 | 
					  - internal/graph/schema/*.gql
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Where should the generated server code go?
 | 
					# Where should the generated server code go?
 | 
				
			||||||
exec:
 | 
					exec:
 | 
				
			||||||
@@ -22,6 +22,8 @@ resolver:
 | 
				
			|||||||
  layout: follow-schema
 | 
					  layout: follow-schema
 | 
				
			||||||
  dir: internal/graph
 | 
					  dir: internal/graph
 | 
				
			||||||
  package: graph
 | 
					  package: graph
 | 
				
			||||||
 | 
					  filename_template: "{name}.resolvers.go"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Optional: turn on to use []Thing instead of []*Thing
 | 
					# Optional: turn on to use []Thing instead of []*Thing
 | 
				
			||||||
omit_slice_element_pointers: true
 | 
					omit_slice_element_pointers: true
 | 
				
			||||||
 
 | 
				
			|||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										92
									
								
								internal/graph/notification.resolvers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								internal/graph/notification.resolvers.go
									
									
									
									
									
										Normal file
									
								
							@@ -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 }
 | 
				
			||||||
							
								
								
									
										406
									
								
								internal/graph/project.resolvers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										406
									
								
								internal/graph/project.resolvers.go
									
									
									
									
									
										Normal file
									
								
							@@ -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 }
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -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!]!
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										0
									
								
								internal/graph/schema/notification.gql
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								internal/graph/schema/notification.gql
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										36
									
								
								internal/graph/schema/notification/notification.gql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								internal/graph/schema/notification/notification.gql
									
									
									
									
									
										Normal file
									
								
							@@ -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!
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										147
									
								
								internal/graph/schema/project.gql
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										147
									
								
								internal/graph/schema/project.gql
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							@@ -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 {
 | 
					extend type Mutation {
 | 
				
			||||||
  createProject(input: NewProject!): Project! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM)
 | 
					  createProject(input: NewProject!): Project! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM)
 | 
				
			||||||
  deleteProject(input: DeleteProject!):
 | 
					  deleteProject(input: DeleteProject!):
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										47
									
								
								internal/graph/schema/project/_model.gql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								internal/graph/schema/project/_model.gql
									
									
									
									
									
										Normal file
									
								
							@@ -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!
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -6,8 +6,8 @@ extend type Mutation {
 | 
				
			|||||||
  updateProjectMemberRole(input: UpdateProjectMemberRole!):
 | 
					  updateProjectMemberRole(input: UpdateProjectMemberRole!):
 | 
				
			||||||
    UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
 | 
					    UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
deleteInvitedProjectMember(input: DeleteInvitedProjectMember!):
 | 
					  deleteInvitedProjectMember(input: DeleteInvitedProjectMember!):
 | 
				
			||||||
  DeleteInvitedProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
 | 
					    DeleteInvitedProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
input DeleteInvitedProjectMember {
 | 
					input DeleteInvitedProjectMember {
 | 
				
			||||||
							
								
								
									
										36
									
								
								internal/graph/schema/project/project.gql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								internal/graph/schema/project/project.gql
									
									
									
									
									
										Normal file
									
								
							@@ -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!
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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 {
 | 
					enum ShareStatus {
 | 
				
			||||||
  INVITED
 | 
					  INVITED
 | 
				
			||||||
  JOINED
 | 
					  JOINED
 | 
				
			||||||
@@ -25,6 +67,7 @@ enum ObjectType {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION
 | 
					directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
directive @requiresUser on FIELD_DEFINITION
 | 
					directive @requiresUser on FIELD_DEFINITION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Query {
 | 
					type Query {
 | 
				
			||||||
							
								
								
									
										338
									
								
								internal/graph/schema/task.gql
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										338
									
								
								internal/graph/schema/task.gql
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							@@ -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 {
 | 
					extend type Mutation {
 | 
				
			||||||
  createTask(input: NewTask!):
 | 
					  createTask(input: NewTask!):
 | 
				
			||||||
    Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_GROUP)
 | 
					    Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK_GROUP)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										104
									
								
								internal/graph/schema/task/_model.gql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								internal/graph/schema/task/_model.gql
									
									
									
									
									
										Normal file
									
								
							@@ -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!]!
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										79
									
								
								internal/graph/schema/task/task.gql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								internal/graph/schema/task/task.gql
									
									
									
									
									
										Normal file
									
								
							@@ -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!
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										8
									
								
								internal/graph/schema/taskList.gql
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										8
									
								
								internal/graph/schema/taskList.gql
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					type TaskGroup {
 | 
				
			||||||
 | 
					  id: ID!
 | 
				
			||||||
 | 
					  projectID: String!
 | 
				
			||||||
 | 
					  createdAt: Time!
 | 
				
			||||||
 | 
					  name: String!
 | 
				
			||||||
 | 
					  position: Float!
 | 
				
			||||||
 | 
					  tasks: [Task!]!
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										8
									
								
								internal/graph/schema/taskList/_model.gql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								internal/graph/schema/taskList/_model.gql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					type TaskGroup {
 | 
				
			||||||
 | 
					  id: ID!
 | 
				
			||||||
 | 
					  projectID: String!
 | 
				
			||||||
 | 
					  createdAt: Time!
 | 
				
			||||||
 | 
					  name: String!
 | 
				
			||||||
 | 
					  position: Float!
 | 
				
			||||||
 | 
					  tasks: [Task!]!
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										0
									
								
								internal/graph/schema/taskList/taskList.gql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								internal/graph/schema/taskList/taskList.gql
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										58
									
								
								internal/graph/schema/team.gql
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										58
									
								
								internal/graph/schema/team.gql
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							@@ -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 {
 | 
					extend type Mutation {
 | 
				
			||||||
  deleteTeam(input: DeleteTeam!):
 | 
					  deleteTeam(input: DeleteTeam!):
 | 
				
			||||||
    DeleteTeamPayload! @hasRole(roles:[ ADMIN], level: TEAM, type: TEAM)
 | 
					    DeleteTeamPayload! @hasRole(roles:[ ADMIN], level: TEAM, type: TEAM)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								internal/graph/schema/team/_model.gql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								internal/graph/schema/team/_model.gql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					type Team {
 | 
				
			||||||
 | 
					  id: ID!
 | 
				
			||||||
 | 
					  createdAt: Time!
 | 
				
			||||||
 | 
					  name: String!
 | 
				
			||||||
 | 
					  permission: TeamPermission!
 | 
				
			||||||
 | 
					  members: [Member!]!
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TeamPermission {
 | 
				
			||||||
 | 
					  team: RoleCode!
 | 
				
			||||||
 | 
					  org: RoleCode!
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										21
									
								
								internal/graph/schema/team/team.gql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								internal/graph/schema/team/team.gql
									
									
									
									
									
										Normal file
									
								
							@@ -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!]!
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										23
									
								
								internal/graph/schema/user.gql
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										23
									
								
								internal/graph/schema/user.gql
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							@@ -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 {
 | 
					extend type Mutation {
 | 
				
			||||||
  createUserAccount(input: NewUserAccount!):
 | 
					  createUserAccount(input: NewUserAccount!):
 | 
				
			||||||
    UserAccount! @hasRole(roles: [ADMIN], level: ORG, type: ORG)
 | 
					    UserAccount! @hasRole(roles: [ADMIN], level: ORG, type: ORG)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										20
									
								
								internal/graph/schema/user/_model.gql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								internal/graph/schema/user/_model.gql
									
									
									
									
									
										Normal file
									
								
							@@ -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!
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										95
									
								
								internal/graph/schema/user/user.gql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								internal/graph/schema/user/user.gql
									
									
									
									
									
										Normal file
									
								
							@@ -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!
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										836
									
								
								internal/graph/task.resolvers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										836
									
								
								internal/graph/task.resolvers.go
									
									
									
									
									
										Normal file
									
								
							@@ -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 }
 | 
				
			||||||
							
								
								
									
										29
									
								
								internal/graph/taskList.resolvers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								internal/graph/taskList.resolvers.go
									
									
									
									
									
										Normal file
									
								
							@@ -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 }
 | 
				
			||||||
							
								
								
									
										177
									
								
								internal/graph/team.resolvers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								internal/graph/team.resolvers.go
									
									
									
									
									
										Normal file
									
								
							@@ -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 }
 | 
				
			||||||
							
								
								
									
										302
									
								
								internal/graph/user.resolvers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										302
									
								
								internal/graph/user.resolvers.go
									
									
									
									
									
										Normal file
									
								
							@@ -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 }
 | 
				
			||||||
							
								
								
									
										30
									
								
								magefile.go
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								magefile.go
									
									
									
									
									
								
							@@ -126,25 +126,33 @@ func (Backend) Build() error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Schema merges GraphQL schema files into single schema & runs gqlgen
 | 
					// Schema merges GraphQL schema files into single schema & runs gqlgen
 | 
				
			||||||
func (Backend) Schema() error {
 | 
					func (Backend) Schema() error {
 | 
				
			||||||
	files, err := ioutil.ReadDir("internal/graph/schema/")
 | 
						folders, err := ioutil.ReadDir("internal/graph/schema/")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		panic(err)
 | 
							panic(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	var schema strings.Builder
 | 
						for _, folder := range folders {
 | 
				
			||||||
	for _, file := range files {
 | 
							if !folder.IsDir() {
 | 
				
			||||||
		filename := "internal/graph/schema/" + file.Name()
 | 
								continue
 | 
				
			||||||
		fmt.Println(filename)
 | 
							}
 | 
				
			||||||
		f, err := os.Open(filename)
 | 
							var schema strings.Builder
 | 
				
			||||||
 | 
							filename := "internal/graph/schema/" + folder.Name()
 | 
				
			||||||
 | 
							files, err := ioutil.ReadDir(filename)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			panic(err)
 | 
								panic(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		content, err := ioutil.ReadAll(f)
 | 
							for _, file := range files {
 | 
				
			||||||
		if err != nil {
 | 
								f, err := os.Open(filename + "/" + file.Name())
 | 
				
			||||||
			panic(err)
 | 
								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 {
 | 
						if err != nil {
 | 
				
			||||||
		panic(err)
 | 
							panic(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user