diff --git a/packages/services/src/Domain/Application/ApplicationInterface.ts b/packages/services/src/Domain/Application/ApplicationInterface.ts index 6ff85a093..8520484bd 100644 --- a/packages/services/src/Domain/Application/ApplicationInterface.ts +++ b/packages/services/src/Domain/Application/ApplicationInterface.ts @@ -11,6 +11,7 @@ import { LegacyApiServiceInterface, StatusServiceInterface, MfaServiceInterface, + GenerateUuid, } from '@standardnotes/services' import { VaultLockServiceInterface } from './../VaultLock/VaultLockServiceInterface' import { HistoryServiceInterface } from './../History/HistoryServiceInterface' @@ -85,6 +86,7 @@ export interface ApplicationInterface { get files(): FilesClientInterface get history(): HistoryServiceInterface get homeServer(): HomeServerServiceInterface | undefined + get generateUuid(): GenerateUuid get items(): ItemManagerInterface get legacyApi(): LegacyApiServiceInterface get mfa(): MfaServiceInterface diff --git a/packages/services/src/Domain/UseCase/GenerateUuid.ts b/packages/services/src/Domain/UseCase/GenerateUuid.ts new file mode 100644 index 000000000..e7e7630f1 --- /dev/null +++ b/packages/services/src/Domain/UseCase/GenerateUuid.ts @@ -0,0 +1,12 @@ +import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core' +import { PureCryptoInterface } from '@standardnotes/sncrypto-common' + +export class GenerateUuid implements SyncUseCaseInterface { + constructor(private crypto: PureCryptoInterface) {} + + execute(): Result { + const uuid = this.crypto.generateUUID() + + return Result.ok(uuid) + } +} diff --git a/packages/services/src/Domain/index.ts b/packages/services/src/Domain/index.ts index 1a57b56c2..f17b8dd9e 100644 --- a/packages/services/src/Domain/index.ts +++ b/packages/services/src/Domain/index.ts @@ -178,6 +178,7 @@ export * from './Sync/SyncServiceInterface' export * from './Sync/SyncSource' export * from './UseCase/ChangeAndSaveItem' export * from './UseCase/DiscardItemsLocally' +export * from './UseCase/GenerateUuid' export * from './UseCase/GetHost' export * from './UseCase/SetHost' export * from './User/AccountEvent' diff --git a/packages/snjs/lib/Application/Application.ts b/packages/snjs/lib/Application/Application.ts index bd8eef1b1..4809a99f9 100644 --- a/packages/snjs/lib/Application/Application.ts +++ b/packages/snjs/lib/Application/Application.ts @@ -81,6 +81,7 @@ import { GetHost, SetHost, MfaServiceInterface, + GenerateUuid, } from '@standardnotes/services' import { SNNote, @@ -112,7 +113,6 @@ import { removeFromArray, isNullOrUndefined, sleep, - UuidGenerator, useBoolean, LoggerInterface, canBlockDeinit, @@ -932,10 +932,6 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli return this.migrations.hasPendingMigrations() } - public generateUuid(): string { - return UuidGenerator.GenerateUuid() - } - public presentKeyRecoveryWizard(): void { const service = this.dependencies.get(TYPES.KeyRecoveryService) return service.presentKeyRecoveryWizard() @@ -1243,6 +1239,10 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli return this.dependencies.get(TYPES.MfaService) } + public get generateUuid(): GenerateUuid { + return this.dependencies.get(TYPES.GenerateUuid) + } + private get migrations(): MigrationService { return this.dependencies.get(TYPES.MigrationService) } diff --git a/packages/snjs/lib/Application/Dependencies/Dependencies.ts b/packages/snjs/lib/Application/Dependencies/Dependencies.ts index 56c7e878a..e32667c69 100644 --- a/packages/snjs/lib/Application/Dependencies/Dependencies.ts +++ b/packages/snjs/lib/Application/Dependencies/Dependencies.ts @@ -130,6 +130,7 @@ import { FullyResolvedApplicationOptions, GetHost, SetHost, + GenerateUuid, } from '@standardnotes/services' import { ItemManager } from '../../Services/Items/ItemManager' import { PayloadManager } from '../../Services/Payloads/PayloadManager' @@ -214,6 +215,10 @@ export class Dependencies { } private registerUseCaseMakers() { + this.factory.set(TYPES.GenerateUuid, () => { + return new GenerateUuid(this.get(TYPES.Crypto)) + }) + this.factory.set(TYPES.DecryptErroredPayloads, () => { return new DecryptErroredPayloads( this.get(TYPES.ItemsEncryptionService), diff --git a/packages/snjs/lib/Application/Dependencies/Types.ts b/packages/snjs/lib/Application/Dependencies/Types.ts index e01402355..98c464f10 100644 --- a/packages/snjs/lib/Application/Dependencies/Types.ts +++ b/packages/snjs/lib/Application/Dependencies/Types.ts @@ -159,6 +159,7 @@ export const TYPES = { ChangeAndSaveItem: Symbol.for('ChangeAndSaveItem'), GetHost: Symbol.for('GetHost'), SetHost: Symbol.for('SetHost'), + GenerateUuid: Symbol.for('GenerateUuid'), // Mappers SessionStorageMapper: Symbol.for('SessionStorageMapper'), diff --git a/packages/ui-services/src/Import/AegisConverter/AegisToAuthenticatorConverter.spec.ts b/packages/ui-services/src/Import/AegisConverter/AegisToAuthenticatorConverter.spec.ts index d8c1a6d40..3f40295cf 100644 --- a/packages/ui-services/src/Import/AegisConverter/AegisToAuthenticatorConverter.spec.ts +++ b/packages/ui-services/src/Import/AegisConverter/AegisToAuthenticatorConverter.spec.ts @@ -1,13 +1,18 @@ +import { PureCryptoInterface } from '@standardnotes/sncrypto-common' import { NativeFeatureIdentifier, NoteType } from '@standardnotes/features' import { AegisToAuthenticatorConverter } from './AegisToAuthenticatorConverter' import data from './testData' -import { UuidGenerator } from '@standardnotes/utils' - -UuidGenerator.SetGenerator(() => String(Math.random())) +import { GenerateUuid } from '@standardnotes/services' describe('AegisConverter', () => { + const crypto = { + generateUUID: () => String(Math.random()), + } as unknown as PureCryptoInterface + + const generateUuid = new GenerateUuid(crypto) + it('should parse entries', () => { - const converter = new AegisToAuthenticatorConverter() + const converter = new AegisToAuthenticatorConverter(generateUuid) const result = converter.parseEntries(data) @@ -28,7 +33,7 @@ describe('AegisConverter', () => { }) it('should create note from entries with editor info', () => { - const converter = new AegisToAuthenticatorConverter() + const converter = new AegisToAuthenticatorConverter(generateUuid) const parsedEntries = converter.parseEntries(data) @@ -55,7 +60,7 @@ describe('AegisConverter', () => { }) it('should create note from entries without editor info', () => { - const converter = new AegisToAuthenticatorConverter() + const converter = new AegisToAuthenticatorConverter(generateUuid) const parsedEntries = converter.parseEntries(data) diff --git a/packages/ui-services/src/Import/AegisConverter/AegisToAuthenticatorConverter.ts b/packages/ui-services/src/Import/AegisConverter/AegisToAuthenticatorConverter.ts index 76c79e104..957c12ef0 100644 --- a/packages/ui-services/src/Import/AegisConverter/AegisToAuthenticatorConverter.ts +++ b/packages/ui-services/src/Import/AegisConverter/AegisToAuthenticatorConverter.ts @@ -2,7 +2,7 @@ import { DecryptedTransferPayload, NoteContent } from '@standardnotes/models' import { readFileAsText } from '../Utils' import { NativeFeatureIdentifier, NoteType } from '@standardnotes/features' import { ContentType } from '@standardnotes/domain-core' -import { UuidGenerator } from '@standardnotes/utils' +import { GenerateUuid } from '@standardnotes/services' type AegisData = { db: { @@ -27,7 +27,7 @@ type AuthenticatorEntry = { } export class AegisToAuthenticatorConverter { - constructor() {} + constructor(private _generateUuid: GenerateUuid) {} // eslint-disable-next-line @typescript-eslint/no-explicit-any static isValidAegisJson(json: any): boolean { @@ -63,7 +63,7 @@ export class AegisToAuthenticatorConverter { created_at_timestamp: file.lastModified, updated_at: new Date(file.lastModified), updated_at_timestamp: file.lastModified, - uuid: UuidGenerator.GenerateUuid(), + uuid: this._generateUuid.execute().getValue(), content_type: ContentType.TYPES.Note, content: { title: file.name.split('.')[0], diff --git a/packages/ui-services/src/Import/EvernoteConverter/EvernoteConverter.spec.ts b/packages/ui-services/src/Import/EvernoteConverter/EvernoteConverter.spec.ts index 116be7b08..86b5744ca 100644 --- a/packages/ui-services/src/Import/EvernoteConverter/EvernoteConverter.spec.ts +++ b/packages/ui-services/src/Import/EvernoteConverter/EvernoteConverter.spec.ts @@ -6,7 +6,8 @@ import { ContentType } from '@standardnotes/domain-core' import { DecryptedTransferPayload, NoteContent, TagContent } from '@standardnotes/models' import { EvernoteConverter } from './EvernoteConverter' import data from './testData' -import { UuidGenerator } from '@standardnotes/utils' +import { PureCryptoInterface } from '@standardnotes/sncrypto-common' +import { GenerateUuid } from '@standardnotes/services' // Mock dayjs so dayjs.extend() doesn't throw an error in EvernoteConverter.ts jest.mock('dayjs', () => { @@ -21,11 +22,17 @@ jest.mock('dayjs', () => { } }) -UuidGenerator.SetGenerator(() => String(Math.random())) + describe('EvernoteConverter', () => { + const crypto = { + generateUUID: () => String(Math.random()), + } as unknown as PureCryptoInterface + + const generateUuid = new GenerateUuid(crypto) + it('should parse and strip html', () => { - const converter = new EvernoteConverter() + const converter = new EvernoteConverter(generateUuid) const result = converter.parseENEXData(data, true) @@ -45,7 +52,7 @@ describe('EvernoteConverter', () => { }) it('should parse and not strip html', () => { - const converter = new EvernoteConverter() + const converter = new EvernoteConverter(generateUuid) const result = converter.parseENEXData(data, false) diff --git a/packages/ui-services/src/Import/EvernoteConverter/EvernoteConverter.ts b/packages/ui-services/src/Import/EvernoteConverter/EvernoteConverter.ts index 0020529da..7181af5c5 100644 --- a/packages/ui-services/src/Import/EvernoteConverter/EvernoteConverter.ts +++ b/packages/ui-services/src/Import/EvernoteConverter/EvernoteConverter.ts @@ -4,14 +4,14 @@ import dayjs from 'dayjs' import customParseFormat from 'dayjs/plugin/customParseFormat' import utc from 'dayjs/plugin/utc' import { ContentType } from '@standardnotes/domain-core' -import { UuidGenerator } from '@standardnotes/utils' +import { GenerateUuid } from '@standardnotes/services' dayjs.extend(customParseFormat) dayjs.extend(utc) const dateFormat = 'YYYYMMDDTHHmmss' export class EvernoteConverter { - constructor() {} + constructor(private _generateUuid: GenerateUuid) {} async convertENEXFileToNotesAndTags(file: File, stripHTML: boolean): Promise { const content = await readFileAsText(file) @@ -35,7 +35,7 @@ export class EvernoteConverter { created_at_timestamp: now.getTime(), updated_at: now, updated_at_timestamp: now.getTime(), - uuid: UuidGenerator.GenerateUuid(), + uuid: this._generateUuid.execute().getValue(), content_type: ContentType.TYPES.Tag, content: { title: defaultTagName, @@ -88,7 +88,7 @@ export class EvernoteConverter { created_at_timestamp: createdAtDate.getTime(), updated_at: updatedAtDate, updated_at_timestamp: updatedAtDate.getTime(), - uuid: UuidGenerator.GenerateUuid(), + uuid: this._generateUuid.execute().getValue(), content_type: ContentType.TYPES.Note, content: { title: !title ? `Imported note ${index + 1} from Evernote` : title, @@ -111,7 +111,7 @@ export class EvernoteConverter { if (!tag) { const now = new Date() tag = { - uuid: UuidGenerator.GenerateUuid(), + uuid: this._generateUuid.execute().getValue(), content_type: ContentType.TYPES.Tag, created_at: now, created_at_timestamp: now.getTime(), diff --git a/packages/ui-services/src/Import/GoogleKeepConverter/GoogleKeepConverter.spec.ts b/packages/ui-services/src/Import/GoogleKeepConverter/GoogleKeepConverter.spec.ts index 487980889..7b71c2436 100644 --- a/packages/ui-services/src/Import/GoogleKeepConverter/GoogleKeepConverter.spec.ts +++ b/packages/ui-services/src/Import/GoogleKeepConverter/GoogleKeepConverter.spec.ts @@ -4,13 +4,18 @@ import { jsonTestData, htmlTestData } from './testData' import { GoogleKeepConverter } from './GoogleKeepConverter' -import { UuidGenerator } from '@standardnotes/utils' - -UuidGenerator.SetGenerator(() => String(Math.random())) +import { PureCryptoInterface } from '@standardnotes/sncrypto-common' +import { GenerateUuid } from '@standardnotes/services' describe('GoogleKeepConverter', () => { + const crypto = { + generateUUID: () => String(Math.random()), + } as unknown as PureCryptoInterface + + const generateUuid = new GenerateUuid(crypto) + it('should parse json data', () => { - const converter = new GoogleKeepConverter() + const converter = new GoogleKeepConverter(generateUuid) const result = converter.tryParseAsJson(jsonTestData) @@ -27,7 +32,7 @@ describe('GoogleKeepConverter', () => { }) it('should parse html data', () => { - const converter = new GoogleKeepConverter() + const converter = new GoogleKeepConverter(generateUuid) const result = converter.tryParseAsHtml( htmlTestData, diff --git a/packages/ui-services/src/Import/GoogleKeepConverter/GoogleKeepConverter.ts b/packages/ui-services/src/Import/GoogleKeepConverter/GoogleKeepConverter.ts index fdc805c66..3e0867ec4 100644 --- a/packages/ui-services/src/Import/GoogleKeepConverter/GoogleKeepConverter.ts +++ b/packages/ui-services/src/Import/GoogleKeepConverter/GoogleKeepConverter.ts @@ -1,7 +1,7 @@ import { ContentType } from '@standardnotes/domain-core' import { DecryptedTransferPayload, NoteContent } from '@standardnotes/models' import { readFileAsText } from '../Utils' -import { UuidGenerator } from '@standardnotes/utils' +import { GenerateUuid } from '@standardnotes/services' type GoogleKeepJsonNote = { color: string @@ -14,7 +14,7 @@ type GoogleKeepJsonNote = { } export class GoogleKeepConverter { - constructor() {} + constructor(private _generateUuid: GenerateUuid) {} async convertGoogleKeepBackupFileToNote( file: File, @@ -66,7 +66,7 @@ export class GoogleKeepConverter { created_at_timestamp: date.getTime(), updated_at: date, updated_at_timestamp: date.getTime(), - uuid: UuidGenerator.GenerateUuid(), + uuid: this._generateUuid.execute().getValue(), content_type: ContentType.TYPES.Note, content: { title: title, @@ -121,7 +121,7 @@ export class GoogleKeepConverter { created_at_timestamp: date.getTime(), updated_at: date, updated_at_timestamp: date.getTime(), - uuid: UuidGenerator.GenerateUuid(), + uuid: this._generateUuid.execute().getValue(), content_type: ContentType.TYPES.Note, content: { title: parsed.title, diff --git a/packages/ui-services/src/Import/Importer.ts b/packages/ui-services/src/Import/Importer.ts index 5f0f10daa..0f825eb97 100644 --- a/packages/ui-services/src/Import/Importer.ts +++ b/packages/ui-services/src/Import/Importer.ts @@ -2,6 +2,7 @@ import { parseFileName } from '@standardnotes/filepicker' import { FeatureStatus, FeaturesClientInterface, + GenerateUuid, ItemManagerInterface, MutatorClientInterface, } from '@standardnotes/services' @@ -27,12 +28,13 @@ export class Importer { private features: FeaturesClientInterface, private mutator: MutatorClientInterface, private items: ItemManagerInterface, + _generateUuid: GenerateUuid, ) { - this.aegisConverter = new AegisToAuthenticatorConverter() - this.googleKeepConverter = new GoogleKeepConverter() - this.simplenoteConverter = new SimplenoteConverter() - this.plaintextConverter = new PlaintextConverter() - this.evernoteConverter = new EvernoteConverter() + this.aegisConverter = new AegisToAuthenticatorConverter(_generateUuid) + this.googleKeepConverter = new GoogleKeepConverter(_generateUuid) + this.simplenoteConverter = new SimplenoteConverter(_generateUuid) + this.plaintextConverter = new PlaintextConverter(_generateUuid) + this.evernoteConverter = new EvernoteConverter(_generateUuid) } static detectService = async (file: File): Promise => { diff --git a/packages/ui-services/src/Import/PlaintextConverter/PlaintextConverter.ts b/packages/ui-services/src/Import/PlaintextConverter/PlaintextConverter.ts index 6581e73d4..1b1d965ae 100644 --- a/packages/ui-services/src/Import/PlaintextConverter/PlaintextConverter.ts +++ b/packages/ui-services/src/Import/PlaintextConverter/PlaintextConverter.ts @@ -2,9 +2,11 @@ import { ContentType } from '@standardnotes/domain-core' import { parseFileName } from '@standardnotes/filepicker' import { DecryptedTransferPayload, NoteContent } from '@standardnotes/models' import { readFileAsText } from '../Utils' -import { UuidGenerator } from '@standardnotes/utils' +import { GenerateUuid } from '@standardnotes/services' export class PlaintextConverter { + constructor(private _generateUuid: GenerateUuid) {} + static isValidPlaintextFile(file: File): boolean { return file.type === 'text/plain' || file.type === 'text/markdown' } @@ -22,7 +24,7 @@ export class PlaintextConverter { created_at_timestamp: createdAtDate.getTime(), updated_at: updatedAtDate, updated_at_timestamp: updatedAtDate.getTime(), - uuid: UuidGenerator.GenerateUuid(), + uuid: this._generateUuid.execute().getValue(), content_type: ContentType.TYPES.Note, content: { title: name, diff --git a/packages/ui-services/src/Import/SimplenoteConverter/SimplenoteConverter.spec.ts b/packages/ui-services/src/Import/SimplenoteConverter/SimplenoteConverter.spec.ts index c9458f596..fb84ba3b6 100644 --- a/packages/ui-services/src/Import/SimplenoteConverter/SimplenoteConverter.spec.ts +++ b/packages/ui-services/src/Import/SimplenoteConverter/SimplenoteConverter.spec.ts @@ -1,12 +1,17 @@ -import { UuidGenerator } from '@standardnotes/utils' +import { PureCryptoInterface } from '@standardnotes/sncrypto-common' import { SimplenoteConverter } from './SimplenoteConverter' import data from './testData' - -UuidGenerator.SetGenerator(() => String(Math.random())) +import { GenerateUuid } from '@standardnotes/services' describe('SimplenoteConverter', () => { + const crypto = { + generateUUID: () => String(Math.random()), + } as unknown as PureCryptoInterface + + const generateUuid = new GenerateUuid(crypto) + it('should parse', () => { - const converter = new SimplenoteConverter() + const converter = new SimplenoteConverter(generateUuid) const result = converter.parse(data) diff --git a/packages/ui-services/src/Import/SimplenoteConverter/SimplenoteConverter.ts b/packages/ui-services/src/Import/SimplenoteConverter/SimplenoteConverter.ts index 347b5d6cf..6f85c003b 100644 --- a/packages/ui-services/src/Import/SimplenoteConverter/SimplenoteConverter.ts +++ b/packages/ui-services/src/Import/SimplenoteConverter/SimplenoteConverter.ts @@ -1,7 +1,7 @@ import { DecryptedTransferPayload, NoteContent } from '@standardnotes/models' import { ContentType } from '@standardnotes/domain-core' import { readFileAsText } from '../Utils' -import { UuidGenerator } from '@standardnotes/utils' +import { GenerateUuid } from '@standardnotes/services' type SimplenoteItem = { creationDate: string @@ -18,7 +18,7 @@ type SimplenoteData = { const isSimplenoteEntry = (entry: any): boolean => entry.id && entry.content && entry.creationDate && entry.lastModified export class SimplenoteConverter { - constructor() {} + constructor(private _generateUuid: GenerateUuid) {} // eslint-disable-next-line @typescript-eslint/no-explicit-any static isValidSimplenoteJson(json: any): boolean { @@ -55,7 +55,7 @@ export class SimplenoteConverter { created_at_timestamp: createdAtDate.getTime(), updated_at: updatedAtDate, updated_at_timestamp: updatedAtDate.getTime(), - uuid: UuidGenerator.GenerateUuid(), + uuid: this._generateUuid.execute().getValue(), content_type: ContentType.TYPES.Note, content: { title, diff --git a/packages/web/src/javascripts/Application/Dependencies/WebDependencies.ts b/packages/web/src/javascripts/Application/Dependencies/WebDependencies.ts index 6a3593971..c601541a8 100644 --- a/packages/web/src/javascripts/Application/Dependencies/WebDependencies.ts +++ b/packages/web/src/javascripts/Application/Dependencies/WebDependencies.ts @@ -56,7 +56,7 @@ export class WebDependencies extends DependencyContainer { super() this.bind(Web_TYPES.Importer, () => { - return new Importer(application.features, application.mutator, application.items) + return new Importer(application.features, application.mutator, application.items, application.generateUuid) }) this.bind(Web_TYPES.IsNativeIOS, () => {