| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801 |
- 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.
- // Code generated by github.com/99designs/gqlgen version v0.17.87
- import (
- "context"
- "errors"
- "fmt"
- "time"
- "gogs.dmsc.dev/arp/auth"
- "gogs.dmsc.dev/arp/graph/model"
- "gogs.dmsc.dev/arp/logging"
- "gogs.dmsc.dev/arp/models"
- "gogs.dmsc.dev/arp/workflow"
- )
- // Login is the resolver for the login field.
- func (r *mutationResolver) Login(ctx context.Context, email string, password string) (*model.AuthPayload, error) {
- var user models.User
- if err := r.DB.Preload("Roles.Permissions").Where("email = ?", email).First(&user).Error; err != nil {
- return nil, errors.New("invalid credentials")
- }
- // Check password
- if !auth.CheckPassword(password, user.Password) {
- return nil, errors.New("invalid credentials")
- }
- token, err := auth.GenerateToken(user)
- if err != nil {
- return nil, fmt.Errorf("failed to generate token: %w", err)
- }
- return &model.AuthPayload{
- Token: token,
- User: convertUser(user),
- }, nil
- }
- // CreateUser is the resolver for the createUser field.
- func (r *mutationResolver) CreateUser(ctx context.Context, input model.NewUser) (*model.User, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- roles := make([]models.Role, len(input.Roles))
- for i, roleIDStr := range input.Roles {
- roleID, err := toID(roleIDStr)
- if err != nil {
- return nil, fmt.Errorf("invalid role ID: %w", err)
- }
- var role models.Role
- if err := r.DB.First(&role, roleID).Error; err != nil {
- return nil, fmt.Errorf("role not found: %w", err)
- }
- roles[i] = role
- }
- // Hash the password before storing
- hashedPassword, err := auth.HashPassword(input.Password)
- if err != nil {
- return nil, fmt.Errorf("failed to hash password: %w", err)
- }
- user := models.User{
- Email: input.Email,
- Password: hashedPassword,
- Roles: roles,
- }
- if err := r.DB.Create(&user).Error; err != nil {
- return nil, fmt.Errorf("failed to create user: %w", err)
- }
- logging.LogMutation(ctx, "CREATE", "USER", user.Email)
- return convertUser(user), nil
- }
- // UpdateUser is the resolver for the updateUser field.
- func (r *mutationResolver) UpdateUser(ctx context.Context, id string, input model.UpdateUserInput) (*model.User, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "user:update") {
- return nil, errors.New("unauthorized: missing user:update permission")
- }
- userID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid user ID: %w", err)
- }
- var existing models.User
- if err := r.DB.First(&existing, userID).Error; err != nil {
- return nil, fmt.Errorf("user not found: %w", err)
- }
- if input.Email != nil {
- existing.Email = *input.Email
- }
- if input.Password != nil {
- // Hash the new password
- hashedPassword, err := auth.HashPassword(*input.Password)
- if err != nil {
- return nil, fmt.Errorf("failed to hash password: %w", err)
- }
- existing.Password = hashedPassword
- }
- if len(input.Roles) > 0 {
- roles := make([]models.Role, len(input.Roles))
- for i, roleIDStr := range input.Roles {
- roleID, err := toID(roleIDStr)
- if err != nil {
- return nil, fmt.Errorf("invalid role ID: %w", err)
- }
- var role models.Role
- if err := r.DB.First(&role, roleID).Error; err != nil {
- return nil, fmt.Errorf("role not found: %w", err)
- }
- roles[i] = role
- }
- existing.Roles = roles
- }
- if err := r.DB.Save(&existing).Error; err != nil {
- return nil, fmt.Errorf("failed to update user: %w", err)
- }
- logging.LogMutation(ctx, "UPDATE", "USER", existing.Email)
- return convertUser(existing), nil
- }
- // DeleteUser is the resolver for the deleteUser field.
- func (r *mutationResolver) DeleteUser(ctx context.Context, id string) (bool, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return false, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "user:delete") {
- return false, errors.New("unauthorized: missing user:delete permission")
- }
- userID, err := toID(id)
- if err != nil {
- return false, fmt.Errorf("invalid user ID: %w", err)
- }
- result := r.DB.Delete(&models.User{}, userID)
- if result.Error != nil {
- return false, fmt.Errorf("failed to delete user: %w", result.Error)
- }
- logging.LogMutation(ctx, "DELETE", "USER", id)
- return result.RowsAffected > 0, nil
- }
- // CreateNote is the resolver for the createNote field.
- func (r *mutationResolver) CreateNote(ctx context.Context, input model.NewNote) (*model.Note, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- userID, err := toID(input.UserID)
- if err != nil {
- return nil, fmt.Errorf("invalid user ID: %w", err)
- }
- serviceID, err := toID(input.ServiceID)
- if err != nil {
- return nil, fmt.Errorf("invalid service ID: %w", err)
- }
- note := models.Note{
- Title: input.Title,
- Content: input.Content,
- UserID: userID,
- ServiceID: serviceID,
- }
- if err := r.DB.Create(¬e).Error; err != nil {
- return nil, fmt.Errorf("failed to create note: %w", err)
- }
- // Reload with associations for response
- r.DB.Preload("User").Preload("Service").First(¬e, note.ID)
- logging.LogMutation(ctx, "CREATE", "NOTE", note.Title)
- return convertNote(note), nil
- }
- // UpdateNote is the resolver for the updateNote field.
- func (r *mutationResolver) UpdateNote(ctx context.Context, id string, input model.UpdateNoteInput) (*model.Note, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "note:update") {
- return nil, errors.New("unauthorized: missing note:update permission")
- }
- noteID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid note ID: %w", err)
- }
- var existing models.Note
- if err := r.DB.First(&existing, noteID).Error; err != nil {
- return nil, fmt.Errorf("note not found: %w", err)
- }
- if input.Title != nil {
- existing.Title = *input.Title
- }
- if input.Content != nil {
- existing.Content = *input.Content
- }
- if input.UserID != nil {
- userID, err := toID(*input.UserID)
- if err != nil {
- return nil, fmt.Errorf("invalid user ID: %w", err)
- }
- existing.UserID = userID
- }
- if input.ServiceID != nil {
- serviceID, err := toID(*input.ServiceID)
- if err != nil {
- return nil, fmt.Errorf("invalid service ID: %w", err)
- }
- existing.ServiceID = serviceID
- }
- if err := r.DB.Save(&existing).Error; err != nil {
- return nil, fmt.Errorf("failed to update note: %w", err)
- }
- // Reload with associations for response
- r.DB.Preload("User").Preload("Service").First(&existing, existing.ID)
- logging.LogMutation(ctx, "UPDATE", "NOTE", existing.Title)
- return convertNote(existing), nil
- }
- // DeleteNote is the resolver for the deleteNote field.
- func (r *mutationResolver) DeleteNote(ctx context.Context, id string) (bool, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return false, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "note:delete") {
- return false, errors.New("unauthorized: missing note:delete permission")
- }
- noteID, err := toID(id)
- if err != nil {
- return false, fmt.Errorf("invalid note ID: %w", err)
- }
- result := r.DB.Delete(&models.Note{}, noteID)
- if result.Error != nil {
- return false, fmt.Errorf("failed to delete note: %w", result.Error)
- }
- logging.LogMutation(ctx, "DELETE", "NOTE", id)
- return result.RowsAffected > 0, nil
- }
- // CreateRole is the resolver for the createRole field.
- func (r *mutationResolver) CreateRole(ctx context.Context, input model.NewRole) (*model.Role, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- permissions := make([]models.Permission, len(input.Permissions))
- for i, permIDStr := range input.Permissions {
- permID, err := toID(permIDStr)
- if err != nil {
- return nil, fmt.Errorf("invalid permission ID: %w", err)
- }
- var perm models.Permission
- if err := r.DB.First(&perm, permID).Error; err != nil {
- return nil, fmt.Errorf("permission not found: %w", err)
- }
- permissions[i] = perm
- }
- role := models.Role{
- Name: input.Name,
- Description: input.Description,
- Permissions: permissions,
- }
- if err := r.DB.Create(&role).Error; err != nil {
- return nil, fmt.Errorf("failed to create role: %w", err)
- }
- logging.LogMutation(ctx, "CREATE", "ROLE", role.Name)
- return convertRole(role), nil
- }
- // UpdateRole is the resolver for the updateRole field.
- func (r *mutationResolver) UpdateRole(ctx context.Context, id string, input model.UpdateRoleInput) (*model.Role, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "role:update") {
- return nil, errors.New("unauthorized: missing role:update permission")
- }
- roleID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid role ID: %w", err)
- }
- var existing models.Role
- if err := r.DB.First(&existing, roleID).Error; err != nil {
- return nil, fmt.Errorf("role not found: %w", err)
- }
- if input.Name != nil {
- existing.Name = *input.Name
- }
- if input.Description != nil {
- existing.Description = *input.Description
- }
- if len(input.Permissions) > 0 {
- permissions := make([]models.Permission, len(input.Permissions))
- for i, permIDStr := range input.Permissions {
- permID, err := toID(permIDStr)
- if err != nil {
- return nil, fmt.Errorf("invalid permission ID: %w", err)
- }
- var perm models.Permission
- if err := r.DB.First(&perm, permID).Error; err != nil {
- return nil, fmt.Errorf("permission not found: %w", err)
- }
- permissions[i] = perm
- }
- existing.Permissions = permissions
- }
- if err := r.DB.Save(&existing).Error; err != nil {
- return nil, fmt.Errorf("failed to update role: %w", err)
- }
- logging.LogMutation(ctx, "UPDATE", "ROLE", existing.Name)
- return convertRole(existing), nil
- }
- // DeleteRole is the resolver for the deleteRole field.
- func (r *mutationResolver) DeleteRole(ctx context.Context, id string) (bool, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return false, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "role:delete") {
- return false, errors.New("unauthorized: missing role:delete permission")
- }
- roleID, err := toID(id)
- if err != nil {
- return false, fmt.Errorf("invalid role ID: %w", err)
- }
- result := r.DB.Delete(&models.Role{}, roleID)
- if result.Error != nil {
- return false, fmt.Errorf("failed to delete role: %w", result.Error)
- }
- logging.LogMutation(ctx, "DELETE", "ROLE", id)
- return result.RowsAffected > 0, nil
- }
- // CreatePermission is the resolver for the createPermission field.
- func (r *mutationResolver) CreatePermission(ctx context.Context, input model.NewPermission) (*model.Permission, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- permission := models.Permission{
- Code: input.Code,
- Description: input.Description,
- }
- if err := r.DB.Create(&permission).Error; err != nil {
- return nil, fmt.Errorf("failed to create permission: %w", err)
- }
- logging.LogMutation(ctx, "CREATE", "PERMISSION", permission.Code)
- return convertPermission(permission), nil
- }
- // UpdatePermission is the resolver for the updatePermission field.
- func (r *mutationResolver) UpdatePermission(ctx context.Context, id string, input model.UpdatePermissionInput) (*model.Permission, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "permission:update") {
- return nil, errors.New("unauthorized: missing permission:update permission")
- }
- permID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid permission ID: %w", err)
- }
- var existing models.Permission
- if err := r.DB.First(&existing, permID).Error; err != nil {
- return nil, fmt.Errorf("permission not found: %w", err)
- }
- if input.Code != nil {
- existing.Code = *input.Code
- }
- if input.Description != nil {
- existing.Description = *input.Description
- }
- if err := r.DB.Save(&existing).Error; err != nil {
- return nil, fmt.Errorf("failed to update permission: %w", err)
- }
- logging.LogMutation(ctx, "UPDATE", "PERMISSION", existing.Code)
- return convertPermission(existing), nil
- }
- // DeletePermission is the resolver for the deletePermission field.
- func (r *mutationResolver) DeletePermission(ctx context.Context, id string) (bool, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return false, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "permission:delete") {
- return false, errors.New("unauthorized: missing permission:delete permission")
- }
- permID, err := toID(id)
- if err != nil {
- return false, fmt.Errorf("invalid permission ID: %w", err)
- }
- result := r.DB.Delete(&models.Permission{}, permID)
- if result.Error != nil {
- return false, fmt.Errorf("failed to delete permission: %w", result.Error)
- }
- logging.LogMutation(ctx, "DELETE", "PERMISSION", id)
- return result.RowsAffected > 0, nil
- }
- // CreateService is the resolver for the createService field.
- func (r *mutationResolver) CreateService(ctx context.Context, input model.NewService) (*model.Service, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- createdByID, err := toID(input.CreatedByID)
- if err != nil {
- return nil, fmt.Errorf("invalid created by ID: %w", err)
- }
- service := models.Service{
- Name: input.Name,
- CreatedByID: createdByID,
- }
- if input.Description != nil {
- service.Description = *input.Description
- }
- // Add participants
- for _, participantIDStr := range input.Participants {
- participantID, err := toID(participantIDStr)
- if err != nil {
- return nil, fmt.Errorf("invalid participant ID: %w", err)
- }
- var user models.User
- if err := r.DB.First(&user, participantID).Error; err != nil {
- return nil, fmt.Errorf("participant not found: %w", err)
- }
- service.Participants = append(service.Participants, user)
- }
- if err := r.DB.Create(&service).Error; err != nil {
- return nil, fmt.Errorf("failed to create service: %w", err)
- }
- // Reload with associations
- r.DB.Preload("CreatedBy").Preload("Participants").Preload("Tasks").First(&service, service.ID)
- logging.LogMutation(ctx, "CREATE", "SERVICE", service.Name)
- return convertService(service), nil
- }
- // UpdateService is the resolver for the updateService field.
- func (r *mutationResolver) UpdateService(ctx context.Context, id string, input model.UpdateServiceInput) (*model.Service, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "service:update") {
- return nil, errors.New("unauthorized: missing service:update permission")
- }
- serviceID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid service ID: %w", err)
- }
- var existing models.Service
- if err := r.DB.Preload("Participants").First(&existing, serviceID).Error; err != nil {
- return nil, fmt.Errorf("service not found: %w", err)
- }
- if input.Name != nil {
- existing.Name = *input.Name
- }
- if input.Description != nil {
- existing.Description = *input.Description
- }
- if len(input.Participants) > 0 {
- participants := []models.User{}
- for _, participantIDStr := range input.Participants {
- participantID, err := toID(participantIDStr)
- if err != nil {
- return nil, fmt.Errorf("invalid participant ID: %w", err)
- }
- var user models.User
- if err := r.DB.First(&user, participantID).Error; err != nil {
- return nil, fmt.Errorf("participant not found: %w", err)
- }
- participants = append(participants, user)
- }
- existing.Participants = participants
- }
- if err := r.DB.Save(&existing).Error; err != nil {
- return nil, fmt.Errorf("failed to update service: %w", err)
- }
- // Reload with associations for response
- r.DB.Preload("CreatedBy").Preload("Participants").Preload("Tasks").First(&existing, existing.ID)
- logging.LogMutation(ctx, "UPDATE", "SERVICE", existing.Name)
- return convertService(existing), nil
- }
- // DeleteService is the resolver for the deleteService field.
- func (r *mutationResolver) DeleteService(ctx context.Context, id string) (bool, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return false, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "service:delete") {
- return false, errors.New("unauthorized: missing service:delete permission")
- }
- serviceID, err := toID(id)
- if err != nil {
- return false, fmt.Errorf("invalid service ID: %w", err)
- }
- result := r.DB.Delete(&models.Service{}, serviceID)
- if result.Error != nil {
- return false, fmt.Errorf("failed to delete service: %w", result.Error)
- }
- logging.LogMutation(ctx, "DELETE", "SERVICE", id)
- return result.RowsAffected > 0, nil
- }
- // CreateTask is the resolver for the createTask field.
- func (r *mutationResolver) CreateTask(ctx context.Context, input model.NewTask) (*model.Task, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- createdByID, err := toID(input.CreatedByID)
- if err != nil {
- return nil, fmt.Errorf("invalid created by ID: %w", err)
- }
- task := models.Task{
- Title: input.Title,
- Content: input.Content,
- CreatedByID: createdByID,
- UpdatedByID: createdByID, // Creator is initial updater
- Priority: input.Priority,
- }
- if input.AssigneeID != nil {
- assigneeID, err := toID(*input.AssigneeID)
- if err != nil {
- return nil, fmt.Errorf("invalid assignee ID: %w", err)
- }
- task.AssigneeID = &assigneeID
- }
- if input.StatusID != nil {
- statusID, err := toID(*input.StatusID)
- if err != nil {
- return nil, fmt.Errorf("invalid status ID: %w", err)
- }
- task.StatusID = statusID
- }
- if input.DueDate != nil {
- parsedTime, parseErr := time.Parse(time.RFC3339, *input.DueDate)
- if parseErr != nil {
- return nil, fmt.Errorf("invalid due date format: %w", parseErr)
- }
- task.DueDate = &parsedTime
- }
- if err := r.DB.Create(&task).Error; err != nil {
- return nil, fmt.Errorf("failed to create task: %w", err)
- }
- // Reload with associations
- r.DB.Preload("CreatedBy").Preload("Assignee").Preload("Status").First(&task, task.ID)
- // Publish task created event to assignee
- graphqlTask := convertTask(task)
- r.PublishTaskEvent(graphqlTask, task.AssigneeID, "created")
- logging.LogMutation(ctx, "CREATE", "TASK", task.Title)
- return graphqlTask, nil
- }
- // UpdateTask is the resolver for the updateTask field.
- func (r *mutationResolver) UpdateTask(ctx context.Context, id string, input model.UpdateTaskInput) (*model.Task, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "task:update") {
- return nil, errors.New("unauthorized: missing task:update permission")
- }
- // Get current user for UpdatedBy
- currentUser, err := auth.CurrentUser(ctx)
- if err != nil {
- return nil, fmt.Errorf("failed to get current user: %w", err)
- }
- taskID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid task ID: %w", err)
- }
- var existing models.Task
- if err := r.DB.Preload("CreatedBy").Preload("Assignee").Preload("Status").First(&existing, taskID).Error; err != nil {
- return nil, fmt.Errorf("task not found: %w", err)
- }
- if input.Title != nil {
- existing.Title = *input.Title
- }
- if input.Content != nil {
- existing.Content = *input.Content
- }
- if input.AssigneeID != nil {
- if *input.AssigneeID == "" {
- existing.AssigneeID = nil
- } else {
- assigneeID, err := toID(*input.AssigneeID)
- if err != nil {
- return nil, fmt.Errorf("invalid assignee ID: %w", err)
- }
- existing.AssigneeID = &assigneeID
- }
- }
- if input.StatusID != nil {
- if *input.StatusID == "" {
- existing.StatusID = 0
- } else {
- statusID, err := toID(*input.StatusID)
- if err != nil {
- return nil, fmt.Errorf("invalid status ID: %w", err)
- }
- existing.StatusID = statusID
- }
- }
- if input.DueDate != nil {
- if *input.DueDate == "" {
- existing.DueDate = nil
- } else {
- parsedTime, parseErr := time.Parse(time.RFC3339, *input.DueDate)
- if parseErr != nil {
- return nil, fmt.Errorf("invalid due date format: %w", parseErr)
- }
- existing.DueDate = &parsedTime
- }
- }
- if input.Priority != nil {
- existing.Priority = *input.Priority
- }
- // Set UpdatedByID to current user
- existing.UpdatedByID = currentUser.ID
- if err := r.DB.Save(&existing).Error; err != nil {
- return nil, fmt.Errorf("failed to update task: %w", err)
- }
- // Reload with associations for response
- r.DB.Preload("CreatedBy").Preload("UpdatedBy").Preload("Assignee").Preload("Status").First(&existing, existing.ID)
- // Publish task updated event to assignee
- graphqlTask := convertTask(existing)
- r.PublishTaskEvent(graphqlTask, existing.AssigneeID, "updated")
- // Workflow integration: Check if task is associated with a workflow node
- // Look up the workflow node by task ID
- var workflowNode models.WorkflowNode
- if err := r.DB.Where("task_id = ?", existing.ID).First(&workflowNode).Error; err == nil && workflowNode.ID > 0 {
- // Get the workflow engine
- workflowEngine := workflow.NewEngine(r.DB)
- // Check if status changed to "done" (status code "done")
- if input.StatusID != nil && *input.StatusID != "" {
- var newStatus models.TaskStatus
- if err := r.DB.First(&newStatus, existing.StatusID).Error; err == nil && newStatus.Code == "done" {
- // Mark node as completed
- if err := workflowEngine.MarkNodeCompleted(workflowNode.ID); err != nil {
- fmt.Printf("ERROR: workflow_node_complete node_id=%d error=%v\n", workflowNode.ID, err)
- }
- } else if input.StatusID != nil && *input.StatusID != "" {
- // Check for cancelled/failed status
- if err := r.DB.First(&newStatus, existing.StatusID).Error; err == nil {
- if newStatus.Code == "cancelled" || newStatus.Code == "failed" {
- // Mark node as failed
- if err := workflowEngine.MarkNodeFailed(workflowNode.ID, fmt.Sprintf("task status changed to %s", newStatus.Code)); err != nil {
- fmt.Printf("ERROR: workflow_node_fail node_id=%d error=%v\n", workflowNode.ID, err)
- }
- }
- }
- }
- }
- }
- logging.LogMutation(ctx, "UPDATE", "TASK", existing.Title)
- return graphqlTask, nil
- }
- // DeleteTask is the resolver for the deleteTask field.
- func (r *mutationResolver) DeleteTask(ctx context.Context, id string) (bool, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return false, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "task:delete") {
- return false, errors.New("unauthorized: missing task:delete permission")
- }
- taskID, err := toID(id)
- if err != nil {
- return false, fmt.Errorf("invalid task ID: %w", err)
- }
- result := r.DB.Delete(&models.Task{}, taskID)
- if result.Error != nil {
- return false, fmt.Errorf("failed to delete task: %w", result.Error)
- }
- logging.LogMutation(ctx, "DELETE", "TASK", id)
- return result.RowsAffected > 0, nil
- }
- // CreateTaskStatus is the resolver for the createTaskStatus field.
- func (r *mutationResolver) CreateTaskStatus(ctx context.Context, input model.NewTaskStatus) (*model.TaskStatus, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- taskStatus := models.TaskStatus{
- Code: input.Code,
- Label: input.Label,
- }
- if err := r.DB.Create(&taskStatus).Error; err != nil {
- return nil, fmt.Errorf("failed to create task status: %w", err)
- }
- logging.LogMutation(ctx, "CREATE", "TASKSTATUS", taskStatus.Code)
- return convertTaskStatus(taskStatus), nil
- }
- // UpdateTaskStatus is the resolver for the updateTaskStatus field.
- func (r *mutationResolver) UpdateTaskStatus(ctx context.Context, id string, input model.UpdateTaskStatusInput) (*model.TaskStatus, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "taskstatus:update") {
- return nil, errors.New("unauthorized: missing taskstatus:update permission")
- }
- statusID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid task status ID: %w", err)
- }
- var existing models.TaskStatus
- if err := r.DB.First(&existing, statusID).Error; err != nil {
- return nil, fmt.Errorf("task status not found: %w", err)
- }
- if input.Code != nil {
- existing.Code = *input.Code
- }
- if input.Label != nil {
- existing.Label = *input.Label
- }
- if err := r.DB.Save(&existing).Error; err != nil {
- return nil, fmt.Errorf("failed to update task status: %w", err)
- }
- // Reload with tasks for response
- r.DB.Preload("Tasks").First(&existing, existing.ID)
- logging.LogMutation(ctx, "UPDATE", "TASKSTATUS", existing.Code)
- return convertTaskStatus(existing), nil
- }
- // DeleteTaskStatus is the resolver for the deleteTaskStatus field.
- func (r *mutationResolver) DeleteTaskStatus(ctx context.Context, id string) (bool, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return false, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "taskstatus:delete") {
- return false, errors.New("unauthorized: missing taskstatus:delete permission")
- }
- statusID, err := toID(id)
- if err != nil {
- return false, fmt.Errorf("invalid task status ID: %w", err)
- }
- result := r.DB.Delete(&models.TaskStatus{}, statusID)
- if result.Error != nil {
- return false, fmt.Errorf("failed to delete task status: %w", result.Error)
- }
- logging.LogMutation(ctx, "DELETE", "TASKSTATUS", id)
- return result.RowsAffected > 0, nil
- }
- // CreateMessage is the resolver for the createMessage field.
- func (r *mutationResolver) CreateMessage(ctx context.Context, input model.NewMessage) (*model.Message, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- // Get sender from authenticated user
- currentUser, err := auth.CurrentUser(ctx)
- if err != nil {
- return nil, fmt.Errorf("failed to get current user: %w", err)
- }
- // Build receivers list
- receivers := make([]models.User, 0, len(input.Receivers))
- receiverIDs := make([]uint, 0, len(input.Receivers))
- for _, receiverIDStr := range input.Receivers {
- receiverID, err := toID(receiverIDStr)
- if err != nil {
- return nil, fmt.Errorf("invalid receiver ID: %w", err)
- }
- var user models.User
- if err := r.DB.First(&user, receiverID).Error; err != nil {
- return nil, fmt.Errorf("receiver not found: %w", err)
- }
- receivers = append(receivers, user)
- receiverIDs = append(receiverIDs, receiverID)
- }
- message := models.Message{
- SenderID: currentUser.ID,
- Content: input.Content,
- Receivers: receivers,
- }
- if err := r.DB.Create(&message).Error; err != nil {
- return nil, fmt.Errorf("failed to create message: %w", err)
- }
- // Reload with associations
- r.DB.Preload("Sender").Preload("Receivers").First(&message, message.ID)
- // Publish message event to receivers (excluding the sender to prevent notification loops)
- notifyReceiverIDs := make([]uint, 0, len(receiverIDs))
- for _, receiverID := range receiverIDs {
- if receiverID != currentUser.ID {
- notifyReceiverIDs = append(notifyReceiverIDs, receiverID)
- }
- }
- graphqlMessage := convertMessage(message)
- r.PublishMessageEvent(graphqlMessage, notifyReceiverIDs)
- logging.LogMutation(ctx, "CREATE", "MESSAGE", fmt.Sprintf("id=%d content=%s", message.ID, message.Content))
- return graphqlMessage, nil
- }
- // UpdateMessage is the resolver for the updateMessage field.
- func (r *mutationResolver) UpdateMessage(ctx context.Context, id string, input model.UpdateMessageInput) (*model.Message, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "message:update") {
- return nil, errors.New("unauthorized: missing message:update permission")
- }
- messageID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid message ID: %w", err)
- }
- var existing models.Message
- if err := r.DB.Preload("Sender").Preload("Receivers").First(&existing, messageID).Error; err != nil {
- return nil, fmt.Errorf("message not found: %w", err)
- }
- if input.Content != nil {
- existing.Content = *input.Content
- }
- if len(input.Receivers) > 0 {
- receivers := make([]models.User, 0, len(input.Receivers))
- for _, receiverIDStr := range input.Receivers {
- receiverID, err := toID(receiverIDStr)
- if err != nil {
- return nil, fmt.Errorf("invalid receiver ID: %w", err)
- }
- var user models.User
- if err := r.DB.First(&user, receiverID).Error; err != nil {
- return nil, fmt.Errorf("receiver not found: %w", err)
- }
- receivers = append(receivers, user)
- }
- existing.Receivers = receivers
- }
- if err := r.DB.Save(&existing).Error; err != nil {
- return nil, fmt.Errorf("failed to update message: %w", err)
- }
- // Reload with associations
- r.DB.Preload("Sender").Preload("Receivers").First(&existing, existing.ID)
- logging.LogMutation(ctx, "UPDATE", "MESSAGE", id)
- return convertMessage(existing), nil
- }
- // DeleteMessage is the resolver for the deleteMessage field.
- func (r *mutationResolver) DeleteMessage(ctx context.Context, id string) (bool, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return false, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "message:delete") {
- return false, errors.New("unauthorized: missing message:delete permission")
- }
- messageID, err := toID(id)
- if err != nil {
- return false, fmt.Errorf("invalid message ID: %w", err)
- }
- result := r.DB.Delete(&models.Message{}, messageID)
- if result.Error != nil {
- return false, fmt.Errorf("failed to delete message: %w", result.Error)
- }
- logging.LogMutation(ctx, "DELETE", "MESSAGE", id)
- return result.RowsAffected > 0, nil
- }
- // CreateWorkflowTemplate is the resolver for the createWorkflowTemplate field.
- func (r *mutationResolver) CreateWorkflowTemplate(ctx context.Context, input model.NewWorkflowTemplate) (*model.WorkflowTemplate, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "workflow:create") {
- return nil, errors.New("unauthorized: missing workflow:create permission")
- }
- currentUser, err := auth.CurrentUser(ctx)
- if err != nil {
- return nil, fmt.Errorf("failed to get current user: %w", err)
- }
- var isActive bool
- if input.IsActive != nil {
- isActive = *input.IsActive
- } else {
- isActive = true
- }
- workflowTemplate := models.WorkflowTemplate{
- Name: input.Name,
- Description: func() string {
- if input.Description != nil {
- return *input.Description
- }
- return ""
- }(),
- Definition: input.Definition,
- IsActive: isActive,
- CreatedByID: currentUser.ID,
- }
- if err := r.DB.Create(&workflowTemplate).Error; err != nil {
- return nil, fmt.Errorf("failed to create workflow template: %w", err)
- }
- // Reload with associations
- r.DB.Preload("CreatedBy").First(&workflowTemplate, workflowTemplate.ID)
- logging.LogMutation(ctx, "CREATE", "WORKFLOW_TEMPLATE", workflowTemplate.Name)
- return convertWorkflowTemplate(workflowTemplate), nil
- }
- // UpdateWorkflowTemplate is the resolver for the updateWorkflowTemplate field.
- func (r *mutationResolver) UpdateWorkflowTemplate(ctx context.Context, id string, input model.UpdateWorkflowTemplateInput) (*model.WorkflowTemplate, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "workflow:manage") {
- return nil, errors.New("unauthorized: missing workflow:manage permission")
- }
- templateID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid workflow template ID: %w", err)
- }
- var existing models.WorkflowTemplate
- if err := r.DB.First(&existing, templateID).Error; err != nil {
- return nil, fmt.Errorf("workflow template not found: %w", err)
- }
- if input.Name != nil {
- existing.Name = *input.Name
- }
- if input.Description != nil {
- existing.Description = *input.Description
- }
- if input.Definition != nil {
- existing.Definition = *input.Definition
- }
- if input.IsActive != nil {
- existing.IsActive = *input.IsActive
- }
- if err := r.DB.Save(&existing).Error; err != nil {
- return nil, fmt.Errorf("failed to update workflow template: %w", err)
- }
- // Reload with associations for response
- r.DB.Preload("CreatedBy").First(&existing, existing.ID)
- logging.LogMutation(ctx, "UPDATE", "WORKFLOW_TEMPLATE", existing.Name)
- return convertWorkflowTemplate(existing), nil
- }
- // DeleteWorkflowTemplate is the resolver for the deleteWorkflowTemplate field.
- func (r *mutationResolver) DeleteWorkflowTemplate(ctx context.Context, id string) (bool, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return false, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "workflow:manage") {
- return false, errors.New("unauthorized: missing workflow:manage permission")
- }
- templateID, err := toID(id)
- if err != nil {
- return false, fmt.Errorf("invalid workflow template ID: %w", err)
- }
- result := r.DB.Delete(&models.WorkflowTemplate{}, templateID)
- if result.Error != nil {
- return false, fmt.Errorf("failed to delete workflow template: %w", result.Error)
- }
- logging.LogMutation(ctx, "DELETE", "WORKFLOW_TEMPLATE", id)
- return result.RowsAffected > 0, nil
- }
- // StartWorkflow is the resolver for the startWorkflow field.
- func (r *mutationResolver) StartWorkflow(ctx context.Context, templateID string, input model.StartWorkflowInput) (*model.WorkflowInstance, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "workflow:start") {
- return nil, errors.New("unauthorized: missing workflow:start permission")
- }
- templateIDUint, err := toID(templateID)
- if err != nil {
- return nil, fmt.Errorf("invalid workflow template ID: %w", err)
- }
- var template models.WorkflowTemplate
- if err := r.DB.First(&template, templateIDUint).Error; err != nil {
- return nil, fmt.Errorf("workflow template not found: %w", err)
- }
- // Parse the workflow definition (JSON DAG)
- workflowEngine := workflow.NewEngine(r.DB)
- instance, nodes, err := workflowEngine.CreateInstance(template, workflow.StartWorkflowInput{
- ServiceID: func() *uint {
- if input.ServiceID != nil {
- id, _ := toID(*input.ServiceID)
- return &id
- }
- return nil
- }(),
- Context: func() string {
- if input.Context != nil {
- return *input.Context
- }
- return ""
- }(),
- })
- if err != nil {
- return nil, fmt.Errorf("failed to create workflow instance: %w", err)
- }
- // Save the workflow instance
- if err := r.DB.Create(&instance).Error; err != nil {
- return nil, fmt.Errorf("failed to save workflow instance: %w", err)
- }
- // Save the workflow nodes
- for i := range nodes {
- nodes[i].WorkflowInstanceID = instance.ID
- if err := r.DB.Create(&nodes[i]).Error; err != nil {
- return nil, fmt.Errorf("failed to save workflow node: %w", err)
- }
- }
- // Reload with associations including nested createdBy
- r.DB.Preload("WorkflowTemplate.CreatedBy").Preload("Service").First(&instance, instance.ID)
- logging.LogMutation(ctx, "START_WORKFLOW", "WORKFLOW_INSTANCE", fmt.Sprintf("template=%s", template.Name))
- return convertWorkflowInstance(instance), nil
- }
- // CancelWorkflow is the resolver for the cancelWorkflow field.
- func (r *mutationResolver) CancelWorkflow(ctx context.Context, id string) (*model.WorkflowInstance, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "workflow:manage") {
- return nil, errors.New("unauthorized: missing workflow:manage permission")
- }
- instanceID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid workflow instance ID: %w", err)
- }
- var instance models.WorkflowInstance
- if err := r.DB.First(&instance, instanceID).Error; err != nil {
- return nil, fmt.Errorf("workflow instance not found: %w", err)
- }
- instance.Status = "failed"
- now := time.Now()
- instance.CompletedAt = &now
- if err := r.DB.Save(&instance).Error; err != nil {
- return nil, fmt.Errorf("failed to cancel workflow: %w", err)
- }
- // Update all running nodes to failed
- r.DB.Model(&models.WorkflowNode{}).
- Where("workflow_instance_id = ? AND status = ?", instanceID, "running").
- Update("status", "failed")
- // Reload with associations for response
- r.DB.Preload("WorkflowTemplate").Preload("Service").First(&instance, instance.ID)
- logging.LogMutation(ctx, "CANCEL_WORKFLOW", "WORKFLOW_INSTANCE", id)
- return convertWorkflowInstance(instance), nil
- }
- // RetryWorkflowNode is the resolver for the retryWorkflowNode field.
- func (r *mutationResolver) RetryWorkflowNode(ctx context.Context, nodeID string) (*model.WorkflowNode, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "workflow:intervene") {
- return nil, errors.New("unauthorized: missing workflow:intervene permission")
- }
- nodeIDUint, err := toID(nodeID)
- if err != nil {
- return nil, fmt.Errorf("invalid workflow node ID: %w", err)
- }
- var node models.WorkflowNode
- if err := r.DB.Preload("WorkflowInstance").First(&node, nodeIDUint).Error; err != nil {
- return nil, fmt.Errorf("workflow node not found: %w", err)
- }
- // Reset node status to pending and clear task association
- node.Status = "pending"
- node.TaskID = nil
- node.RetryCount++
- node.OutputData = ""
- if err := r.DB.Save(&node).Error; err != nil {
- return nil, fmt.Errorf("failed to retry workflow node: %w", err)
- }
- // Reload with task for response
- r.DB.Preload("Task").First(&node, node.ID)
- logging.LogMutation(ctx, "RETRY_NODE", "WORKFLOW_NODE", nodeID)
- return convertWorkflowNode(node), nil
- }
- // Users is the resolver for the users field.
- func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- var users []models.User
- if err := r.DB.Preload("Roles").Find(&users).Error; err != nil {
- return nil, fmt.Errorf("failed to fetch users: %w", err)
- }
- logging.LogQuery(ctx, "USERS", "all")
- return convertUsers(users), nil
- }
- // User is the resolver for the user field.
- func (r *queryResolver) User(ctx context.Context, id string) (*model.User, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- userID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid user ID: %w", err)
- }
- var user models.User
- if err := r.DB.Preload("Roles.Permissions").First(&user, userID).Error; err != nil {
- return nil, fmt.Errorf("user not found: %w", err)
- }
- logging.LogQuery(ctx, "USER", id)
- return convertUser(user), nil
- }
- // Notes is the resolver for the notes field.
- func (r *queryResolver) Notes(ctx context.Context) ([]*model.Note, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- var notes []models.Note
- if err := r.DB.Preload("User").Preload("Service").Find(¬es).Error; err != nil {
- return nil, fmt.Errorf("failed to fetch notes: %w", err)
- }
- logging.LogQuery(ctx, "NOTES", "all")
- return convertNotes(notes), nil
- }
- // Note is the resolver for the note field.
- func (r *queryResolver) Note(ctx context.Context, id string) (*model.Note, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- noteID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid note ID: %w", err)
- }
- var note models.Note
- if err := r.DB.Preload("User").Preload("Service").First(¬e, noteID).Error; err != nil {
- return nil, fmt.Errorf("note not found: %w", err)
- }
- logging.LogQuery(ctx, "NOTE", id)
- return convertNote(note), nil
- }
- // Roles is the resolver for the roles field.
- func (r *queryResolver) Roles(ctx context.Context) ([]*model.Role, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- var roles []models.Role
- if err := r.DB.Preload("Permissions").Find(&roles).Error; err != nil {
- return nil, fmt.Errorf("failed to fetch roles: %w", err)
- }
- logging.LogQuery(ctx, "ROLES", "all")
- return convertRoles(roles), nil
- }
- // Role is the resolver for the role field.
- func (r *queryResolver) Role(ctx context.Context, id string) (*model.Role, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- roleID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid role ID: %w", err)
- }
- var role models.Role
- if err := r.DB.Preload("Permissions").First(&role, roleID).Error; err != nil {
- return nil, fmt.Errorf("role not found: %w", err)
- }
- logging.LogQuery(ctx, "ROLE", id)
- return convertRole(role), nil
- }
- // Permissions is the resolver for the permissions field.
- func (r *queryResolver) Permissions(ctx context.Context) ([]*model.Permission, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- var perms []models.Permission
- if err := r.DB.Find(&perms).Error; err != nil {
- return nil, fmt.Errorf("failed to fetch permissions: %w", err)
- }
- logging.LogQuery(ctx, "PERMISSIONS", "all")
- return convertPermissions(perms), nil
- }
- // Permission is the resolver for the permission field.
- func (r *queryResolver) Permission(ctx context.Context, id string) (*model.Permission, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- permID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid permission ID: %w", err)
- }
- var perm models.Permission
- if err := r.DB.First(&perm, permID).Error; err != nil {
- return nil, fmt.Errorf("permission not found: %w", err)
- }
- logging.LogQuery(ctx, "PERMISSION", id)
- return convertPermission(perm), nil
- }
- // Services is the resolver for the services field.
- func (r *queryResolver) Services(ctx context.Context) ([]*model.Service, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- var services []models.Service
- if err := r.DB.Preload("CreatedBy").Preload("Participants").Preload("Tasks").Find(&services).Error; err != nil {
- return nil, fmt.Errorf("failed to fetch services: %w", err)
- }
- logging.LogQuery(ctx, "SERVICES", "all")
- return convertServices(services), nil
- }
- // Service is the resolver for the service field.
- func (r *queryResolver) Service(ctx context.Context, id string) (*model.Service, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- serviceID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid service ID: %w", err)
- }
- var service models.Service
- if err := r.DB.Preload("CreatedBy").Preload("Participants").Preload("Tasks").First(&service, serviceID).Error; err != nil {
- return nil, fmt.Errorf("service not found: %w", err)
- }
- logging.LogQuery(ctx, "SERVICE", id)
- return convertService(service), nil
- }
- // Tasks is the resolver for the tasks field.
- func (r *queryResolver) Tasks(ctx context.Context) ([]*model.Task, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- var tasks []models.Task
- if err := r.DB.Preload("CreatedBy").Preload("UpdatedBy").Preload("Assignee").Preload("Status").Find(&tasks).Error; err != nil {
- return nil, fmt.Errorf("failed to fetch tasks: %w", err)
- }
- logging.LogQuery(ctx, "TASKS", "all")
- return convertTasks(tasks), nil
- }
- // Task is the resolver for the task field.
- func (r *queryResolver) Task(ctx context.Context, id string) (*model.Task, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- taskID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid task ID: %w", err)
- }
- var task models.Task
- if err := r.DB.Preload("CreatedBy").Preload("UpdatedBy").Preload("Assignee").Preload("Status").First(&task, taskID).Error; err != nil {
- return nil, fmt.Errorf("task not found: %w", err)
- }
- logging.LogQuery(ctx, "TASK", id)
- return convertTask(task), nil
- }
- // TaskStatuses is the resolver for the taskStatuses field.
- func (r *queryResolver) TaskStatuses(ctx context.Context) ([]*model.TaskStatus, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- var statuses []models.TaskStatus
- if err := r.DB.Preload("Tasks").Find(&statuses).Error; err != nil {
- return nil, fmt.Errorf("failed to fetch task statuses: %w", err)
- }
- logging.LogQuery(ctx, "TASKSTATUSES", "all")
- return convertTaskStatuses(statuses), nil
- }
- // TaskStatus
- func (r *queryResolver) TaskStatus(ctx context.Context, id string) (*model.TaskStatus, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- statusID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid task status ID: %w", err)
- }
- var status models.TaskStatus
- if err := r.DB.Preload("Tasks").First(&status, statusID).Error; err != nil {
- return nil, fmt.Errorf("task status not found: %w", err)
- }
- logging.LogQuery(ctx, "TASKSTATUS", id)
- return convertTaskStatus(status), nil
- }
- // Messages is the resolver for the messages field.
- func (r *queryResolver) Messages(ctx context.Context) ([]*model.Message, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- var messages []models.Message
- if err := r.DB.Preload("Sender").Preload("Receivers").Find(&messages).Error; err != nil {
- return nil, fmt.Errorf("failed to fetch messages: %w", err)
- }
- logging.LogQuery(ctx, "MESSAGES", "all")
- return convertMessages(messages), nil
- }
- // Message is the resolver for the message field.
- func (r *queryResolver) Message(ctx context.Context, id string) (*model.Message, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- messageID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid message ID: %w", err)
- }
- var message models.Message
- if err := r.DB.Preload("Sender").Preload("Receivers").First(&message, messageID).Error; err != nil {
- return nil, fmt.Errorf("message not found: %w", err)
- }
- logging.LogQuery(ctx, "MESSAGE", id)
- return convertMessage(message), nil
- }
- // WorkflowTemplates is the resolver for the workflowTemplates field.
- func (r *queryResolver) WorkflowTemplates(ctx context.Context) ([]*model.WorkflowTemplate, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "workflow:view") {
- return nil, errors.New("unauthorized: missing workflow:view permission")
- }
- var templates []models.WorkflowTemplate
- if err := r.DB.Preload("CreatedBy").Find(&templates).Error; err != nil {
- return nil, fmt.Errorf("failed to fetch workflow templates: %w", err)
- }
- logging.LogQuery(ctx, "WORKFLOW_TEMPLATES", "all")
- return convertWorkflowTemplates(templates), nil
- }
- // WorkflowTemplate is the resolver for the workflowTemplate field.
- func (r *queryResolver) WorkflowTemplate(ctx context.Context, id string) (*model.WorkflowTemplate, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "workflow:view") {
- return nil, errors.New("unauthorized: missing workflow:view permission")
- }
- templateID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid workflow template ID: %w", err)
- }
- var template models.WorkflowTemplate
- if err := r.DB.Preload("CreatedBy").First(&template, templateID).Error; err != nil {
- return nil, fmt.Errorf("workflow template not found: %w", err)
- }
- logging.LogQuery(ctx, "WORKFLOW_TEMPLATE", id)
- return convertWorkflowTemplate(template), nil
- }
- // WorkflowInstances is the resolver for the workflowInstances field.
- func (r *queryResolver) WorkflowInstances(ctx context.Context) ([]*model.WorkflowInstance, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "workflow:view") {
- return nil, errors.New("unauthorized: missing workflow:view permission")
- }
- var instances []models.WorkflowInstance
- if err := r.DB.Preload("WorkflowTemplate").Preload("Service").Find(&instances).Error; err != nil {
- return nil, fmt.Errorf("failed to fetch workflow instances: %w", err)
- }
- logging.LogQuery(ctx, "WORKFLOW_INSTANCES", "all")
- return convertWorkflowInstances(instances), nil
- }
- // WorkflowInstance is the resolver for the workflowInstance field.
- func (r *queryResolver) WorkflowInstance(ctx context.Context, id string) (*model.WorkflowInstance, error) {
- // Auth check
- if !auth.IsAuthenticated(ctx) {
- return nil, errors.New("unauthorized: authentication required")
- }
- if !auth.HasPermission(ctx, "workflow:view") {
- return nil, errors.New("unauthorized: missing workflow:view permission")
- }
- instanceID, err := toID(id)
- if err != nil {
- return nil, fmt.Errorf("invalid workflow instance ID: %w", err)
- }
- var instance models.WorkflowInstance
- if err := r.DB.Preload("WorkflowTemplate").Preload("Service").First(&instance, instanceID).Error; err != nil {
- return nil, fmt.Errorf("workflow instance not found: %w", err)
- }
- logging.LogQuery(ctx, "WORKFLOW_INSTANCE", id)
- return convertWorkflowInstance(instance), nil
- }
- // TaskCreated is the resolver for the taskCreated field.
- // Users only receive events for tasks where they are the assignee.
- func (r *subscriptionResolver) TaskCreated(ctx context.Context) (<-chan *model.Task, error) {
- // Get current user
- user, err := auth.CurrentUser(ctx)
- if err != nil {
- return nil, errors.New("unauthorized: authentication required")
- }
- // Subscribe to task events
- eventChan := r.SubscribeToTasks(user.ID)
- // Create output channel
- outputChan := make(chan *model.Task, 10)
- // Start goroutine to filter and forward events
- go func() {
- defer close(outputChan)
- for {
- select {
- case <-ctx.Done():
- return
- case event, ok := <-eventChan:
- if !ok {
- return
- }
- // Only forward "created" events
- if event.EventType == "created" && event.Task != nil {
- select {
- case outputChan <- event.Task:
- default:
- // Channel full, skip
- }
- }
- }
- }
- }()
- return outputChan, nil
- }
- // TaskUpdated is the resolver for the taskUpdated field.
- // Users only receive events for tasks where they are the assignee.
- func (r *subscriptionResolver) TaskUpdated(ctx context.Context) (<-chan *model.Task, error) {
- // Get current user
- user, err := auth.CurrentUser(ctx)
- if err != nil {
- return nil, errors.New("unauthorized: authentication required")
- }
- // Subscribe to task events
- eventChan := r.SubscribeToTasks(user.ID)
- // Create output channel
- outputChan := make(chan *model.Task, 10)
- // Start goroutine to filter and forward events
- go func() {
- defer close(outputChan)
- for {
- select {
- case <-ctx.Done():
- return
- case event, ok := <-eventChan:
- if !ok {
- return
- }
- // Only forward "updated" events
- if event.EventType == "updated" && event.Task != nil {
- select {
- case outputChan <- event.Task:
- default:
- // Channel full, skip
- }
- }
- }
- }
- }()
- return outputChan, nil
- }
- // TaskDeleted is the resolver for the taskDeleted field.
- // Users only receive events for tasks where they are the assignee.
- func (r *subscriptionResolver) TaskDeleted(ctx context.Context) (<-chan *model.Task, error) {
- // Get current user
- user, err := auth.CurrentUser(ctx)
- if err != nil {
- return nil, errors.New("unauthorized: authentication required")
- }
- // Subscribe to task events
- eventChan := r.SubscribeToTasks(user.ID)
- // Create output channel
- outputChan := make(chan *model.Task, 10)
- // Start goroutine to filter and forward events
- go func() {
- defer close(outputChan)
- for {
- select {
- case <-ctx.Done():
- return
- case event, ok := <-eventChan:
- if !ok {
- return
- }
- // Only forward "deleted" events
- if event.EventType == "deleted" && event.Task != nil {
- select {
- case outputChan <- event.Task:
- default:
- // Channel full, skip
- }
- }
- }
- }
- }()
- return outputChan, nil
- }
- // MessageAdded is the resolver for the messageAdded field.
- // Users only receive events for messages where they are in the receivers list.
- func (r *subscriptionResolver) MessageAdded(ctx context.Context) (<-chan *model.Message, error) {
- // Get current user
- user, err := auth.CurrentUser(ctx)
- if err != nil {
- return nil, errors.New("unauthorized: authentication required")
- }
- // Subscribe to message events
- eventChan := r.SubscribeToMessages(user.ID)
- // Create output channel
- outputChan := make(chan *model.Message, 10)
- // Start goroutine to filter and forward events
- go func() {
- defer close(outputChan)
- for {
- select {
- case <-ctx.Done():
- return
- case event, ok := <-eventChan:
- if !ok {
- return
- }
- // Check if user is in the receiver list
- isReceiver := false
- for _, receiverID := range event.ReceiverIDs {
- if receiverID == user.ID {
- isReceiver = true
- break
- }
- }
- if isReceiver && event.Message != nil {
- select {
- case outputChan <- event.Message:
- default:
- // Channel full, skip
- }
- }
- }
- }
- }()
- return outputChan, nil
- }
- // Mutation returns MutationResolver implementation.
- func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} }
- // Query returns QueryResolver implementation.
- func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
- // Subscription returns SubscriptionResolver implementation.
- func (r *Resolver) Subscription() SubscriptionResolver { return &subscriptionResolver{r} }
- type mutationResolver struct{ *Resolver }
- type queryResolver struct{ *Resolver }
- type subscriptionResolver struct{ *Resolver }
|