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.

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