mirror of
https://github.com/standardnotes/server
synced 2026-07-14 00:01:54 -04:00
fix(syncing-server): add missing messages and key system identifier sql representations (#663)
This commit is contained in:
@@ -78,4 +78,18 @@ describe('Validator', () => {
|
||||
expect(Validator.isNotEmpty(value).isFailed()).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
describe('is not empty string', () => {
|
||||
it('should not validate invalid string', () => {
|
||||
expect(Validator.isNotEmptyString(123 as unknown as string).isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should not validate an empty string', () => {
|
||||
expect(Validator.isNotEmptyString('').isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should validate a string', () => {
|
||||
expect(Validator.isNotEmptyString('foo').isFailed()).toBeFalsy()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -42,4 +42,18 @@ export class Validator {
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
|
||||
static isNotEmptyString(value: unknown): Result<string> {
|
||||
const isStringResult = Validator.isString(value)
|
||||
if (isStringResult.isFailed()) {
|
||||
return isStringResult
|
||||
}
|
||||
|
||||
const isNotEmptyResult = Validator.isNotEmpty(value)
|
||||
if (isNotEmptyResult.isFailed()) {
|
||||
return isNotEmptyResult
|
||||
}
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddMessages1689745128577 implements MigrationInterface {
|
||||
name = 'AddMessages1689745128577'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE `messages` (`uuid` varchar(36) NOT NULL, `recipient_uuid` varchar(36) NOT NULL, `sender_uuid` varchar(36) NOT NULL, `encrypted_message` text NOT NULL, `replaceability_identifier` varchar(255) NULL, `created_at_timestamp` bigint NOT NULL, `updated_at_timestamp` bigint NOT NULL, INDEX `recipient_uuid_on_messages` (`recipient_uuid`), INDEX `sender_uuid_on_messages` (`sender_uuid`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX `sender_uuid_on_messages` ON `messages`')
|
||||
await queryRunner.query('DROP INDEX `recipient_uuid_on_messages` ON `messages`')
|
||||
await queryRunner.query('DROP TABLE `messages`')
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class RenameKeyMessageIdentifier1689746180559 implements MigrationInterface {
|
||||
name = 'RenameKeyMessageIdentifier1689746180559'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX `key_system_uuid_on_key_system_associations` ON `key_system_associations`')
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE `key_system_associations` CHANGE `key_system_uuid` `key_system_identifier` varchar(36) NOT NULL',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'CREATE INDEX `key_system_identifier_on_key_system_associations` ON `key_system_associations` (`key_system_identifier`)',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'DROP INDEX `key_system_identifier_on_key_system_associations` ON `key_system_associations`',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE `key_system_associations` CHANGE `key_system_identifier` `key_system_uuid` varchar(36) NOT NULL',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'CREATE INDEX `key_system_uuid_on_key_system_associations` ON `key_system_associations` (`key_system_uuid`)',
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddMessages1689744778643 implements MigrationInterface {
|
||||
name = 'AddMessages1689744778643'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE "messages" ("uuid" varchar PRIMARY KEY NOT NULL, "recipient_uuid" varchar(36) NOT NULL, "sender_uuid" varchar(36) NOT NULL, "encrypted_message" text NOT NULL, "replaceability_identifier" varchar(255), "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL)',
|
||||
)
|
||||
await queryRunner.query('CREATE INDEX "recipient_uuid_on_messages" ON "messages" ("recipient_uuid") ')
|
||||
await queryRunner.query('CREATE INDEX "sender_uuid_on_messages" ON "messages" ("sender_uuid") ')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX "sender_uuid_on_messages"')
|
||||
await queryRunner.query('DROP INDEX "recipient_uuid_on_messages"')
|
||||
await queryRunner.query('DROP TABLE "messages"')
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class RenameKeyMessageIdentifier1689746527310 implements MigrationInterface {
|
||||
name = 'RenameKeyMessageIdentifier1689746527310'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX "key_system_uuid_on_key_system_associations"')
|
||||
await queryRunner.query('DROP INDEX "item_uuid_on_key_system_associations"')
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE "temporary_key_system_associations" ("uuid" varchar PRIMARY KEY NOT NULL, "key_system_identifier" varchar(36) NOT NULL, "item_uuid" varchar(36) NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL)',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'INSERT INTO "temporary_key_system_associations"("uuid", "key_system_identifier", "item_uuid", "created_at_timestamp", "updated_at_timestamp") SELECT "uuid", "key_system_uuid", "item_uuid", "created_at_timestamp", "updated_at_timestamp" FROM "key_system_associations"',
|
||||
)
|
||||
await queryRunner.query('DROP TABLE "key_system_associations"')
|
||||
await queryRunner.query('ALTER TABLE "temporary_key_system_associations" RENAME TO "key_system_associations"')
|
||||
await queryRunner.query(
|
||||
'CREATE INDEX "item_uuid_on_key_system_associations" ON "key_system_associations" ("item_uuid") ',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'CREATE INDEX "key_system_identifier_on_key_system_associations" ON "key_system_associations" ("key_system_identifier") ',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX "key_system_identifier_on_key_system_associations"')
|
||||
await queryRunner.query('DROP INDEX "item_uuid_on_key_system_associations"')
|
||||
await queryRunner.query('ALTER TABLE "key_system_associations" RENAME TO "temporary_key_system_associations"')
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE "key_system_associations" ("uuid" varchar PRIMARY KEY NOT NULL, "key_system_uuid" varchar(36) NOT NULL, "item_uuid" varchar(36) NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL)',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'INSERT INTO "key_system_associations"("uuid", "key_system_uuid", "item_uuid", "created_at_timestamp", "updated_at_timestamp") SELECT "uuid", "key_system_identifier", "item_uuid", "created_at_timestamp", "updated_at_timestamp" FROM "temporary_key_system_associations"',
|
||||
)
|
||||
await queryRunner.query('DROP TABLE "temporary_key_system_associations"')
|
||||
await queryRunner.query(
|
||||
'CREATE INDEX "item_uuid_on_key_system_associations" ON "key_system_associations" ("item_uuid") ',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'CREATE INDEX "key_system_uuid_on_key_system_associations" ON "key_system_associations" ("key_system_uuid") ',
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import { TypeORMKeySystemAssociation } from '../Infra/TypeORM/TypeORMKeySystemAs
|
||||
import { TypeORMSharedVault } from '../Infra/TypeORM/TypeORMSharedVault'
|
||||
import { TypeORMSharedVaultUser } from '../Infra/TypeORM/TypeORMSharedVaultUser'
|
||||
import { TypeORMSharedVaultInvite } from '../Infra/TypeORM/TypeORMSharedVaultInvite'
|
||||
import { TypeORMMessage } from '../Infra/TypeORM/TypeORMMessage'
|
||||
|
||||
export class AppDataSource {
|
||||
private _dataSource: DataSource | undefined
|
||||
@@ -46,6 +47,7 @@ export class AppDataSource {
|
||||
TypeORMSharedVault,
|
||||
TypeORMSharedVaultUser,
|
||||
TypeORMSharedVaultInvite,
|
||||
TypeORMMessage,
|
||||
],
|
||||
migrations: [`${__dirname}/../../migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
|
||||
migrationsRun: true,
|
||||
|
||||
@@ -2,6 +2,6 @@ import { Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
export interface KeySystemAssociationProps {
|
||||
itemUuid: Uuid
|
||||
keySystemUuid: Uuid
|
||||
keySystemIdentifier: string
|
||||
timestamps: Timestamps
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ describe('KeySystemAssociation', () => {
|
||||
const entityOrError = KeySystemAssociation.create({
|
||||
timestamps: Timestamps.create(123456789, 123456789).getValue(),
|
||||
itemUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
keySystemUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
keySystemIdentifier: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(entityOrError.isFailed()).toBeFalsy()
|
||||
|
||||
@@ -388,12 +388,12 @@ describe('SaveNewItem', () => {
|
||||
expect(itemRepository.save).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return a failure if the item hash has an invalid key system uuid', async () => {
|
||||
it('should return a failure if the item hash has an invalid key system identifier', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
itemHash1 = ItemHash.create({
|
||||
...itemHash1.props,
|
||||
key_system_identifier: '1-2-3',
|
||||
key_system_identifier: 123 as unknown as string,
|
||||
}).getValue()
|
||||
|
||||
const result = await useCase.execute({
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
UniqueEntityId,
|
||||
UseCaseInterface,
|
||||
Uuid,
|
||||
Validator,
|
||||
} from '@standardnotes/domain-core'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
@@ -111,11 +112,11 @@ export class SaveNewItem implements UseCaseInterface<Item> {
|
||||
|
||||
let keySystemAssociation = undefined
|
||||
if (dto.itemHash.hasDedicatedKeySystemAssociation()) {
|
||||
const keySystemUuidOrError = Uuid.create(dto.itemHash.props.key_system_identifier as string)
|
||||
if (keySystemUuidOrError.isFailed()) {
|
||||
return Result.fail(keySystemUuidOrError.getError())
|
||||
const keySystemIdentifiedValidationResult = Validator.isNotEmptyString(dto.itemHash.props.key_system_identifier)
|
||||
if (keySystemIdentifiedValidationResult.isFailed()) {
|
||||
return Result.fail(keySystemIdentifiedValidationResult.getError())
|
||||
}
|
||||
const keySystemUuid = keySystemUuidOrError.getValue()
|
||||
const keySystemIdentifier = dto.itemHash.props.key_system_identifier as string
|
||||
|
||||
const keySystemAssociationOrError = KeySystemAssociation.create({
|
||||
itemUuid: uuid,
|
||||
@@ -123,7 +124,7 @@ export class SaveNewItem implements UseCaseInterface<Item> {
|
||||
this.timer.getTimestampInMicroseconds(),
|
||||
this.timer.getTimestampInMicroseconds(),
|
||||
).getValue(),
|
||||
keySystemUuid,
|
||||
keySystemIdentifier,
|
||||
})
|
||||
if (keySystemAssociationOrError.isFailed()) {
|
||||
return Result.fail(keySystemAssociationOrError.getError())
|
||||
|
||||
+4
-4
@@ -385,7 +385,7 @@ describe('UpdateExistingItem', () => {
|
||||
})
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
expect(item1.props.keySystemAssociation).not.toBeUndefined()
|
||||
expect(item1.props.keySystemAssociation?.props.keySystemUuid.value).toBe('00000000-0000-0000-0000-000000000000')
|
||||
expect(item1.props.keySystemAssociation?.props.keySystemIdentifier).toBe('00000000-0000-0000-0000-000000000000')
|
||||
})
|
||||
|
||||
it('should not add a key system association if item hash has a dedicated key system and the existing item is already associated to the key system', async () => {
|
||||
@@ -398,7 +398,7 @@ describe('UpdateExistingItem', () => {
|
||||
|
||||
item1.props.keySystemAssociation = KeySystemAssociation.create({
|
||||
itemUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
keySystemUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
keySystemIdentifier: '00000000-0000-0000-0000-000000000000',
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
const idBefore = item1.props.keySystemAssociation?.id.toString()
|
||||
@@ -416,12 +416,12 @@ describe('UpdateExistingItem', () => {
|
||||
expect(item1.props.keySystemAssociation.id.toString()).toEqual(idBefore)
|
||||
})
|
||||
|
||||
it('should return error if key system uuid is invalid', async () => {
|
||||
it('should return error if key system identifier is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const itemHash = ItemHash.create({
|
||||
...itemHash1.props,
|
||||
key_system_identifier: 'invalid-uuid',
|
||||
key_system_identifier: 123 as unknown as string,
|
||||
}).getValue()
|
||||
|
||||
const result = await useCase.execute({
|
||||
|
||||
+7
-7
@@ -1,4 +1,4 @@
|
||||
import { ContentType, Dates, Result, Timestamps, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { ContentType, Dates, Result, Timestamps, UseCaseInterface, Uuid, Validator } from '@standardnotes/domain-core'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
@@ -135,11 +135,11 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
|
||||
dto.itemHash.hasDedicatedKeySystemAssociation() &&
|
||||
!this.itemIsAlreadyAssociatedWithTheKeySystem(dto.existingItem, dto.itemHash)
|
||||
) {
|
||||
const keySystemUuidOrError = Uuid.create(dto.itemHash.props.key_system_identifier as string)
|
||||
if (keySystemUuidOrError.isFailed()) {
|
||||
return Result.fail(keySystemUuidOrError.getError())
|
||||
const keySystemIdentifiedValidationResult = Validator.isNotEmptyString(dto.itemHash.props.key_system_identifier)
|
||||
if (keySystemIdentifiedValidationResult.isFailed()) {
|
||||
return Result.fail(keySystemIdentifiedValidationResult.getError())
|
||||
}
|
||||
const keySystemUuid = keySystemUuidOrError.getValue()
|
||||
const keySystemIdentifier = dto.itemHash.props.key_system_identifier as string
|
||||
|
||||
const keySystemAssociationOrError = KeySystemAssociation.create({
|
||||
itemUuid: Uuid.create(dto.existingItem.id.toString()).getValue(),
|
||||
@@ -147,7 +147,7 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
|
||||
this.timer.getTimestampInMicroseconds(),
|
||||
this.timer.getTimestampInMicroseconds(),
|
||||
).getValue(),
|
||||
keySystemUuid,
|
||||
keySystemIdentifier,
|
||||
})
|
||||
if (keySystemAssociationOrError.isFailed()) {
|
||||
return Result.fail(keySystemAssociationOrError.getError())
|
||||
@@ -203,7 +203,7 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
|
||||
private itemIsAlreadyAssociatedWithTheKeySystem(item: Item, itemHash: ItemHash): boolean {
|
||||
return (
|
||||
item.props.keySystemAssociation !== undefined &&
|
||||
item.props.keySystemAssociation.props.keySystemUuid.value === itemHash.props.key_system_identifier
|
||||
item.props.keySystemAssociation.props.keySystemIdentifier === itemHash.props.key_system_identifier
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ export class TypeORMKeySystemAssociation {
|
||||
declare uuid: string
|
||||
|
||||
@Column({
|
||||
name: 'key_system_uuid',
|
||||
name: 'key_system_identifier',
|
||||
length: 36,
|
||||
})
|
||||
@Index('key_system_uuid_on_key_system_associations')
|
||||
declare keySystemUuid: string
|
||||
@Index('key_system_identifier_on_key_system_associations')
|
||||
declare keySystemIdentifier: string
|
||||
|
||||
@Column({
|
||||
name: 'item_uuid',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
|
||||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
|
||||
|
||||
@Entity({ name: 'messages' })
|
||||
export class TypeORMMessage {
|
||||
@@ -9,12 +9,14 @@ export class TypeORMMessage {
|
||||
name: 'recipient_uuid',
|
||||
length: 36,
|
||||
})
|
||||
@Index('recipient_uuid_on_messages')
|
||||
declare recipientUuid: string
|
||||
|
||||
@Column({
|
||||
name: 'sender_uuid',
|
||||
length: 36,
|
||||
})
|
||||
@Index('sender_uuid_on_messages')
|
||||
declare senderUuid: string
|
||||
|
||||
@Column({
|
||||
|
||||
@@ -27,7 +27,7 @@ export class ItemHttpMapper implements MapperInterface<Item, ItemHttpRepresentat
|
||||
updated_at_timestamp: domain.props.timestamps.updatedAt,
|
||||
updated_with_session: domain.props.updatedWithSession ? domain.props.updatedWithSession.value : null,
|
||||
key_system_identifier: domain.props.keySystemAssociation
|
||||
? domain.props.keySystemAssociation.props.keySystemUuid.value
|
||||
? domain.props.keySystemAssociation.props.keySystemIdentifier
|
||||
: null,
|
||||
shared_vault_uuid: domain.props.sharedVaultAssociation
|
||||
? domain.props.sharedVaultAssociation.props.sharedVaultUuid.value
|
||||
|
||||
@@ -23,7 +23,7 @@ export class SavedItemHttpMapper implements MapperInterface<Item, SavedItemHttpR
|
||||
updated_at: this.timer.convertMicrosecondsToStringDate(domain.props.timestamps.updatedAt),
|
||||
updated_at_timestamp: domain.props.timestamps.updatedAt,
|
||||
key_system_identifier: domain.props.keySystemAssociation
|
||||
? domain.props.keySystemAssociation.props.keySystemUuid.value
|
||||
? domain.props.keySystemAssociation.props.keySystemIdentifier
|
||||
: null,
|
||||
shared_vault_uuid: domain.props.sharedVaultAssociation
|
||||
? domain.props.sharedVaultAssociation.props.sharedVaultUuid.value
|
||||
|
||||
+6
-7
@@ -1,4 +1,4 @@
|
||||
import { MapperInterface, Timestamps, UniqueEntityId, Uuid } from '@standardnotes/domain-core'
|
||||
import { MapperInterface, Timestamps, UniqueEntityId, Uuid, Validator } from '@standardnotes/domain-core'
|
||||
|
||||
import { KeySystemAssociation } from '../../Domain/KeySystem/KeySystemAssociation'
|
||||
|
||||
@@ -14,11 +14,10 @@ export class KeySystemAssociationPersistenceMapper
|
||||
}
|
||||
const itemUuid = itemUuidOrError.getValue()
|
||||
|
||||
const keySystemUuidOrError = Uuid.create(projection.keySystemUuid)
|
||||
if (keySystemUuidOrError.isFailed()) {
|
||||
throw new Error(`Failed to create key system from projection: ${keySystemUuidOrError.getError()}`)
|
||||
const keySystemIdentifiedValidationResult = Validator.isNotEmptyString(projection.keySystemIdentifier)
|
||||
if (keySystemIdentifiedValidationResult.isFailed()) {
|
||||
throw new Error(`Failed to create key system from projection: ${keySystemIdentifiedValidationResult.getError()}`)
|
||||
}
|
||||
const keySystemUuid = keySystemUuidOrError.getValue()
|
||||
|
||||
const timestampsOrError = Timestamps.create(projection.createdAtTimestamp, projection.updatedAtTimestamp)
|
||||
if (timestampsOrError.isFailed()) {
|
||||
@@ -30,7 +29,7 @@ export class KeySystemAssociationPersistenceMapper
|
||||
{
|
||||
itemUuid,
|
||||
timestamps,
|
||||
keySystemUuid,
|
||||
keySystemIdentifier: projection.keySystemIdentifier,
|
||||
},
|
||||
new UniqueEntityId(projection.uuid),
|
||||
)
|
||||
@@ -48,7 +47,7 @@ export class KeySystemAssociationPersistenceMapper
|
||||
|
||||
typeorm.uuid = domain.id.toString()
|
||||
typeorm.itemUuid = domain.props.itemUuid.value
|
||||
typeorm.keySystemUuid = domain.props.keySystemUuid.value
|
||||
typeorm.keySystemIdentifier = domain.props.keySystemIdentifier
|
||||
typeorm.createdAtTimestamp = domain.props.timestamps.createdAt
|
||||
typeorm.updatedAtTimestamp = domain.props.timestamps.updatedAt
|
||||
|
||||
|
||||
Reference in New Issue
Block a user