package resolvers // 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" "errors" "git.aiterp.net/stufflog/server/graph/loaders" "log" "sort" "time" "git.aiterp.net/stufflog/server/graph/graphcore" "git.aiterp.net/stufflog/server/internal/generate" "git.aiterp.net/stufflog/server/internal/slerrors" "git.aiterp.net/stufflog/server/models" ) func (r *mutationResolver) CreateProject(ctx context.Context, input graphcore.ProjectCreateInput) (*models.Project, error) { user := r.Auth.UserFromContext(ctx) if user == nil { return nil, slerrors.PermissionDenied } project := &models.Project{ ID: input.ID, Name: input.Name, Description: input.Description, DailyPoints: input.DailyPoints, } if !project.ValidKey() { return nil, errors.New("the project must have a valid ID (only uppercase allowed)") } if project.Name == "" || project.Description == "" { return nil, errors.New("name and description cannot be left empty") } project, err := r.Database.Projects().Insert(ctx, *project) if err != nil { return nil, err } err = r.Database.Projects().SetPermission(ctx, models.ProjectPermission{ ProjectID: project.ID, UserID: user.ID, Level: models.ProjectPermissionLevelOwner, }) return project, nil } func (r *mutationResolver) CreateActivity(ctx context.Context, input graphcore.ActivityCreateInput) (*models.Activity, error) { user := r.Auth.UserFromContext(ctx) if user == nil { return nil, slerrors.PermissionDenied } project, err := r.Database.Projects().Find(ctx, input.ProjectID) if err != nil { return nil, err } if perm, err := r.Auth.ProjectPermission(ctx, project.ID); err != nil || !perm.CanManageActivities() { return nil, slerrors.PermissionDenied } activity := &models.Activity{ ProjectID: project.ID, Name: input.Name, Countable: input.Countable != nil && *input.Countable, UnitIsTimeSpent: input.UnitIsTimeSpent != nil && *input.UnitIsTimeSpent, BaseValue: input.BaseValue, } if !activity.UnitIsTimeSpent && activity.Countable { if input.UnitName != nil { activity.UnitName = *input.UnitName } else { return nil, errors.New("unit name is required for countable non-time-spent activities") } } if activity.UnitIsTimeSpent || activity.Countable { if input.UnitValue != nil { activity.UnitValue = *input.UnitValue } } return r.Database.Activities().Insert(ctx, *activity) } func (r *mutationResolver) EditActivity(ctx context.Context, input graphcore.ActivityEditInput) (*models.Activity, error) { user := r.Auth.UserFromContext(ctx) if user == nil { return nil, slerrors.PermissionDenied } activity, err := r.Database.Activities().Find(ctx, input.ActivityID) if err != nil { return nil, err } project, err := r.Database.Projects().Find(ctx, activity.ProjectID) if err != nil { return nil, err } if perm, err := r.Auth.ProjectPermission(ctx, project.ID); err != nil || !perm.CanManageActivities() { return nil, slerrors.PermissionDenied } if input.SetName != nil { activity.Name = *input.SetName } if input.SetBaseValue != nil { activity.BaseValue = *input.SetBaseValue } if input.SetUnitName != nil { activity.UnitName = *input.SetUnitName } if input.SetUnitValue != nil { activity.UnitValue = *input.SetUnitValue } err = r.Database.Activities().Save(ctx, *activity) if err != nil { return nil, err } return activity, nil } func (r *mutationResolver) CreateItem(ctx context.Context, input *graphcore.ItemCreateInput) (*models.Item, error) { user := r.Auth.UserFromContext(ctx) if user == nil { return nil, slerrors.PermissionDenied } if input.Name == "" { return nil, errors.New("name cannot be blank") } if input.Description == "" { return nil, errors.New("name cannot be blank") } if len(input.Tags) == 0 { return nil, errors.New("at least one tag is required") } item := models.Item{ Name: input.Name, Description: input.Description, Tags: input.Tags, QuantityUnit: input.QuantityUnit, ImageURL: nil, } if input.Image != nil { url, err := r.Upload.UploadImage( ctx, "item-image/"+generate.Generate(16, "I"), input.Image.ContentType, input.Image.Size, input.Image.File, ) if err != nil { return nil, err } item.ImageURL = &url } return r.Database.Items().Insert(ctx, item) } func (r *mutationResolver) EditItem(ctx context.Context, input *graphcore.ItemEditInput) (*models.Item, error) { user := r.Auth.UserFromContext(ctx) if user == nil { return nil, slerrors.PermissionDenied } item, err := r.Database.Items().Find(ctx, input.ItemID) if err != nil { return nil, err } deleteList := make([]int, 0, len(input.RemoveTags)) for _, tag := range input.RemoveTags { for i, tag2 := range item.Tags { if tag == tag2 { deleteList = append(deleteList, i-len(deleteList)) break } } } for _, index := range deleteList { item.Tags = append(item.Tags[:index], item.Tags[index+1:]...) } for _, tag := range input.AddTags { found := false for _, tag2 := range item.Tags { if tag == tag2 { found = true break } } if !found { item.Tags = append(item.Tags, tag) } } sort.Strings(item.Tags) if input.SetQuantityUnit != nil { item.QuantityUnit = input.SetQuantityUnit } if input.ClearQuantityUnit != nil && *input.ClearQuantityUnit { item.QuantityUnit = nil } if input.SetName != nil { item.Name = *input.SetName } if input.SetDescription != nil { item.Description = *input.SetDescription } var prevFile *string if input.UpdateImage != nil { url, err := r.Upload.UploadImage( ctx, "item-image/"+generate.Generate(16, "U"), input.UpdateImage.ContentType, input.UpdateImage.Size, input.UpdateImage.File, ) if err != nil { return nil, err } prevFile = item.ImageURL item.ImageURL = &url } err = r.Database.Items().Save(ctx, *item) if err != nil { return nil, err } if prevFile != nil { err := r.Upload.Delete(ctx, *prevFile) if err != nil { log.Printf("Failed to delete %s: %s", *prevFile, err) } } return item, nil } func (r *mutationResolver) CreateIssue(ctx context.Context, input graphcore.IssueCreateInput) (*models.Issue, error) { user := r.Auth.UserFromContext(ctx) if user == nil { return nil, slerrors.PermissionDenied } project, err := r.Database.Projects().Find(ctx, input.ProjectID) if err != nil { return nil, err } if perm, err := r.Auth.ProjectPermission(ctx, project.ID); err != nil || !perm.CanManageOwnIssue() { return nil, slerrors.PermissionDenied } status, err := r.Database.ProjectStatuses().Find(ctx, project.ID, input.StatusName) if err != nil { return nil, err } issue := &models.Issue{ ProjectID: project.ID, OwnerID: user.ID, AssigneeID: "", StatusStage: status.Stage, StatusName: status.Name, Name: input.Name, Title: input.Name, // Title set below if it's in the input. Description: input.Description, } if input.Title != nil && *input.Title != "" { issue.Title = *input.Title } if input.DueTime != nil && !input.DueTime.IsZero() { issue.DueTime = *input.DueTime } if input.AssigneeID != nil && *input.AssigneeID != "" { issue.AssigneeID = *input.AssigneeID } issue, err = r.Database.Issues().Insert(ctx, *issue) if err != nil { return nil, err } loaders.IssueLoaderFromContext(ctx).Prime(issue.ID, issue) return issue, nil } func (r *mutationResolver) CreateIssueTask(ctx context.Context, input graphcore.IssueTaskCreateInput) (*models.IssueTask, error) { user := r.Auth.UserFromContext(ctx) if user == nil { return nil, slerrors.PermissionDenied } issue, err := loaders.IssueLoaderFromContext(ctx).Load(input.IssueID) if err != nil { return nil, err } if perm, err := r.Auth.IssuePermission(ctx, *issue); err != nil || !perm.CanManageOwnIssue() { return nil, slerrors.PermissionDenied } status, err := r.Database.ProjectStatuses().Find(ctx, issue.ProjectID, input.StatusName) if err != nil { return nil, err } activity, err := loaders.ActivityLoaderFromContext(ctx).Load(input.ActivityID) if err != nil { return nil, err } else if activity.ProjectID != issue.ProjectID { return nil, slerrors.NotFound("Activity") } issueTask := &models.IssueTask{ IssueID: issue.ID, ActivityID: activity.ID, CreatedTime: time.Now(), UpdatedTime: time.Now(), StatusStage: status.Stage, StatusName: status.Name, Name: input.Name, Description: input.Description, PointsMultiplier: 1.0, } if input.EstimatedUnits != nil && activity.Countable && !activity.UnitIsTimeSpent { issueTask.EstimatedUnits = *input.EstimatedUnits } if input.PointsMultiplier != nil && *input.PointsMultiplier > 0 { issueTask.PointsMultiplier = *input.PointsMultiplier } issueTask, err = r.Database.IssueTasks().Insert(ctx, *issueTask) if err != nil { return nil, err } return issueTask, nil } func (r *mutationResolver) EditIssueTask(ctx context.Context, input graphcore.IssueTaskEditInput) (*models.IssueTask, error) { user := r.Auth.UserFromContext(ctx) if user == nil { return nil, slerrors.PermissionDenied } task, err := r.Database.IssueTasks().Find(ctx, input.IssueTaskID) if err != nil { return nil, err } issue, err := loaders.IssueLoaderFromContext(ctx).Load(task.IssueID) if err != nil { return nil, err } if perm, err := r.Auth.IssuePermission(ctx, *issue); err != nil || !perm.CanManageOwnIssue() { return nil, slerrors.PermissionDenied } if input.SetName != nil { task.Name = *input.SetName } if input.SetDescription != nil { task.Description = *input.SetDescription } if input.SetDueTime != nil { task.DueTime = input.SetDueTime } if input.SetEstimatedTime != nil { task.EstimatedTime = *input.SetEstimatedTime } if input.SetEstimatedUnits != nil { activity, err := loaders.ActivityLoaderFromContext(ctx).Load(task.ActivityID) if err != nil { return nil, err } if activity.Countable && !activity.UnitIsTimeSpent { task.EstimatedUnits = *input.SetEstimatedUnits } } if input.SetPointsMultiplier != nil && *input.SetPointsMultiplier > 0 { task.PointsMultiplier = *input.SetPointsMultiplier } if input.SetStatusName != nil { status, err := r.Database.ProjectStatuses().Find(ctx, issue.ProjectID, *input.SetStatusName) if err != nil { return nil, err } task.StatusName = status.Name task.StatusStage = status.Stage } err = r.Database.IssueTasks().Save(ctx, *task) if err != nil { return nil, err } return task, nil } func (r *mutationResolver) CreateIssueItem(ctx context.Context, input graphcore.IssueItemCreateInput) (*models.IssueItem, error) { user := r.Auth.UserFromContext(ctx) if user == nil { return nil, slerrors.PermissionDenied } issue, err := loaders.IssueLoaderFromContext(ctx).Load(input.IssueID) if err != nil { return nil, err } if perm, err := r.Auth.IssuePermission(ctx, *issue); err != nil || !perm.CanManageOwnIssue() { return nil, slerrors.PermissionDenied } item, err := r.Database.Items().Find(ctx, input.ItemID) if err != nil { return nil, err } issueItem := &models.IssueItem{ IssueID: issue.ID, ItemID: item.ID, Quantity: input.Quanitty, Acquired: input.Acquired != nil && *input.Acquired, } return r.Database.IssueItems().Insert(ctx, *issueItem) } func (r *mutationResolver) EditIssueItem(ctx context.Context, input graphcore.IssueItemEditInput) (*models.IssueItem, error) { user := r.Auth.UserFromContext(ctx) if user == nil { return nil, slerrors.PermissionDenied } item, err := r.Database.IssueItems().Find(ctx, input.IssueItemID) if err != nil { return nil, err } issue, err := loaders.IssueLoaderFromContext(ctx).Load(item.IssueID) if err != nil { return nil, err } if perm, err := r.Auth.IssuePermission(ctx, *issue); err != nil || !perm.CanManageOwnIssue() { return nil, slerrors.PermissionDenied } if input.SetAcquired != nil { item.Acquired = *input.SetAcquired } if input.SetQuanitty != nil { item.Quantity = *input.SetQuanitty } err = r.Database.IssueItems().Save(ctx, *item) if err != nil { return nil, err } return item, nil } func (r *mutationResolver) CreateLog(ctx context.Context, input graphcore.LogCreateInput) (*models.Log, error) { user := r.Auth.UserFromContext(ctx) if user == nil { return nil, slerrors.PermissionDenied } log := &models.Log{ UserID: user.ID, Date: input.Date, Description: input.Description, Items: make([]models.LogItem, 0, len(input.Items)), Tasks: make([]models.LogTask, 0, len(input.Tasks)), } for _, itemInput := range input.Items { item, err := r.Database.IssueItems().Find(ctx, itemInput.IssueItemID) if err != nil { return nil, err } issue, err := loaders.IssueLoaderFromContext(ctx).Load(item.IssueID) if err != nil { return nil, err } _, err = r.Auth.IssuePermission(ctx, *issue) if err != nil { return nil, err } log.Items = append(log.Items, models.LogItem{ IssueID: item.IssueID, IssueItemID: item.ID, Amount: itemInput.Amount, }) } for _, taskInput := range input.Tasks { task, err := r.Database.IssueTasks().Find(ctx, taskInput.IssueTaskID) if err != nil { return nil, err } issue, err := loaders.IssueLoaderFromContext(ctx).Load(task.IssueID) if err != nil { return nil, err } _, err = r.Auth.IssuePermission(ctx, *issue) if err != nil { return nil, err } log.Tasks = append(log.Tasks, models.LogTask{ IssueID: task.IssueID, IssueTaskID: task.ID, Duration: taskInput.Duration, Units: taskInput.Units, }) } return r.Database.Logs().Insert(ctx, *log) } func (r *mutationResolver) EditLog(ctx context.Context, input graphcore.LogEditInput) (*models.Log, error) { user := r.Auth.UserFromContext(ctx) if user == nil { return nil, slerrors.PermissionDenied } log, err := r.Database.Logs().Find(ctx, input.LogID) if err != nil { return nil, err } removeItems := append([]string{}, input.RemoveItems...) for _, itemInput := range input.UpdateItems { removeItems = append(removeItems, itemInput.IssueItemID) } removeTasks := append([]string{}, input.RemoveTasks...) for _, taskInput := range input.UpdateTasks { removeTasks = append(removeTasks, taskInput.IssueTaskID) } if input.SetDate != nil { log.Date = *input.SetDate } if input.SetDescription != nil { log.Description = *input.SetDescription } for _, remove := range removeItems { for i, item := range log.Items { if remove == item.IssueItemID { log.Items = append(log.Items[:i], log.Items[i+1:]...) } } } for _, remove := range removeTasks { for i, task := range log.Tasks { if remove == task.IssueTaskID { log.Tasks = append(log.Tasks[:i], log.Tasks[i+1:]...) } } } for _, itemInput := range input.UpdateItems { item, err := r.Database.IssueItems().Find(ctx, itemInput.IssueItemID) if err != nil { return nil, err } issue, err := loaders.IssueLoaderFromContext(ctx).Load(item.IssueID) if err != nil { return nil, err } _, err = r.Auth.IssuePermission(ctx, *issue) if err != nil { return nil, err } log.Items = append(log.Items, models.LogItem{ LogID: log.ID, IssueID: item.IssueID, IssueItemID: item.ID, Amount: itemInput.Amount, }) } for _, taskInput := range input.UpdateTasks { task, err := r.Database.IssueTasks().Find(ctx, taskInput.IssueTaskID) if err != nil { return nil, err } issue, err := loaders.IssueLoaderFromContext(ctx).Load(task.IssueID) if err != nil { return nil, err } _, err = r.Auth.IssuePermission(ctx, *issue) if err != nil { return nil, err } log.Tasks = append(log.Tasks, models.LogTask{ LogID: log.ID, IssueID: task.IssueID, IssueTaskID: task.ID, Duration: taskInput.Duration, Units: taskInput.Units, }) } err = r.Database.Logs().Save(ctx, *log) if err != nil { return nil, err } return log, nil } func (r *mutationResolver) LoginUser(ctx context.Context, input graphcore.UserLoginInput) (*models.User, error) { return r.Auth.Login(ctx, input.Username, input.Password) } func (r *mutationResolver) LogoutUser(ctx context.Context) (*models.User, error) { return r.Auth.Logout(ctx) } func (r *mutationResolver) CreateUser(ctx context.Context, input graphcore.UserCreateInput) (*models.User, error) { active := input.Active == nil || *input.Active admin := input.Admin != nil && *input.Admin return r.Auth.CreateUser(ctx, input.Username, input.Password, input.Name, active, admin) } func (r *mutationResolver) EditUser(ctx context.Context, input graphcore.UserEditInput) (*models.User, error) { return r.Auth.EditUser(ctx, input.Username, input.SetName, input.CurrentPassword, input.SetPassword) } // Mutation returns graphcore.MutationResolver implementation. func (r *Resolver) Mutation() graphcore.MutationResolver { return &mutationResolver{r} } type mutationResolver struct{ *Resolver }