feat: add models package

This commit is contained in:
Karol Sójko
2022-07-05 20:47:11 +02:00
parent 60d1554ff7
commit b614c71e79
199 changed files with 8772 additions and 22 deletions

View File

@@ -0,0 +1,75 @@
import { ConflictStrategy } from './../../Abstract/Item/Types/ConflictStrategy'
import { ContentType } from '@standardnotes/common'
import { FillItemContent } from '../../Abstract/Content/ItemContent'
import { DecryptedPayload, PayloadTimestampDefaults } from '../../Abstract/Payload'
import { FileContent, FileItem } from './File'
import { UuidGenerator } from '@standardnotes/utils'
UuidGenerator.SetGenerator(() => String(Math.random()))
describe('file', () => {
const createFile = (content: Partial<FileContent> = {}): FileItem => {
return new FileItem(
new DecryptedPayload<FileContent>({
uuid: '123',
content_type: ContentType.File,
content: FillItemContent<FileContent>({
name: 'name.png',
key: 'secret',
remoteIdentifier: 'A',
encryptionHeader: 'header',
encryptedChunkSizes: [1, 2, 3],
...content,
}),
dirty: true,
...PayloadTimestampDefaults(),
}),
)
}
const copyFile = (file: FileItem, override: Partial<FileContent> = {}): FileItem => {
return new FileItem(
file.payload.copy({
content: {
...file.content,
...override,
} as FileContent,
}),
)
}
it('should not copy on name conflict', () => {
const file = createFile({ name: 'file.png' })
const conflictedFile = copyFile(file, { name: 'different.png' })
expect(file.strategyWhenConflictingWithItem(conflictedFile)).toEqual(ConflictStrategy.KeepBase)
})
it('should copy on key conflict', () => {
const file = createFile({ name: 'file.png' })
const conflictedFile = copyFile(file, { key: 'different-secret' })
expect(file.strategyWhenConflictingWithItem(conflictedFile)).toEqual(ConflictStrategy.KeepBaseDuplicateApply)
})
it('should copy on header conflict', () => {
const file = createFile({ name: 'file.png' })
const conflictedFile = copyFile(file, { encryptionHeader: 'different-header' })
expect(file.strategyWhenConflictingWithItem(conflictedFile)).toEqual(ConflictStrategy.KeepBaseDuplicateApply)
})
it('should copy on identifier conflict', () => {
const file = createFile({ name: 'file.png' })
const conflictedFile = copyFile(file, { remoteIdentifier: 'different-identifier' })
expect(file.strategyWhenConflictingWithItem(conflictedFile)).toEqual(ConflictStrategy.KeepBaseDuplicateApply)
})
it('should copy on chunk sizes conflict', () => {
const file = createFile({ name: 'file.png' })
const conflictedFile = copyFile(file, { encryptedChunkSizes: [10, 9, 8] })
expect(file.strategyWhenConflictingWithItem(conflictedFile)).toEqual(ConflictStrategy.KeepBaseDuplicateApply)
})
})

View File

@@ -0,0 +1,85 @@
import { DecryptedItem } from '../../Abstract/Item/Implementations/DecryptedItem'
import { ItemContent } from '../../Abstract/Content/ItemContent'
import { DecryptedPayloadInterface } from '../../Abstract/Payload/Interfaces/DecryptedPayload'
import { FileMetadata } from './FileMetadata'
import { FileProtocolV1 } from './FileProtocolV1'
import { SortableItem } from '../../Runtime/Collection/CollectionSort'
import { ConflictStrategy } from '../../Abstract/Item'
type EncryptedBytesLength = number
type DecryptedBytesLength = number
interface SizesDeprecatedDueToAmbiguousNaming {
size?: DecryptedBytesLength
chunkSizes?: EncryptedBytesLength[]
}
interface Sizes {
decryptedSize: DecryptedBytesLength
encryptedChunkSizes: EncryptedBytesLength[]
}
interface FileContentWithoutSize {
remoteIdentifier: string
name: string
key: string
encryptionHeader: string
mimeType: string
}
export type FileContentSpecialized = FileContentWithoutSize & FileMetadata & SizesDeprecatedDueToAmbiguousNaming & Sizes
export type FileContent = FileContentSpecialized & ItemContent
export class FileItem
extends DecryptedItem<FileContent>
implements FileContentWithoutSize, Sizes, FileProtocolV1, FileMetadata, SortableItem
{
public readonly remoteIdentifier: string
public readonly name: string
public readonly key: string
public readonly encryptionHeader: string
public readonly mimeType: string
public readonly decryptedSize: DecryptedBytesLength
public readonly encryptedChunkSizes: EncryptedBytesLength[]
constructor(payload: DecryptedPayloadInterface<FileContent>) {
super(payload)
this.remoteIdentifier = this.content.remoteIdentifier
this.name = this.content.name
this.key = this.content.key
if (this.content.size && this.content.chunkSizes) {
this.decryptedSize = this.content.size
this.encryptedChunkSizes = this.content.chunkSizes
} else {
this.decryptedSize = this.content.decryptedSize
this.encryptedChunkSizes = this.content.encryptedChunkSizes
}
this.encryptionHeader = this.content.encryptionHeader
this.mimeType = this.content.mimeType
}
public override strategyWhenConflictingWithItem(item: FileItem): ConflictStrategy {
if (
item.key !== this.key ||
item.encryptionHeader !== this.encryptionHeader ||
item.remoteIdentifier !== this.remoteIdentifier ||
JSON.stringify(item.encryptedChunkSizes) !== JSON.stringify(this.encryptedChunkSizes)
) {
return ConflictStrategy.KeepBaseDuplicateApply
}
return ConflictStrategy.KeepBase
}
public get encryptedSize(): number {
return this.encryptedChunkSizes.reduce((total, chunk) => total + chunk, 0)
}
public get title(): string {
return this.name
}
}

View File

@@ -0,0 +1,4 @@
export interface FileMetadata {
name: string
mimeType: string
}

View File

@@ -0,0 +1,33 @@
import { ContentType } from '@standardnotes/common'
import { SNNote } from '../Note/Note'
import { FileContent } from './File'
import { FileToNoteReference } from '../../Abstract/Reference/FileToNoteReference'
import { ContenteReferenceType } from '../../Abstract/Reference/ContenteReferenceType'
import { DecryptedItemMutator } from '../../Abstract/Item/Mutator/DecryptedItemMutator'
export class FileMutator extends DecryptedItemMutator<FileContent> {
set name(newName: string) {
this.mutableContent.name = newName
}
set encryptionHeader(encryptionHeader: string) {
this.mutableContent.encryptionHeader = encryptionHeader
}
public addNote(note: SNNote): void {
const reference: FileToNoteReference = {
reference_type: ContenteReferenceType.FileToNote,
content_type: ContentType.Note,
uuid: note.uuid,
}
const references = this.mutableContent.references || []
references.push(reference)
this.mutableContent.references = references
}
public removeNote(note: SNNote): void {
const references = this.immutableItem.references.filter((ref) => ref.uuid !== note.uuid)
this.mutableContent.references = references
}
}

View File

@@ -0,0 +1,9 @@
export interface FileProtocolV1 {
readonly encryptionHeader: string
readonly key: string
readonly remoteIdentifier: string
}
export enum FileProtocolV1Constants {
KeySize = 256,
}

View File

@@ -0,0 +1,4 @@
export * from './File'
export * from './FileMutator'
export * from './FileMetadata'
export * from './FileProtocolV1'