chore: fix uuid generation for importers

This commit is contained in:
Mo
2023-08-07 05:06:11 -05:00
parent cedd8e1006
commit ee473fe34d
17 changed files with 94 additions and 47 deletions

View File

@@ -11,6 +11,7 @@ import {
LegacyApiServiceInterface, LegacyApiServiceInterface,
StatusServiceInterface, StatusServiceInterface,
MfaServiceInterface, MfaServiceInterface,
GenerateUuid,
} from '@standardnotes/services' } from '@standardnotes/services'
import { VaultLockServiceInterface } from './../VaultLock/VaultLockServiceInterface' import { VaultLockServiceInterface } from './../VaultLock/VaultLockServiceInterface'
import { HistoryServiceInterface } from './../History/HistoryServiceInterface' import { HistoryServiceInterface } from './../History/HistoryServiceInterface'
@@ -85,6 +86,7 @@ export interface ApplicationInterface {
get files(): FilesClientInterface get files(): FilesClientInterface
get history(): HistoryServiceInterface get history(): HistoryServiceInterface
get homeServer(): HomeServerServiceInterface | undefined get homeServer(): HomeServerServiceInterface | undefined
get generateUuid(): GenerateUuid
get items(): ItemManagerInterface get items(): ItemManagerInterface
get legacyApi(): LegacyApiServiceInterface get legacyApi(): LegacyApiServiceInterface
get mfa(): MfaServiceInterface get mfa(): MfaServiceInterface

View File

@@ -0,0 +1,12 @@
import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
export class GenerateUuid implements SyncUseCaseInterface<string> {
constructor(private crypto: PureCryptoInterface) {}
execute(): Result<string> {
const uuid = this.crypto.generateUUID()
return Result.ok(uuid)
}
}

View File

@@ -178,6 +178,7 @@ export * from './Sync/SyncServiceInterface'
export * from './Sync/SyncSource' export * from './Sync/SyncSource'
export * from './UseCase/ChangeAndSaveItem' export * from './UseCase/ChangeAndSaveItem'
export * from './UseCase/DiscardItemsLocally' export * from './UseCase/DiscardItemsLocally'
export * from './UseCase/GenerateUuid'
export * from './UseCase/GetHost' export * from './UseCase/GetHost'
export * from './UseCase/SetHost' export * from './UseCase/SetHost'
export * from './User/AccountEvent' export * from './User/AccountEvent'

View File

@@ -81,6 +81,7 @@ import {
GetHost, GetHost,
SetHost, SetHost,
MfaServiceInterface, MfaServiceInterface,
GenerateUuid,
} from '@standardnotes/services' } from '@standardnotes/services'
import { import {
SNNote, SNNote,
@@ -112,7 +113,6 @@ import {
removeFromArray, removeFromArray,
isNullOrUndefined, isNullOrUndefined,
sleep, sleep,
UuidGenerator,
useBoolean, useBoolean,
LoggerInterface, LoggerInterface,
canBlockDeinit, canBlockDeinit,
@@ -932,10 +932,6 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
return this.migrations.hasPendingMigrations() return this.migrations.hasPendingMigrations()
} }
public generateUuid(): string {
return UuidGenerator.GenerateUuid()
}
public presentKeyRecoveryWizard(): void { public presentKeyRecoveryWizard(): void {
const service = this.dependencies.get<KeyRecoveryService>(TYPES.KeyRecoveryService) const service = this.dependencies.get<KeyRecoveryService>(TYPES.KeyRecoveryService)
return service.presentKeyRecoveryWizard() return service.presentKeyRecoveryWizard()
@@ -1243,6 +1239,10 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
return this.dependencies.get<MfaService>(TYPES.MfaService) return this.dependencies.get<MfaService>(TYPES.MfaService)
} }
public get generateUuid(): GenerateUuid {
return this.dependencies.get<GenerateUuid>(TYPES.GenerateUuid)
}
private get migrations(): MigrationService { private get migrations(): MigrationService {
return this.dependencies.get<MigrationService>(TYPES.MigrationService) return this.dependencies.get<MigrationService>(TYPES.MigrationService)
} }

View File

@@ -130,6 +130,7 @@ import {
FullyResolvedApplicationOptions, FullyResolvedApplicationOptions,
GetHost, GetHost,
SetHost, SetHost,
GenerateUuid,
} from '@standardnotes/services' } from '@standardnotes/services'
import { ItemManager } from '../../Services/Items/ItemManager' import { ItemManager } from '../../Services/Items/ItemManager'
import { PayloadManager } from '../../Services/Payloads/PayloadManager' import { PayloadManager } from '../../Services/Payloads/PayloadManager'
@@ -214,6 +215,10 @@ export class Dependencies {
} }
private registerUseCaseMakers() { private registerUseCaseMakers() {
this.factory.set(TYPES.GenerateUuid, () => {
return new GenerateUuid(this.get<PureCryptoInterface>(TYPES.Crypto))
})
this.factory.set(TYPES.DecryptErroredPayloads, () => { this.factory.set(TYPES.DecryptErroredPayloads, () => {
return new DecryptErroredPayloads( return new DecryptErroredPayloads(
this.get<ItemsEncryptionService>(TYPES.ItemsEncryptionService), this.get<ItemsEncryptionService>(TYPES.ItemsEncryptionService),

View File

@@ -159,6 +159,7 @@ export const TYPES = {
ChangeAndSaveItem: Symbol.for('ChangeAndSaveItem'), ChangeAndSaveItem: Symbol.for('ChangeAndSaveItem'),
GetHost: Symbol.for('GetHost'), GetHost: Symbol.for('GetHost'),
SetHost: Symbol.for('SetHost'), SetHost: Symbol.for('SetHost'),
GenerateUuid: Symbol.for('GenerateUuid'),
// Mappers // Mappers
SessionStorageMapper: Symbol.for('SessionStorageMapper'), SessionStorageMapper: Symbol.for('SessionStorageMapper'),

View File

@@ -1,13 +1,18 @@
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
import { NativeFeatureIdentifier, NoteType } from '@standardnotes/features' import { NativeFeatureIdentifier, NoteType } from '@standardnotes/features'
import { AegisToAuthenticatorConverter } from './AegisToAuthenticatorConverter' import { AegisToAuthenticatorConverter } from './AegisToAuthenticatorConverter'
import data from './testData' import data from './testData'
import { UuidGenerator } from '@standardnotes/utils' import { GenerateUuid } from '@standardnotes/services'
UuidGenerator.SetGenerator(() => String(Math.random()))
describe('AegisConverter', () => { describe('AegisConverter', () => {
const crypto = {
generateUUID: () => String(Math.random()),
} as unknown as PureCryptoInterface
const generateUuid = new GenerateUuid(crypto)
it('should parse entries', () => { it('should parse entries', () => {
const converter = new AegisToAuthenticatorConverter() const converter = new AegisToAuthenticatorConverter(generateUuid)
const result = converter.parseEntries(data) const result = converter.parseEntries(data)
@@ -28,7 +33,7 @@ describe('AegisConverter', () => {
}) })
it('should create note from entries with editor info', () => { it('should create note from entries with editor info', () => {
const converter = new AegisToAuthenticatorConverter() const converter = new AegisToAuthenticatorConverter(generateUuid)
const parsedEntries = converter.parseEntries(data) const parsedEntries = converter.parseEntries(data)
@@ -55,7 +60,7 @@ describe('AegisConverter', () => {
}) })
it('should create note from entries without editor info', () => { it('should create note from entries without editor info', () => {
const converter = new AegisToAuthenticatorConverter() const converter = new AegisToAuthenticatorConverter(generateUuid)
const parsedEntries = converter.parseEntries(data) const parsedEntries = converter.parseEntries(data)

View File

@@ -2,7 +2,7 @@ import { DecryptedTransferPayload, NoteContent } from '@standardnotes/models'
import { readFileAsText } from '../Utils' import { readFileAsText } from '../Utils'
import { NativeFeatureIdentifier, NoteType } from '@standardnotes/features' import { NativeFeatureIdentifier, NoteType } from '@standardnotes/features'
import { ContentType } from '@standardnotes/domain-core' import { ContentType } from '@standardnotes/domain-core'
import { UuidGenerator } from '@standardnotes/utils' import { GenerateUuid } from '@standardnotes/services'
type AegisData = { type AegisData = {
db: { db: {
@@ -27,7 +27,7 @@ type AuthenticatorEntry = {
} }
export class AegisToAuthenticatorConverter { export class AegisToAuthenticatorConverter {
constructor() {} constructor(private _generateUuid: GenerateUuid) {}
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
static isValidAegisJson(json: any): boolean { static isValidAegisJson(json: any): boolean {
@@ -63,7 +63,7 @@ export class AegisToAuthenticatorConverter {
created_at_timestamp: file.lastModified, created_at_timestamp: file.lastModified,
updated_at: new Date(file.lastModified), updated_at: new Date(file.lastModified),
updated_at_timestamp: file.lastModified, updated_at_timestamp: file.lastModified,
uuid: UuidGenerator.GenerateUuid(), uuid: this._generateUuid.execute().getValue(),
content_type: ContentType.TYPES.Note, content_type: ContentType.TYPES.Note,
content: { content: {
title: file.name.split('.')[0], title: file.name.split('.')[0],

View File

@@ -6,7 +6,8 @@ import { ContentType } from '@standardnotes/domain-core'
import { DecryptedTransferPayload, NoteContent, TagContent } from '@standardnotes/models' import { DecryptedTransferPayload, NoteContent, TagContent } from '@standardnotes/models'
import { EvernoteConverter } from './EvernoteConverter' import { EvernoteConverter } from './EvernoteConverter'
import data from './testData' 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 // Mock dayjs so dayjs.extend() doesn't throw an error in EvernoteConverter.ts
jest.mock('dayjs', () => { jest.mock('dayjs', () => {
@@ -21,11 +22,17 @@ jest.mock('dayjs', () => {
} }
}) })
UuidGenerator.SetGenerator(() => String(Math.random()))
describe('EvernoteConverter', () => { describe('EvernoteConverter', () => {
const crypto = {
generateUUID: () => String(Math.random()),
} as unknown as PureCryptoInterface
const generateUuid = new GenerateUuid(crypto)
it('should parse and strip html', () => { it('should parse and strip html', () => {
const converter = new EvernoteConverter() const converter = new EvernoteConverter(generateUuid)
const result = converter.parseENEXData(data, true) const result = converter.parseENEXData(data, true)
@@ -45,7 +52,7 @@ describe('EvernoteConverter', () => {
}) })
it('should parse and not strip html', () => { it('should parse and not strip html', () => {
const converter = new EvernoteConverter() const converter = new EvernoteConverter(generateUuid)
const result = converter.parseENEXData(data, false) const result = converter.parseENEXData(data, false)

View File

@@ -4,14 +4,14 @@ import dayjs from 'dayjs'
import customParseFormat from 'dayjs/plugin/customParseFormat' import customParseFormat from 'dayjs/plugin/customParseFormat'
import utc from 'dayjs/plugin/utc' import utc from 'dayjs/plugin/utc'
import { ContentType } from '@standardnotes/domain-core' import { ContentType } from '@standardnotes/domain-core'
import { UuidGenerator } from '@standardnotes/utils' import { GenerateUuid } from '@standardnotes/services'
dayjs.extend(customParseFormat) dayjs.extend(customParseFormat)
dayjs.extend(utc) dayjs.extend(utc)
const dateFormat = 'YYYYMMDDTHHmmss' const dateFormat = 'YYYYMMDDTHHmmss'
export class EvernoteConverter { export class EvernoteConverter {
constructor() {} constructor(private _generateUuid: GenerateUuid) {}
async convertENEXFileToNotesAndTags(file: File, stripHTML: boolean): Promise<DecryptedTransferPayload[]> { async convertENEXFileToNotesAndTags(file: File, stripHTML: boolean): Promise<DecryptedTransferPayload[]> {
const content = await readFileAsText(file) const content = await readFileAsText(file)
@@ -35,7 +35,7 @@ export class EvernoteConverter {
created_at_timestamp: now.getTime(), created_at_timestamp: now.getTime(),
updated_at: now, updated_at: now,
updated_at_timestamp: now.getTime(), updated_at_timestamp: now.getTime(),
uuid: UuidGenerator.GenerateUuid(), uuid: this._generateUuid.execute().getValue(),
content_type: ContentType.TYPES.Tag, content_type: ContentType.TYPES.Tag,
content: { content: {
title: defaultTagName, title: defaultTagName,
@@ -88,7 +88,7 @@ export class EvernoteConverter {
created_at_timestamp: createdAtDate.getTime(), created_at_timestamp: createdAtDate.getTime(),
updated_at: updatedAtDate, updated_at: updatedAtDate,
updated_at_timestamp: updatedAtDate.getTime(), updated_at_timestamp: updatedAtDate.getTime(),
uuid: UuidGenerator.GenerateUuid(), uuid: this._generateUuid.execute().getValue(),
content_type: ContentType.TYPES.Note, content_type: ContentType.TYPES.Note,
content: { content: {
title: !title ? `Imported note ${index + 1} from Evernote` : title, title: !title ? `Imported note ${index + 1} from Evernote` : title,
@@ -111,7 +111,7 @@ export class EvernoteConverter {
if (!tag) { if (!tag) {
const now = new Date() const now = new Date()
tag = { tag = {
uuid: UuidGenerator.GenerateUuid(), uuid: this._generateUuid.execute().getValue(),
content_type: ContentType.TYPES.Tag, content_type: ContentType.TYPES.Tag,
created_at: now, created_at: now,
created_at_timestamp: now.getTime(), created_at_timestamp: now.getTime(),

View File

@@ -4,13 +4,18 @@
import { jsonTestData, htmlTestData } from './testData' import { jsonTestData, htmlTestData } from './testData'
import { GoogleKeepConverter } from './GoogleKeepConverter' import { GoogleKeepConverter } from './GoogleKeepConverter'
import { UuidGenerator } from '@standardnotes/utils' import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
import { GenerateUuid } from '@standardnotes/services'
UuidGenerator.SetGenerator(() => String(Math.random()))
describe('GoogleKeepConverter', () => { describe('GoogleKeepConverter', () => {
const crypto = {
generateUUID: () => String(Math.random()),
} as unknown as PureCryptoInterface
const generateUuid = new GenerateUuid(crypto)
it('should parse json data', () => { it('should parse json data', () => {
const converter = new GoogleKeepConverter() const converter = new GoogleKeepConverter(generateUuid)
const result = converter.tryParseAsJson(jsonTestData) const result = converter.tryParseAsJson(jsonTestData)
@@ -27,7 +32,7 @@ describe('GoogleKeepConverter', () => {
}) })
it('should parse html data', () => { it('should parse html data', () => {
const converter = new GoogleKeepConverter() const converter = new GoogleKeepConverter(generateUuid)
const result = converter.tryParseAsHtml( const result = converter.tryParseAsHtml(
htmlTestData, htmlTestData,

View File

@@ -1,7 +1,7 @@
import { ContentType } from '@standardnotes/domain-core' import { ContentType } from '@standardnotes/domain-core'
import { DecryptedTransferPayload, NoteContent } from '@standardnotes/models' import { DecryptedTransferPayload, NoteContent } from '@standardnotes/models'
import { readFileAsText } from '../Utils' import { readFileAsText } from '../Utils'
import { UuidGenerator } from '@standardnotes/utils' import { GenerateUuid } from '@standardnotes/services'
type GoogleKeepJsonNote = { type GoogleKeepJsonNote = {
color: string color: string
@@ -14,7 +14,7 @@ type GoogleKeepJsonNote = {
} }
export class GoogleKeepConverter { export class GoogleKeepConverter {
constructor() {} constructor(private _generateUuid: GenerateUuid) {}
async convertGoogleKeepBackupFileToNote( async convertGoogleKeepBackupFileToNote(
file: File, file: File,
@@ -66,7 +66,7 @@ export class GoogleKeepConverter {
created_at_timestamp: date.getTime(), created_at_timestamp: date.getTime(),
updated_at: date, updated_at: date,
updated_at_timestamp: date.getTime(), updated_at_timestamp: date.getTime(),
uuid: UuidGenerator.GenerateUuid(), uuid: this._generateUuid.execute().getValue(),
content_type: ContentType.TYPES.Note, content_type: ContentType.TYPES.Note,
content: { content: {
title: title, title: title,
@@ -121,7 +121,7 @@ export class GoogleKeepConverter {
created_at_timestamp: date.getTime(), created_at_timestamp: date.getTime(),
updated_at: date, updated_at: date,
updated_at_timestamp: date.getTime(), updated_at_timestamp: date.getTime(),
uuid: UuidGenerator.GenerateUuid(), uuid: this._generateUuid.execute().getValue(),
content_type: ContentType.TYPES.Note, content_type: ContentType.TYPES.Note,
content: { content: {
title: parsed.title, title: parsed.title,

View File

@@ -2,6 +2,7 @@ import { parseFileName } from '@standardnotes/filepicker'
import { import {
FeatureStatus, FeatureStatus,
FeaturesClientInterface, FeaturesClientInterface,
GenerateUuid,
ItemManagerInterface, ItemManagerInterface,
MutatorClientInterface, MutatorClientInterface,
} from '@standardnotes/services' } from '@standardnotes/services'
@@ -27,12 +28,13 @@ export class Importer {
private features: FeaturesClientInterface, private features: FeaturesClientInterface,
private mutator: MutatorClientInterface, private mutator: MutatorClientInterface,
private items: ItemManagerInterface, private items: ItemManagerInterface,
_generateUuid: GenerateUuid,
) { ) {
this.aegisConverter = new AegisToAuthenticatorConverter() this.aegisConverter = new AegisToAuthenticatorConverter(_generateUuid)
this.googleKeepConverter = new GoogleKeepConverter() this.googleKeepConverter = new GoogleKeepConverter(_generateUuid)
this.simplenoteConverter = new SimplenoteConverter() this.simplenoteConverter = new SimplenoteConverter(_generateUuid)
this.plaintextConverter = new PlaintextConverter() this.plaintextConverter = new PlaintextConverter(_generateUuid)
this.evernoteConverter = new EvernoteConverter() this.evernoteConverter = new EvernoteConverter(_generateUuid)
} }
static detectService = async (file: File): Promise<NoteImportType | null> => { static detectService = async (file: File): Promise<NoteImportType | null> => {

View File

@@ -2,9 +2,11 @@ import { ContentType } from '@standardnotes/domain-core'
import { parseFileName } from '@standardnotes/filepicker' import { parseFileName } from '@standardnotes/filepicker'
import { DecryptedTransferPayload, NoteContent } from '@standardnotes/models' import { DecryptedTransferPayload, NoteContent } from '@standardnotes/models'
import { readFileAsText } from '../Utils' import { readFileAsText } from '../Utils'
import { UuidGenerator } from '@standardnotes/utils' import { GenerateUuid } from '@standardnotes/services'
export class PlaintextConverter { export class PlaintextConverter {
constructor(private _generateUuid: GenerateUuid) {}
static isValidPlaintextFile(file: File): boolean { static isValidPlaintextFile(file: File): boolean {
return file.type === 'text/plain' || file.type === 'text/markdown' return file.type === 'text/plain' || file.type === 'text/markdown'
} }
@@ -22,7 +24,7 @@ export class PlaintextConverter {
created_at_timestamp: createdAtDate.getTime(), created_at_timestamp: createdAtDate.getTime(),
updated_at: updatedAtDate, updated_at: updatedAtDate,
updated_at_timestamp: updatedAtDate.getTime(), updated_at_timestamp: updatedAtDate.getTime(),
uuid: UuidGenerator.GenerateUuid(), uuid: this._generateUuid.execute().getValue(),
content_type: ContentType.TYPES.Note, content_type: ContentType.TYPES.Note,
content: { content: {
title: name, title: name,

View File

@@ -1,12 +1,17 @@
import { UuidGenerator } from '@standardnotes/utils' import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
import { SimplenoteConverter } from './SimplenoteConverter' import { SimplenoteConverter } from './SimplenoteConverter'
import data from './testData' import data from './testData'
import { GenerateUuid } from '@standardnotes/services'
UuidGenerator.SetGenerator(() => String(Math.random()))
describe('SimplenoteConverter', () => { describe('SimplenoteConverter', () => {
const crypto = {
generateUUID: () => String(Math.random()),
} as unknown as PureCryptoInterface
const generateUuid = new GenerateUuid(crypto)
it('should parse', () => { it('should parse', () => {
const converter = new SimplenoteConverter() const converter = new SimplenoteConverter(generateUuid)
const result = converter.parse(data) const result = converter.parse(data)

View File

@@ -1,7 +1,7 @@
import { DecryptedTransferPayload, NoteContent } from '@standardnotes/models' import { DecryptedTransferPayload, NoteContent } from '@standardnotes/models'
import { ContentType } from '@standardnotes/domain-core' import { ContentType } from '@standardnotes/domain-core'
import { readFileAsText } from '../Utils' import { readFileAsText } from '../Utils'
import { UuidGenerator } from '@standardnotes/utils' import { GenerateUuid } from '@standardnotes/services'
type SimplenoteItem = { type SimplenoteItem = {
creationDate: string creationDate: string
@@ -18,7 +18,7 @@ type SimplenoteData = {
const isSimplenoteEntry = (entry: any): boolean => entry.id && entry.content && entry.creationDate && entry.lastModified const isSimplenoteEntry = (entry: any): boolean => entry.id && entry.content && entry.creationDate && entry.lastModified
export class SimplenoteConverter { export class SimplenoteConverter {
constructor() {} constructor(private _generateUuid: GenerateUuid) {}
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
static isValidSimplenoteJson(json: any): boolean { static isValidSimplenoteJson(json: any): boolean {
@@ -55,7 +55,7 @@ export class SimplenoteConverter {
created_at_timestamp: createdAtDate.getTime(), created_at_timestamp: createdAtDate.getTime(),
updated_at: updatedAtDate, updated_at: updatedAtDate,
updated_at_timestamp: updatedAtDate.getTime(), updated_at_timestamp: updatedAtDate.getTime(),
uuid: UuidGenerator.GenerateUuid(), uuid: this._generateUuid.execute().getValue(),
content_type: ContentType.TYPES.Note, content_type: ContentType.TYPES.Note,
content: { content: {
title, title,

View File

@@ -56,7 +56,7 @@ export class WebDependencies extends DependencyContainer {
super() super()
this.bind(Web_TYPES.Importer, () => { 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, () => { this.bind(Web_TYPES.IsNativeIOS, () => {