You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
659 lines
17 KiB
659 lines
17 KiB
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 }
|