stufflog graphql server
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

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. package resolvers
  2. // This file will be automatically regenerated based on the schema, any resolver implementations
  3. // will be copied through when generating and any unknown code will be moved to the end.
  4. import (
  5. "context"
  6. "errors"
  7. "git.aiterp.net/stufflog/server/graph/loaders"
  8. "log"
  9. "sort"
  10. "time"
  11. "git.aiterp.net/stufflog/server/graph/graphcore"
  12. "git.aiterp.net/stufflog/server/internal/generate"
  13. "git.aiterp.net/stufflog/server/internal/slerrors"
  14. "git.aiterp.net/stufflog/server/models"
  15. )
  16. func (r *mutationResolver) CreateProject(ctx context.Context, input graphcore.ProjectCreateInput) (*models.Project, error) {
  17. user := r.Auth.UserFromContext(ctx)
  18. if user == nil {
  19. return nil, slerrors.PermissionDenied
  20. }
  21. project := &models.Project{
  22. ID: input.ID,
  23. Name: input.Name,
  24. Description: input.Description,
  25. DailyPoints: input.DailyPoints,
  26. }
  27. if !project.ValidKey() {
  28. return nil, errors.New("the project must have a valid ID (only uppercase allowed)")
  29. }
  30. if project.Name == "" || project.Description == "" {
  31. return nil, errors.New("name and description cannot be left empty")
  32. }
  33. project, err := r.Database.Projects().Insert(ctx, *project)
  34. if err != nil {
  35. return nil, err
  36. }
  37. err = r.Database.Projects().SetPermission(ctx, models.ProjectPermission{
  38. ProjectID: project.ID,
  39. UserID: user.ID,
  40. Level: models.ProjectPermissionLevelOwner,
  41. })
  42. return project, nil
  43. }
  44. func (r *mutationResolver) CreateActivity(ctx context.Context, input graphcore.ActivityCreateInput) (*models.Activity, error) {
  45. user := r.Auth.UserFromContext(ctx)
  46. if user == nil {
  47. return nil, slerrors.PermissionDenied
  48. }
  49. project, err := r.Database.Projects().Find(ctx, input.ProjectID)
  50. if err != nil {
  51. return nil, err
  52. }
  53. if perm, err := r.Auth.ProjectPermission(ctx, project.ID); err != nil || !perm.CanManageActivities() {
  54. return nil, slerrors.PermissionDenied
  55. }
  56. activity := &models.Activity{
  57. ProjectID: project.ID,
  58. Name: input.Name,
  59. Countable: input.Countable != nil && *input.Countable,
  60. UnitIsTimeSpent: input.UnitIsTimeSpent != nil && *input.UnitIsTimeSpent,
  61. BaseValue: input.BaseValue,
  62. }
  63. if !activity.UnitIsTimeSpent && activity.Countable {
  64. if input.UnitName != nil {
  65. activity.UnitName = *input.UnitName
  66. } else {
  67. return nil, errors.New("unit name is required for countable non-time-spent activities")
  68. }
  69. }
  70. if activity.UnitIsTimeSpent || activity.Countable {
  71. if input.UnitValue != nil {
  72. activity.UnitValue = *input.UnitValue
  73. }
  74. }
  75. return r.Database.Activities().Insert(ctx, *activity)
  76. }
  77. func (r *mutationResolver) EditActivity(ctx context.Context, input graphcore.ActivityEditInput) (*models.Activity, error) {
  78. user := r.Auth.UserFromContext(ctx)
  79. if user == nil {
  80. return nil, slerrors.PermissionDenied
  81. }
  82. activity, err := r.Database.Activities().Find(ctx, input.ActivityID)
  83. if err != nil {
  84. return nil, err
  85. }
  86. project, err := r.Database.Projects().Find(ctx, activity.ProjectID)
  87. if err != nil {
  88. return nil, err
  89. }
  90. if perm, err := r.Auth.ProjectPermission(ctx, project.ID); err != nil || !perm.CanManageActivities() {
  91. return nil, slerrors.PermissionDenied
  92. }
  93. if input.SetName != nil {
  94. activity.Name = *input.SetName
  95. }
  96. if input.SetBaseValue != nil {
  97. activity.BaseValue = *input.SetBaseValue
  98. }
  99. if input.SetUnitName != nil {
  100. activity.UnitName = *input.SetUnitName
  101. }
  102. if input.SetUnitValue != nil {
  103. activity.UnitValue = *input.SetUnitValue
  104. }
  105. err = r.Database.Activities().Save(ctx, *activity)
  106. if err != nil {
  107. return nil, err
  108. }
  109. return activity, nil
  110. }
  111. func (r *mutationResolver) CreateItem(ctx context.Context, input *graphcore.ItemCreateInput) (*models.Item, error) {
  112. user := r.Auth.UserFromContext(ctx)
  113. if user == nil {
  114. return nil, slerrors.PermissionDenied
  115. }
  116. if input.Name == "" {
  117. return nil, errors.New("name cannot be blank")
  118. }
  119. if input.Description == "" {
  120. return nil, errors.New("name cannot be blank")
  121. }
  122. if len(input.Tags) == 0 {
  123. return nil, errors.New("at least one tag is required")
  124. }
  125. item := models.Item{
  126. Name: input.Name,
  127. Description: input.Description,
  128. Tags: input.Tags,
  129. QuantityUnit: input.QuantityUnit,
  130. ImageURL: nil,
  131. }
  132. if input.Image != nil {
  133. url, err := r.Upload.UploadImage(
  134. ctx,
  135. "item-image/"+generate.Generate(16, "I"),
  136. input.Image.ContentType,
  137. input.Image.Size,
  138. input.Image.File,
  139. )
  140. if err != nil {
  141. return nil, err
  142. }
  143. item.ImageURL = &url
  144. }
  145. return r.Database.Items().Insert(ctx, item)
  146. }
  147. func (r *mutationResolver) EditItem(ctx context.Context, input *graphcore.ItemEditInput) (*models.Item, error) {
  148. user := r.Auth.UserFromContext(ctx)
  149. if user == nil {
  150. return nil, slerrors.PermissionDenied
  151. }
  152. item, err := r.Database.Items().Find(ctx, input.ItemID)
  153. if err != nil {
  154. return nil, err
  155. }
  156. deleteList := make([]int, 0, len(input.RemoveTags))
  157. for _, tag := range input.RemoveTags {
  158. for i, tag2 := range item.Tags {
  159. if tag == tag2 {
  160. deleteList = append(deleteList, i-len(deleteList))
  161. break
  162. }
  163. }
  164. }
  165. for _, index := range deleteList {
  166. item.Tags = append(item.Tags[:index], item.Tags[index+1:]...)
  167. }
  168. for _, tag := range input.AddTags {
  169. found := false
  170. for _, tag2 := range item.Tags {
  171. if tag == tag2 {
  172. found = true
  173. break
  174. }
  175. }
  176. if !found {
  177. item.Tags = append(item.Tags, tag)
  178. }
  179. }
  180. sort.Strings(item.Tags)
  181. if input.SetQuantityUnit != nil {
  182. item.QuantityUnit = input.SetQuantityUnit
  183. }
  184. if input.ClearQuantityUnit != nil && *input.ClearQuantityUnit {
  185. item.QuantityUnit = nil
  186. }
  187. if input.SetName != nil {
  188. item.Name = *input.SetName
  189. }
  190. if input.SetDescription != nil {
  191. item.Description = *input.SetDescription
  192. }
  193. var prevFile *string
  194. if input.UpdateImage != nil {
  195. url, err := r.Upload.UploadImage(
  196. ctx,
  197. "item-image/"+generate.Generate(16, "U"),
  198. input.UpdateImage.ContentType,
  199. input.UpdateImage.Size,
  200. input.UpdateImage.File,
  201. )
  202. if err != nil {
  203. return nil, err
  204. }
  205. prevFile = item.ImageURL
  206. item.ImageURL = &url
  207. }
  208. err = r.Database.Items().Save(ctx, *item)
  209. if err != nil {
  210. return nil, err
  211. }
  212. if prevFile != nil {
  213. err := r.Upload.Delete(ctx, *prevFile)
  214. if err != nil {
  215. log.Printf("Failed to delete %s: %s", *prevFile, err)
  216. }
  217. }
  218. return item, nil
  219. }
  220. func (r *mutationResolver) CreateIssue(ctx context.Context, input graphcore.IssueCreateInput) (*models.Issue, error) {
  221. user := r.Auth.UserFromContext(ctx)
  222. if user == nil {
  223. return nil, slerrors.PermissionDenied
  224. }
  225. project, err := r.Database.Projects().Find(ctx, input.ProjectID)
  226. if err != nil {
  227. return nil, err
  228. }
  229. if perm, err := r.Auth.ProjectPermission(ctx, project.ID); err != nil || !perm.CanManageOwnIssue() {
  230. return nil, slerrors.PermissionDenied
  231. }
  232. status, err := r.Database.ProjectStatuses().Find(ctx, project.ID, input.StatusName)
  233. if err != nil {
  234. return nil, err
  235. }
  236. issue := &models.Issue{
  237. ProjectID: project.ID,
  238. OwnerID: user.ID,
  239. AssigneeID: "",
  240. StatusStage: status.Stage,
  241. StatusName: status.Name,
  242. Name: input.Name,
  243. Title: input.Name, // Title set below if it's in the input.
  244. Description: input.Description,
  245. }
  246. if input.Title != nil && *input.Title != "" {
  247. issue.Title = *input.Title
  248. }
  249. if input.DueTime != nil && !input.DueTime.IsZero() {
  250. issue.DueTime = *input.DueTime
  251. }
  252. if input.AssigneeID != nil && *input.AssigneeID != "" {
  253. issue.AssigneeID = *input.AssigneeID
  254. }
  255. issue, err = r.Database.Issues().Insert(ctx, *issue)
  256. if err != nil {
  257. return nil, err
  258. }
  259. loaders.IssueLoaderFromContext(ctx).Prime(issue.ID, issue)
  260. return issue, nil
  261. }
  262. func (r *mutationResolver) CreateIssueTask(ctx context.Context, input graphcore.IssueTaskCreateInput) (*models.IssueTask, error) {
  263. user := r.Auth.UserFromContext(ctx)
  264. if user == nil {
  265. return nil, slerrors.PermissionDenied
  266. }
  267. issue, err := loaders.IssueLoaderFromContext(ctx).Load(input.IssueID)
  268. if err != nil {
  269. return nil, err
  270. }
  271. if perm, err := r.Auth.IssuePermission(ctx, *issue); err != nil || !perm.CanManageOwnIssue() {
  272. return nil, slerrors.PermissionDenied
  273. }
  274. status, err := r.Database.ProjectStatuses().Find(ctx, issue.ProjectID, input.StatusName)
  275. if err != nil {
  276. return nil, err
  277. }
  278. activity, err := loaders.ActivityLoaderFromContext(ctx).Load(input.ActivityID)
  279. if err != nil {
  280. return nil, err
  281. } else if activity.ProjectID != issue.ProjectID {
  282. return nil, slerrors.NotFound("Activity")
  283. }
  284. issueTask := &models.IssueTask{
  285. IssueID: issue.ID,
  286. ActivityID: activity.ID,
  287. CreatedTime: time.Now(),
  288. UpdatedTime: time.Now(),
  289. StatusStage: status.Stage,
  290. StatusName: status.Name,
  291. Name: input.Name,
  292. Description: input.Description,
  293. PointsMultiplier: 1.0,
  294. }
  295. if input.EstimatedUnits != nil && activity.Countable && !activity.UnitIsTimeSpent {
  296. issueTask.EstimatedUnits = *input.EstimatedUnits
  297. }
  298. if input.PointsMultiplier != nil && *input.PointsMultiplier > 0 {
  299. issueTask.PointsMultiplier = *input.PointsMultiplier
  300. }
  301. issueTask, err = r.Database.IssueTasks().Insert(ctx, *issueTask)
  302. if err != nil {
  303. return nil, err
  304. }
  305. return issueTask, nil
  306. }
  307. func (r *mutationResolver) EditIssueTask(ctx context.Context, input graphcore.IssueTaskEditInput) (*models.IssueTask, error) {
  308. user := r.Auth.UserFromContext(ctx)
  309. if user == nil {
  310. return nil, slerrors.PermissionDenied
  311. }
  312. task, err := r.Database.IssueTasks().Find(ctx, input.IssueTaskID)
  313. if err != nil {
  314. return nil, err
  315. }
  316. issue, err := loaders.IssueLoaderFromContext(ctx).Load(task.IssueID)
  317. if err != nil {
  318. return nil, err
  319. }
  320. if perm, err := r.Auth.IssuePermission(ctx, *issue); err != nil || !perm.CanManageOwnIssue() {
  321. return nil, slerrors.PermissionDenied
  322. }
  323. if input.SetName != nil {
  324. task.Name = *input.SetName
  325. }
  326. if input.SetDescription != nil {
  327. task.Description = *input.SetDescription
  328. }
  329. if input.SetDueTime != nil {
  330. task.DueTime = input.SetDueTime
  331. }
  332. if input.SetEstimatedTime != nil {
  333. task.EstimatedTime = *input.SetEstimatedTime
  334. }
  335. if input.SetEstimatedUnits != nil {
  336. activity, err := loaders.ActivityLoaderFromContext(ctx).Load(task.ActivityID)
  337. if err != nil {
  338. return nil, err
  339. }
  340. if activity.Countable && !activity.UnitIsTimeSpent {
  341. task.EstimatedUnits = *input.SetEstimatedUnits
  342. }
  343. }
  344. if input.SetPointsMultiplier != nil && *input.SetPointsMultiplier > 0 {
  345. task.PointsMultiplier = *input.SetPointsMultiplier
  346. }
  347. if input.SetStatusName != nil {
  348. status, err := r.Database.ProjectStatuses().Find(ctx, issue.ProjectID, *input.SetStatusName)
  349. if err != nil {
  350. return nil, err
  351. }
  352. task.StatusName = status.Name
  353. task.StatusStage = status.Stage
  354. }
  355. err = r.Database.IssueTasks().Save(ctx, *task)
  356. if err != nil {
  357. return nil, err
  358. }
  359. return task, nil
  360. }
  361. func (r *mutationResolver) CreateIssueItem(ctx context.Context, input graphcore.IssueItemCreateInput) (*models.IssueItem, error) {
  362. user := r.Auth.UserFromContext(ctx)
  363. if user == nil {
  364. return nil, slerrors.PermissionDenied
  365. }
  366. issue, err := loaders.IssueLoaderFromContext(ctx).Load(input.IssueID)
  367. if err != nil {
  368. return nil, err
  369. }
  370. if perm, err := r.Auth.IssuePermission(ctx, *issue); err != nil || !perm.CanManageOwnIssue() {
  371. return nil, slerrors.PermissionDenied
  372. }
  373. item, err := r.Database.Items().Find(ctx, input.ItemID)
  374. if err != nil {
  375. return nil, err
  376. }
  377. issueItem := &models.IssueItem{
  378. IssueID: issue.ID,
  379. ItemID: item.ID,
  380. Quantity: input.Quanitty,
  381. Acquired: input.Acquired != nil && *input.Acquired,
  382. }
  383. return r.Database.IssueItems().Insert(ctx, *issueItem)
  384. }
  385. func (r *mutationResolver) EditIssueItem(ctx context.Context, input graphcore.IssueItemEditInput) (*models.IssueItem, error) {
  386. user := r.Auth.UserFromContext(ctx)
  387. if user == nil {
  388. return nil, slerrors.PermissionDenied
  389. }
  390. item, err := r.Database.IssueItems().Find(ctx, input.IssueItemID)
  391. if err != nil {
  392. return nil, err
  393. }
  394. issue, err := loaders.IssueLoaderFromContext(ctx).Load(item.IssueID)
  395. if err != nil {
  396. return nil, err
  397. }
  398. if perm, err := r.Auth.IssuePermission(ctx, *issue); err != nil || !perm.CanManageOwnIssue() {
  399. return nil, slerrors.PermissionDenied
  400. }
  401. if input.SetAcquired != nil {
  402. item.Acquired = *input.SetAcquired
  403. }
  404. if input.SetQuanitty != nil {
  405. item.Quantity = *input.SetQuanitty
  406. }
  407. err = r.Database.IssueItems().Save(ctx, *item)
  408. if err != nil {
  409. return nil, err
  410. }
  411. return item, nil
  412. }
  413. func (r *mutationResolver) CreateLog(ctx context.Context, input graphcore.LogCreateInput) (*models.Log, error) {
  414. user := r.Auth.UserFromContext(ctx)
  415. if user == nil {
  416. return nil, slerrors.PermissionDenied
  417. }
  418. log := &models.Log{
  419. UserID: user.ID,
  420. Date: input.Date,
  421. Description: input.Description,
  422. Items: make([]models.LogItem, 0, len(input.Items)),
  423. Tasks: make([]models.LogTask, 0, len(input.Tasks)),
  424. }
  425. for _, itemInput := range input.Items {
  426. item, err := r.Database.IssueItems().Find(ctx, itemInput.IssueItemID)
  427. if err != nil {
  428. return nil, err
  429. }
  430. issue, err := loaders.IssueLoaderFromContext(ctx).Load(item.IssueID)
  431. if err != nil {
  432. return nil, err
  433. }
  434. _, err = r.Auth.IssuePermission(ctx, *issue)
  435. if err != nil {
  436. return nil, err
  437. }
  438. log.Items = append(log.Items, models.LogItem{
  439. IssueID: item.IssueID,
  440. IssueItemID: item.ID,
  441. Amount: itemInput.Amount,
  442. })
  443. }
  444. for _, taskInput := range input.Tasks {
  445. task, err := r.Database.IssueTasks().Find(ctx, taskInput.IssueTaskID)
  446. if err != nil {
  447. return nil, err
  448. }
  449. issue, err := loaders.IssueLoaderFromContext(ctx).Load(task.IssueID)
  450. if err != nil {
  451. return nil, err
  452. }
  453. _, err = r.Auth.IssuePermission(ctx, *issue)
  454. if err != nil {
  455. return nil, err
  456. }
  457. log.Tasks = append(log.Tasks, models.LogTask{
  458. IssueID: task.IssueID,
  459. IssueTaskID: task.ID,
  460. Duration: taskInput.Duration,
  461. Units: taskInput.Units,
  462. })
  463. }
  464. return r.Database.Logs().Insert(ctx, *log)
  465. }
  466. func (r *mutationResolver) EditLog(ctx context.Context, input graphcore.LogEditInput) (*models.Log, error) {
  467. user := r.Auth.UserFromContext(ctx)
  468. if user == nil {
  469. return nil, slerrors.PermissionDenied
  470. }
  471. log, err := r.Database.Logs().Find(ctx, input.LogID)
  472. if err != nil {
  473. return nil, err
  474. }
  475. removeItems := append([]string{}, input.RemoveItems...)
  476. for _, itemInput := range input.UpdateItems {
  477. removeItems = append(removeItems, itemInput.IssueItemID)
  478. }
  479. removeTasks := append([]string{}, input.RemoveTasks...)
  480. for _, taskInput := range input.UpdateTasks {
  481. removeTasks = append(removeTasks, taskInput.IssueTaskID)
  482. }
  483. if input.SetDate != nil {
  484. log.Date = *input.SetDate
  485. }
  486. if input.SetDescription != nil {
  487. log.Description = *input.SetDescription
  488. }
  489. for _, remove := range removeItems {
  490. for i, item := range log.Items {
  491. if remove == item.IssueItemID {
  492. log.Items = append(log.Items[:i], log.Items[i+1:]...)
  493. }
  494. }
  495. }
  496. for _, remove := range removeTasks {
  497. for i, task := range log.Tasks {
  498. if remove == task.IssueTaskID {
  499. log.Tasks = append(log.Tasks[:i], log.Tasks[i+1:]...)
  500. }
  501. }
  502. }
  503. for _, itemInput := range input.UpdateItems {
  504. item, err := r.Database.IssueItems().Find(ctx, itemInput.IssueItemID)
  505. if err != nil {
  506. return nil, err
  507. }
  508. issue, err := loaders.IssueLoaderFromContext(ctx).Load(item.IssueID)
  509. if err != nil {
  510. return nil, err
  511. }
  512. _, err = r.Auth.IssuePermission(ctx, *issue)
  513. if err != nil {
  514. return nil, err
  515. }
  516. log.Items = append(log.Items, models.LogItem{
  517. LogID: log.ID,
  518. IssueID: item.IssueID,
  519. IssueItemID: item.ID,
  520. Amount: itemInput.Amount,
  521. })
  522. }
  523. for _, taskInput := range input.UpdateTasks {
  524. task, err := r.Database.IssueTasks().Find(ctx, taskInput.IssueTaskID)
  525. if err != nil {
  526. return nil, err
  527. }
  528. issue, err := loaders.IssueLoaderFromContext(ctx).Load(task.IssueID)
  529. if err != nil {
  530. return nil, err
  531. }
  532. _, err = r.Auth.IssuePermission(ctx, *issue)
  533. if err != nil {
  534. return nil, err
  535. }
  536. log.Tasks = append(log.Tasks, models.LogTask{
  537. LogID: log.ID,
  538. IssueID: task.IssueID,
  539. IssueTaskID: task.ID,
  540. Duration: taskInput.Duration,
  541. Units: taskInput.Units,
  542. })
  543. }
  544. err = r.Database.Logs().Save(ctx, *log)
  545. if err != nil {
  546. return nil, err
  547. }
  548. return log, nil
  549. }
  550. func (r *mutationResolver) LoginUser(ctx context.Context, input graphcore.UserLoginInput) (*models.User, error) {
  551. return r.Auth.Login(ctx, input.Username, input.Password)
  552. }
  553. func (r *mutationResolver) LogoutUser(ctx context.Context) (*models.User, error) {
  554. return r.Auth.Logout(ctx)
  555. }
  556. func (r *mutationResolver) CreateUser(ctx context.Context, input graphcore.UserCreateInput) (*models.User, error) {
  557. active := input.Active == nil || *input.Active
  558. admin := input.Admin != nil && *input.Admin
  559. return r.Auth.CreateUser(ctx, input.Username, input.Password, input.Name, active, admin)
  560. }
  561. func (r *mutationResolver) EditUser(ctx context.Context, input graphcore.UserEditInput) (*models.User, error) {
  562. return r.Auth.EditUser(ctx, input.Username, input.SetName, input.CurrentPassword, input.SetPassword)
  563. }
  564. // Mutation returns graphcore.MutationResolver implementation.
  565. func (r *Resolver) Mutation() graphcore.MutationResolver { return &mutationResolver{r} }
  566. type mutationResolver struct{ *Resolver }