UpdateExistingItem.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import { ContentType, Dates, Result, Timestamps, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
  2. import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
  3. import { TimerInterface } from '@standardnotes/time'
  4. import { Item } from '../../../Item/Item'
  5. import { UpdateExistingItemDTO } from './UpdateExistingItemDTO'
  6. import { ItemRepositoryInterface } from '../../../Item/ItemRepositoryInterface'
  7. import { DomainEventFactoryInterface } from '../../../Event/DomainEventFactoryInterface'
  8. export class UpdateExistingItem implements UseCaseInterface<Item> {
  9. constructor(
  10. private itemRepository: ItemRepositoryInterface,
  11. private timer: TimerInterface,
  12. private domainEventPublisher: DomainEventPublisherInterface,
  13. private domainEventFactory: DomainEventFactoryInterface,
  14. private revisionFrequency: number,
  15. ) {}
  16. async execute(dto: UpdateExistingItemDTO): Promise<Result<Item>> {
  17. let sessionUuid = null
  18. if (dto.sessionUuid) {
  19. const sessionUuidOrError = Uuid.create(dto.sessionUuid)
  20. if (sessionUuidOrError.isFailed()) {
  21. return Result.fail(sessionUuidOrError.getError())
  22. }
  23. sessionUuid = sessionUuidOrError.getValue()
  24. }
  25. dto.existingItem.props.updatedWithSession = sessionUuid
  26. if (dto.itemHash.props.content) {
  27. dto.existingItem.props.content = dto.itemHash.props.content
  28. }
  29. if (dto.itemHash.props.content_type) {
  30. const contentTypeOrError = ContentType.create(dto.itemHash.props.content_type)
  31. if (contentTypeOrError.isFailed()) {
  32. return Result.fail(contentTypeOrError.getError())
  33. }
  34. const contentType = contentTypeOrError.getValue()
  35. dto.existingItem.props.contentType = contentType
  36. }
  37. if (dto.itemHash.props.deleted !== undefined) {
  38. dto.existingItem.props.deleted = dto.itemHash.props.deleted
  39. }
  40. let wasMarkedAsDuplicate = false
  41. if (dto.itemHash.props.duplicate_of) {
  42. const duplicateOfOrError = Uuid.create(dto.itemHash.props.duplicate_of)
  43. if (duplicateOfOrError.isFailed()) {
  44. return Result.fail(duplicateOfOrError.getError())
  45. }
  46. wasMarkedAsDuplicate = dto.existingItem.props.duplicateOf === null
  47. dto.existingItem.props.duplicateOf = duplicateOfOrError.getValue()
  48. }
  49. if (dto.itemHash.props.auth_hash) {
  50. dto.existingItem.props.authHash = dto.itemHash.props.auth_hash
  51. }
  52. if (dto.itemHash.props.enc_item_key) {
  53. dto.existingItem.props.encItemKey = dto.itemHash.props.enc_item_key
  54. }
  55. if (dto.itemHash.props.items_key_id) {
  56. dto.existingItem.props.itemsKeyId = dto.itemHash.props.items_key_id
  57. }
  58. const updatedAtTimestamp = this.timer.getTimestampInMicroseconds()
  59. const secondsFromLastUpdate = this.timer.convertMicrosecondsToSeconds(
  60. updatedAtTimestamp - dto.existingItem.props.timestamps.updatedAt,
  61. )
  62. const updatedAtDate = this.timer.convertMicrosecondsToDate(updatedAtTimestamp)
  63. let createdAtTimestamp: number
  64. let createdAtDate: Date
  65. if (dto.itemHash.props.created_at_timestamp) {
  66. createdAtTimestamp = dto.itemHash.props.created_at_timestamp
  67. createdAtDate = this.timer.convertMicrosecondsToDate(createdAtTimestamp)
  68. } else if (dto.itemHash.props.created_at) {
  69. createdAtTimestamp = this.timer.convertStringDateToMicroseconds(dto.itemHash.props.created_at)
  70. createdAtDate = this.timer.convertStringDateToDate(dto.itemHash.props.created_at)
  71. } else {
  72. return Result.fail('Created at timestamp is required.')
  73. }
  74. const datesOrError = Dates.create(createdAtDate, updatedAtDate)
  75. if (datesOrError.isFailed()) {
  76. return Result.fail(datesOrError.getError())
  77. }
  78. dto.existingItem.props.dates = datesOrError.getValue()
  79. const timestampsOrError = Timestamps.create(createdAtTimestamp, updatedAtTimestamp)
  80. if (timestampsOrError.isFailed()) {
  81. return Result.fail(timestampsOrError.getError())
  82. }
  83. dto.existingItem.props.timestamps = timestampsOrError.getValue()
  84. dto.existingItem.props.contentSize = Buffer.byteLength(JSON.stringify(dto.existingItem))
  85. if (dto.itemHash.props.deleted === true) {
  86. dto.existingItem.props.deleted = true
  87. dto.existingItem.props.content = null
  88. dto.existingItem.props.contentSize = 0
  89. dto.existingItem.props.encItemKey = null
  90. dto.existingItem.props.authHash = null
  91. dto.existingItem.props.itemsKeyId = null
  92. }
  93. await this.itemRepository.save(dto.existingItem)
  94. if (secondsFromLastUpdate >= this.revisionFrequency) {
  95. if (
  96. dto.existingItem.props.contentType.value !== null &&
  97. [ContentType.TYPES.Note, ContentType.TYPES.File].includes(dto.existingItem.props.contentType.value)
  98. ) {
  99. await this.domainEventPublisher.publish(
  100. this.domainEventFactory.createItemRevisionCreationRequested(
  101. dto.existingItem.id.toString(),
  102. dto.existingItem.props.userUuid.value,
  103. ),
  104. )
  105. }
  106. }
  107. if (wasMarkedAsDuplicate) {
  108. await this.domainEventPublisher.publish(
  109. this.domainEventFactory.createDuplicateItemSyncedEvent(
  110. dto.existingItem.id.toString(),
  111. dto.existingItem.props.userUuid.value,
  112. ),
  113. )
  114. }
  115. return Result.ok(dto.existingItem)
  116. }
  117. }