mirror of
https://github.com/standardnotes/server
synced 2026-01-28 02:01:09 -05:00
Compare commits
6 Commits
@standardn
...
@standardn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47119fb346 | ||
|
|
d77eb7f5f1 | ||
|
|
1b0a2bb34c | ||
|
|
a363039fa1 | ||
|
|
32c740b58e | ||
|
|
822ee890af |
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.11.6](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.5...@standardnotes/analytics@2.11.6) (2022-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.11.5](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.4...@standardnotes/analytics@2.11.5) (2022-11-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/analytics",
|
||||
"version": "2.11.5",
|
||||
"version": "2.11.6",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.38.9](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.38.8...@standardnotes/api-gateway@1.38.9) (2022-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.38.8](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.38.7...@standardnotes/api-gateway@1.38.8) (2022-11-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.38.8",
|
||||
"version": "1.38.9",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.60.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.60.3...@standardnotes/auth-server@1.60.4) (2022-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.60.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.60.2...@standardnotes/auth-server@1.60.3) (2022-11-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/auth-server",
|
||||
"version": "1.60.3",
|
||||
"version": "1.60.4",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.46.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.45.0...@standardnotes/common@1.46.0) (2022-11-22)
|
||||
|
||||
### Features
|
||||
|
||||
* **common:** add marketing campaign for black friday 2022 email message identifier ([d77eb7f](https://github.com/standardnotes/server/commit/d77eb7f5f11bcc7cd5c6fa6d20e891b466af7b45))
|
||||
|
||||
# [1.45.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.44.4...@standardnotes/common@1.45.0) (2022-11-14)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/common",
|
||||
"version": "1.45.0",
|
||||
"version": "1.46.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -18,6 +18,7 @@ export enum EmailMessageIdentifier {
|
||||
STUDENT_DISCOUNT_REQUESTED = 'STUDENT_DISCOUNT_REQUESTED',
|
||||
STUDENT_DISCOUNT_APPROVED = 'STUDENT_DISCOUNT_APPROVED',
|
||||
MARKETING_CAMPAIGN_FILES = 'MARKETING_CAMPAIGN_FILES',
|
||||
MARKETING_BLACK_FRIDAY_2022 = 'MARKETING_BLACK_FRIDAY_2022',
|
||||
PAYMENT_FAILED = 'PAYMENT_FAILED',
|
||||
SEND_INVOICE = 'SEND_INVOICE',
|
||||
DISCOUNT_NOTICE = 'DISCOUNT_NOTICE',
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.2.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.2.1...@standardnotes/domain-core@1.2.2) (2022-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-core
|
||||
|
||||
## [1.2.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.2.0...@standardnotes/domain-core@1.2.1) (2022-11-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-core",
|
||||
"version": "1.2.1",
|
||||
"version": "1.2.2",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.9.31](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.9.30...@standardnotes/domain-events-infra@1.9.31) (2022-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.9.30](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.9.29...@standardnotes/domain-events-infra@1.9.30) (2022-11-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events-infra",
|
||||
"version": "1.9.30",
|
||||
"version": "1.9.31",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.90.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.90.0...@standardnotes/domain-events@2.90.1) (2022-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events
|
||||
|
||||
# [2.90.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.89.0...@standardnotes/domain-events@2.90.0) (2022-11-21)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events",
|
||||
"version": "2.90.0",
|
||||
"version": "2.90.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.6.26](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.6.25...@standardnotes/event-store@1.6.26) (2022-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.6.25](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.6.24...@standardnotes/event-store@1.6.25) (2022-11-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/event-store",
|
||||
"version": "1.6.25",
|
||||
"version": "1.6.26",
|
||||
"description": "Event Store Service",
|
||||
"private": true,
|
||||
"main": "dist/src/index.js",
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.8.26](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.8.25...@standardnotes/files-server@1.8.26) (2022-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.8.25](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.8.24...@standardnotes/files-server@1.8.25) (2022-11-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files-server",
|
||||
"version": "1.8.25",
|
||||
"version": "1.8.26",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.6.1](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.6.0...@standardnotes/predicates@1.6.1) (2022-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/predicates
|
||||
|
||||
# [1.6.0](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.5.7...@standardnotes/predicates@1.6.0) (2022-11-14)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/predicates",
|
||||
"version": "1.6.0",
|
||||
"version": "1.6.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.2.2](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.2.1...@standardnotes/revisions-server@1.2.2) (2022-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
## [1.2.1](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.2.0...@standardnotes/revisions-server@1.2.1) (2022-11-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **revisions:** add missing worker process ([a363039](https://github.com/standardnotes/server/commit/a363039fa1f1c75842d1eaba2a476257eba385f7))
|
||||
|
||||
# [1.2.0](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.1.3...@standardnotes/revisions-server@1.2.0) (2022-11-21)
|
||||
|
||||
### Features
|
||||
|
||||
* **revisions:** add persisting revisions from s3 dump ([822ee89](https://github.com/standardnotes/server/commit/822ee890aff80cd099fc67b778ee02b8e9ef40eb))
|
||||
|
||||
## [1.1.3](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.1.2...@standardnotes/revisions-server@1.1.3) (2022-11-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
25
packages/revisions/bin/worker.ts
Normal file
25
packages/revisions/bin/worker.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import 'newrelic'
|
||||
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||
import TYPES from '../src/Bootstrap/Types'
|
||||
import { Env } from '../src/Bootstrap/Env'
|
||||
import { DomainEventSubscriberFactoryInterface } from '@standardnotes/domain-events'
|
||||
|
||||
const container = new ContainerConfigLoader()
|
||||
void container.load().then((container) => {
|
||||
const env: Env = new Env()
|
||||
env.load()
|
||||
|
||||
const logger: Logger = container.get(TYPES.Logger)
|
||||
|
||||
logger.info('Starting worker...')
|
||||
|
||||
const subscriberFactory: DomainEventSubscriberFactoryInterface = container.get(TYPES.DomainEventSubscriberFactory)
|
||||
subscriberFactory.create().start()
|
||||
|
||||
setInterval(() => logger.info('Alive and kicking!'), 20 * 60 * 1000)
|
||||
})
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/revisions-server",
|
||||
"version": "1.1.3",
|
||||
"version": "1.2.2",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -29,6 +29,12 @@ import { MySQLRevisionRepository } from '../Infra/MySQL/MySQLRevisionRepository'
|
||||
import { RevisionMetadataPersistenceMapper } from '../Mapping/RevisionMetadataPersistenceMapper'
|
||||
import { TypeORMRevision } from '../Infra/TypeORM/TypeORMRevision'
|
||||
import { RevisionMetadata } from '../Domain/Revision/RevisionMetadata'
|
||||
import { Revision } from '../Domain/Revision/Revision'
|
||||
import { RevisionItemStringMapper } from '../Mapping/RevisionItemStringMapper'
|
||||
import { RevisionPersistenceMapper } from '../Mapping/RevisionPersistenceMapper'
|
||||
import { ItemDumpedEventHandler } from '../Domain/Handler/ItemDumpedEventHandler'
|
||||
import { DumpRepositoryInterface } from '../Domain/Dump/DumpRepositoryInterface'
|
||||
import { S3DumpRepository } from '../Infra/S3/S3ItemDumpRepository'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const newrelicFormatter = require('@newrelic/winston-enricher')
|
||||
@@ -88,22 +94,18 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind<MapperInterface<RevisionMetadata, TypeORMRevision>>(TYPES.RevisionMetadataPersistenceMapper)
|
||||
.toConstantValue(new RevisionMetadataPersistenceMapper())
|
||||
container
|
||||
.bind<MapperInterface<Revision, TypeORMRevision>>(TYPES.RevisionPersistenceMapper)
|
||||
.toConstantValue(new RevisionPersistenceMapper())
|
||||
container
|
||||
.bind<MapperInterface<Revision, string>>(TYPES.RevisionItemStringMapper)
|
||||
.toConstantValue(new RevisionItemStringMapper())
|
||||
|
||||
// ORM
|
||||
container
|
||||
.bind<Repository<TypeORMRevision>>(TYPES.ORMRevisionRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(TypeORMRevision))
|
||||
|
||||
// Repositories
|
||||
container
|
||||
.bind<RevisionRepositoryInterface>(TYPES.RevisionRepository)
|
||||
.toConstantValue(
|
||||
new MySQLRevisionRepository(
|
||||
container.get(TYPES.ORMRevisionRepository),
|
||||
container.get(TYPES.RevisionMetadataPersistenceMapper),
|
||||
),
|
||||
)
|
||||
|
||||
// env vars
|
||||
container.bind(TYPES.REDIS_URL).toConstantValue(env.get('REDIS_URL'))
|
||||
container.bind(TYPES.SQS_QUEUE_URL).toConstantValue(env.get('SQS_QUEUE_URL', true))
|
||||
@@ -114,6 +116,26 @@ export class ContainerConfigLoader {
|
||||
container.bind(TYPES.NEW_RELIC_ENABLED).toConstantValue(env.get('NEW_RELIC_ENABLED', true))
|
||||
container.bind(TYPES.VERSION).toConstantValue(env.get('VERSION'))
|
||||
|
||||
// Repositories
|
||||
container
|
||||
.bind<RevisionRepositoryInterface>(TYPES.RevisionRepository)
|
||||
.toConstantValue(
|
||||
new MySQLRevisionRepository(
|
||||
container.get(TYPES.ORMRevisionRepository),
|
||||
container.get(TYPES.RevisionMetadataPersistenceMapper),
|
||||
container.get(TYPES.RevisionPersistenceMapper),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<DumpRepositoryInterface>(TYPES.DumpRepository)
|
||||
.toConstantValue(
|
||||
new S3DumpRepository(
|
||||
container.get(TYPES.S3_BACKUP_BUCKET_NAME),
|
||||
container.get(TYPES.S3),
|
||||
container.get(TYPES.RevisionItemStringMapper),
|
||||
),
|
||||
)
|
||||
|
||||
// use cases
|
||||
container
|
||||
.bind<GetRevisionsMetada>(TYPES.GetRevisionsMetada)
|
||||
@@ -125,6 +147,11 @@ export class ContainerConfigLoader {
|
||||
.toConstantValue(new RevisionsController(container.get(TYPES.GetRevisionsMetada), container.get(TYPES.Logger)))
|
||||
|
||||
// Handlers
|
||||
container
|
||||
.bind<ItemDumpedEventHandler>(TYPES.ItemDumpedEventHandler)
|
||||
.toConstantValue(
|
||||
new ItemDumpedEventHandler(container.get(TYPES.DumpRepository), container.get(TYPES.RevisionRepository)),
|
||||
)
|
||||
|
||||
// Services
|
||||
container
|
||||
@@ -136,7 +163,9 @@ export class ContainerConfigLoader {
|
||||
.bind<InversifyExpressApiGatewayAuthMiddleware>(TYPES.ApiGatewayAuthMiddleware)
|
||||
.to(InversifyExpressApiGatewayAuthMiddleware)
|
||||
|
||||
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([])
|
||||
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
|
||||
['ITEM_DUMPED', container.get(TYPES.ItemDumpedEventHandler)],
|
||||
])
|
||||
|
||||
if (env.get('SQS_QUEUE_URL', true)) {
|
||||
container
|
||||
|
||||
@@ -6,10 +6,13 @@ const TYPES = {
|
||||
S3: Symbol.for('S3'),
|
||||
// Map
|
||||
RevisionMetadataPersistenceMapper: Symbol.for('RevisionMetadataPersistenceMapper'),
|
||||
RevisionPersistenceMapper: Symbol.for('RevisionPersistenceMapper'),
|
||||
RevisionItemStringMapper: Symbol.for('RevisionItemStringMapper'),
|
||||
// ORM
|
||||
ORMRevisionRepository: Symbol.for('ORMRevisionRepository'),
|
||||
// Repositories
|
||||
RevisionRepository: Symbol.for('RevisionRepository'),
|
||||
DumpRepository: Symbol.for('DumpRepository'),
|
||||
// env vars
|
||||
REDIS_URL: Symbol.for('REDIS_URL'),
|
||||
SQS_QUEUE_URL: Symbol.for('SQS_QUEUE_URL'),
|
||||
@@ -25,6 +28,7 @@ const TYPES = {
|
||||
// Controller
|
||||
RevisionsController: Symbol.for('RevisionsController'),
|
||||
// Handlers
|
||||
ItemDumpedEventHandler: Symbol.for('ItemDumpedEventHandler'),
|
||||
// Services
|
||||
CrossServiceTokenDecoder: Symbol.for('CrossServiceTokenDecoder'),
|
||||
DomainEventSubscriberFactory: Symbol.for('DomainEventSubscriberFactory'),
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Revision } from '../Revision/Revision'
|
||||
|
||||
export interface DumpRepositoryInterface {
|
||||
getRevisionFromDumpPath(path: string): Promise<Revision | null>
|
||||
removeDump(path: string): Promise<void>
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { ItemDumpedEvent } from '@standardnotes/domain-events'
|
||||
import { DumpRepositoryInterface } from '../Dump/DumpRepositoryInterface'
|
||||
import { Revision } from '../Revision/Revision'
|
||||
import { RevisionRepositoryInterface } from '../Revision/RevisionRepositoryInterface'
|
||||
import { ItemDumpedEventHandler } from './ItemDumpedEventHandler'
|
||||
|
||||
describe('ItemDumpedEventHandler', () => {
|
||||
let dumpRepository: DumpRepositoryInterface
|
||||
let revisionRepository: RevisionRepositoryInterface
|
||||
let revision: Revision
|
||||
let event: ItemDumpedEvent
|
||||
|
||||
const createHandler = () => new ItemDumpedEventHandler(dumpRepository, revisionRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
revision = {} as jest.Mocked<Revision>
|
||||
|
||||
dumpRepository = {} as jest.Mocked<DumpRepositoryInterface>
|
||||
dumpRepository.getRevisionFromDumpPath = jest.fn().mockReturnValue(revision)
|
||||
dumpRepository.removeDump = jest.fn()
|
||||
|
||||
revisionRepository = {} as jest.Mocked<RevisionRepositoryInterface>
|
||||
revisionRepository.save = jest.fn()
|
||||
|
||||
event = {} as jest.Mocked<ItemDumpedEvent>
|
||||
event.payload = {
|
||||
fileDumpPath: 'foobar',
|
||||
}
|
||||
})
|
||||
|
||||
it('should save a revision from file dump', async () => {
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(revisionRepository.save).toHaveBeenCalled()
|
||||
expect(dumpRepository.removeDump).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not save a revision if it could not be created from dump', async () => {
|
||||
dumpRepository.getRevisionFromDumpPath = jest.fn().mockReturnValue(null)
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(revisionRepository.save).not.toHaveBeenCalled()
|
||||
expect(dumpRepository.removeDump).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,24 @@
|
||||
import { DomainEventHandlerInterface, ItemDumpedEvent } from '@standardnotes/domain-events'
|
||||
|
||||
import { DumpRepositoryInterface } from '../Dump/DumpRepositoryInterface'
|
||||
import { RevisionRepositoryInterface } from '../Revision/RevisionRepositoryInterface'
|
||||
|
||||
export class ItemDumpedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
private dumpRepository: DumpRepositoryInterface,
|
||||
private revisionRepository: RevisionRepositoryInterface,
|
||||
) {}
|
||||
|
||||
async handle(event: ItemDumpedEvent): Promise<void> {
|
||||
const revision = await this.dumpRepository.getRevisionFromDumpPath(event.payload.fileDumpPath)
|
||||
if (revision === null) {
|
||||
await this.dumpRepository.removeDump(event.payload.fileDumpPath)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
await this.revisionRepository.save(revision)
|
||||
|
||||
await this.dumpRepository.removeDump(event.payload.fileDumpPath)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
import { Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { ContentType } from './ContentType'
|
||||
|
||||
@@ -10,6 +10,5 @@ export interface RevisionProps {
|
||||
encItemKey: string | null
|
||||
authHash: string | null
|
||||
creationDate: Date
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
timestamps: Timestamps
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { Revision } from './Revision'
|
||||
import { RevisionMetadata } from './RevisionMetadata'
|
||||
|
||||
export interface RevisionRepositoryInterface {
|
||||
findMetadataByItemId(itemUuid: Uuid): Promise<Array<RevisionMetadata>>
|
||||
save(revision: Revision): Promise<Revision>
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { MapperInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { Repository } from 'typeorm'
|
||||
import { Revision } from '../../Domain/Revision/Revision'
|
||||
|
||||
import { RevisionMetadata } from '../../Domain/Revision/RevisionMetadata'
|
||||
import { RevisionRepositoryInterface } from '../../Domain/Revision/RevisionRepositoryInterface'
|
||||
@@ -8,9 +9,18 @@ import { TypeORMRevision } from '../TypeORM/TypeORMRevision'
|
||||
export class MySQLRevisionRepository implements RevisionRepositoryInterface {
|
||||
constructor(
|
||||
private ormRepository: Repository<TypeORMRevision>,
|
||||
private revisionMapper: MapperInterface<RevisionMetadata, TypeORMRevision>,
|
||||
private revisionMetadataMapper: MapperInterface<RevisionMetadata, TypeORMRevision>,
|
||||
private revisionMapper: MapperInterface<Revision, TypeORMRevision>,
|
||||
) {}
|
||||
|
||||
async save(revision: Revision): Promise<Revision> {
|
||||
const typeormRevision = this.revisionMapper.toProjection(revision)
|
||||
|
||||
await this.ormRepository.save(typeormRevision)
|
||||
|
||||
return revision
|
||||
}
|
||||
|
||||
async findMetadataByItemId(itemUuid: Uuid): Promise<Array<RevisionMetadata>> {
|
||||
const queryBuilder = this.ormRepository
|
||||
.createQueryBuilder()
|
||||
@@ -27,7 +37,7 @@ export class MySQLRevisionRepository implements RevisionRepositoryInterface {
|
||||
|
||||
const metadata = []
|
||||
for (const simplifiedRevision of simplifiedRevisions) {
|
||||
metadata.push(this.revisionMapper.toDomain(simplifiedRevision))
|
||||
metadata.push(this.revisionMetadataMapper.toDomain(simplifiedRevision))
|
||||
}
|
||||
|
||||
return metadata
|
||||
|
||||
39
packages/revisions/src/Infra/S3/S3ItemDumpRepository.ts
Normal file
39
packages/revisions/src/Infra/S3/S3ItemDumpRepository.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { MapperInterface } from '@standardnotes/domain-core'
|
||||
import { S3 } from 'aws-sdk'
|
||||
|
||||
import { DumpRepositoryInterface } from '../../Domain/Dump/DumpRepositoryInterface'
|
||||
import { Revision } from '../../Domain/Revision/Revision'
|
||||
|
||||
export class S3DumpRepository implements DumpRepositoryInterface {
|
||||
constructor(
|
||||
private dumpBucketName: string,
|
||||
private s3Client: S3,
|
||||
private revisionStringItemMapper: MapperInterface<Revision, string>,
|
||||
) {}
|
||||
|
||||
async getRevisionFromDumpPath(path: string): Promise<Revision | null> {
|
||||
const s3Object = await this.s3Client
|
||||
.getObject({
|
||||
Bucket: this.dumpBucketName,
|
||||
Key: path,
|
||||
})
|
||||
.promise()
|
||||
|
||||
if (s3Object.Body === undefined) {
|
||||
return null
|
||||
}
|
||||
|
||||
const revision = this.revisionStringItemMapper.toDomain(s3Object.Body as string)
|
||||
|
||||
return revision
|
||||
}
|
||||
|
||||
async removeDump(path: string): Promise<void> {
|
||||
await this.s3Client
|
||||
.deleteObject({
|
||||
Bucket: this.dumpBucketName,
|
||||
Key: path,
|
||||
})
|
||||
.promise()
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
|
||||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
|
||||
|
||||
@Entity({ name: 'revisions' })
|
||||
@@ -25,7 +23,7 @@ export class TypeORMRevision {
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare contentType: ContentType | null
|
||||
declare contentType: string | null
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
|
||||
43
packages/revisions/src/Mapping/RevisionItemStringMapper.ts
Normal file
43
packages/revisions/src/Mapping/RevisionItemStringMapper.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { MapperInterface, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { ContentType } from '../Domain/Revision/ContentType'
|
||||
import { Revision } from '../Domain/Revision/Revision'
|
||||
|
||||
export class RevisionItemStringMapper implements MapperInterface<Revision, string> {
|
||||
toDomain(projection: string): Revision {
|
||||
const item = JSON.parse(projection)
|
||||
|
||||
const contentTypeOrError = ContentType.create(item.content_type)
|
||||
if (contentTypeOrError.isFailed()) {
|
||||
throw new Error(`Could not map item string to revision: ${contentTypeOrError.getError()}`)
|
||||
}
|
||||
const contentType = contentTypeOrError.getValue()
|
||||
|
||||
const itemUuidOrError = Uuid.create(item.uuid)
|
||||
if (itemUuidOrError.isFailed()) {
|
||||
throw new Error(`Could not map item string to revision: ${itemUuidOrError.getError()}`)
|
||||
}
|
||||
const itemUuid = itemUuidOrError.getValue()
|
||||
|
||||
const revisionOrError = Revision.create({
|
||||
itemUuid,
|
||||
authHash: item.auth_hash,
|
||||
content: item.content,
|
||||
contentType,
|
||||
itemsKeyId: item.items_key_id,
|
||||
encItemKey: item.enc_item_key,
|
||||
creationDate: new Date(),
|
||||
timestamps: Timestamps.create(new Date(), new Date()).getValue(),
|
||||
})
|
||||
|
||||
if (revisionOrError.isFailed()) {
|
||||
throw new Error(`Could not map item string to revision: ${revisionOrError.getError()}`)
|
||||
}
|
||||
|
||||
return revisionOrError.getValue()
|
||||
}
|
||||
|
||||
toProjection(domain: Revision): string {
|
||||
return JSON.stringify(domain)
|
||||
}
|
||||
}
|
||||
62
packages/revisions/src/Mapping/RevisionPersistenceMapper.ts
Normal file
62
packages/revisions/src/Mapping/RevisionPersistenceMapper.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { MapperInterface, Timestamps, UniqueEntityId, Uuid } from '@standardnotes/domain-core'
|
||||
import { ContentType } from '../Domain/Revision/ContentType'
|
||||
import { Revision } from '../Domain/Revision/Revision'
|
||||
import { TypeORMRevision } from '../Infra/TypeORM/TypeORMRevision'
|
||||
|
||||
export class RevisionPersistenceMapper implements MapperInterface<Revision, TypeORMRevision> {
|
||||
toDomain(projection: TypeORMRevision): Revision {
|
||||
const contentTypeOrError = ContentType.create(projection.contentType)
|
||||
if (contentTypeOrError.isFailed()) {
|
||||
throw new Error(`Could not map typeorm revision to domain revision: ${contentTypeOrError.getError()}`)
|
||||
}
|
||||
const contentType = contentTypeOrError.getValue()
|
||||
|
||||
const timestampsOrError = Timestamps.create(projection.createdAt, projection.updatedAt)
|
||||
if (timestampsOrError.isFailed()) {
|
||||
throw new Error(`Could not map typeorm revision to domain revision: ${timestampsOrError.getError()}`)
|
||||
}
|
||||
const timestamps = timestampsOrError.getValue()
|
||||
|
||||
const itemUuidOrError = Uuid.create(projection.itemUuid)
|
||||
if (itemUuidOrError.isFailed()) {
|
||||
throw new Error(`Could not map typeorm revision to domain revision: ${itemUuidOrError.getError()}`)
|
||||
}
|
||||
const itemUuid = itemUuidOrError.getValue()
|
||||
|
||||
const revisionOrError = Revision.create(
|
||||
{
|
||||
authHash: projection.authHash,
|
||||
content: projection.content,
|
||||
contentType,
|
||||
creationDate: projection.creationDate,
|
||||
encItemKey: projection.encItemKey,
|
||||
itemsKeyId: projection.itemsKeyId,
|
||||
itemUuid,
|
||||
timestamps,
|
||||
},
|
||||
new UniqueEntityId(projection.uuid),
|
||||
)
|
||||
if (revisionOrError.isFailed()) {
|
||||
throw new Error(`Could not map typeorm revision to domain revision: ${revisionOrError.getError()}`)
|
||||
}
|
||||
|
||||
return revisionOrError.getValue()
|
||||
}
|
||||
|
||||
toProjection(domain: Revision): TypeORMRevision {
|
||||
const typeormRevision = new TypeORMRevision()
|
||||
|
||||
typeormRevision.authHash = domain.props.authHash
|
||||
typeormRevision.content = domain.props.content
|
||||
typeormRevision.contentType = domain.props.contentType.value
|
||||
typeormRevision.createdAt = domain.props.timestamps.createdAt
|
||||
typeormRevision.updatedAt = domain.props.timestamps.updatedAt
|
||||
typeormRevision.creationDate = domain.props.creationDate
|
||||
typeormRevision.encItemKey = domain.props.encItemKey
|
||||
typeormRevision.itemUuid = domain.props.itemUuid.value
|
||||
typeormRevision.itemsKeyId = domain.props.itemsKeyId
|
||||
typeormRevision.uuid = domain.id.toString()
|
||||
|
||||
return typeormRevision
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.13.27](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.13.26...@standardnotes/scheduler-server@1.13.27) (2022-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.13.26](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.13.25...@standardnotes/scheduler-server@1.13.26) (2022-11-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/scheduler-server",
|
||||
"version": "1.13.26",
|
||||
"version": "1.13.27",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.7.1](https://github.com/standardnotes/server/compare/@standardnotes/security@1.7.0...@standardnotes/security@1.7.1) (2022-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/security
|
||||
|
||||
# [1.7.0](https://github.com/standardnotes/server/compare/@standardnotes/security@1.6.4...@standardnotes/security@1.7.0) (2022-11-14)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/security",
|
||||
"version": "1.7.0",
|
||||
"version": "1.7.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.16.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.16.0...@standardnotes/syncing-server@1.16.1) (2022-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
# [1.16.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.15.0...@standardnotes/syncing-server@1.16.0) (2022-11-21)
|
||||
|
||||
### Features
|
||||
|
||||
* **revisions:** add persisting revisions from s3 dump ([822ee89](https://github.com/standardnotes/syncing-server-js/commit/822ee890aff80cd099fc67b778ee02b8e9ef40eb))
|
||||
|
||||
# [1.15.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.14.0...@standardnotes/syncing-server@1.15.0) (2022-11-21)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/syncing-server",
|
||||
"version": "1.15.0",
|
||||
"version": "1.16.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -5,16 +5,15 @@ import {
|
||||
DomainEventService,
|
||||
ItemRevisionCreationRequestedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
|
||||
import { Item } from '../Item/Item'
|
||||
import { ItemRepositoryInterface } from '../Item/ItemRepositoryInterface'
|
||||
import { ItemRevisionCreationRequestedEventHandler } from './ItemRevisionCreationRequestedEventHandler'
|
||||
import { RevisionServiceInterface } from '../Revision/RevisionServiceInterface'
|
||||
import { ItemBackupServiceInterface } from '../Item/ItemBackupServiceInterface'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
|
||||
describe('ItemRevisionCreationRequestedEventHandler', () => {
|
||||
let itemRepository: ItemRepositoryInterface
|
||||
let revisionService: RevisionServiceInterface
|
||||
let event: ItemRevisionCreationRequestedEvent
|
||||
let item: Item
|
||||
let itemBackupService: ItemBackupServiceInterface
|
||||
@@ -24,7 +23,6 @@ describe('ItemRevisionCreationRequestedEventHandler', () => {
|
||||
const createHandler = () =>
|
||||
new ItemRevisionCreationRequestedEventHandler(
|
||||
itemRepository,
|
||||
revisionService,
|
||||
itemBackupService,
|
||||
domainEventFactory,
|
||||
domainEventPublisher,
|
||||
@@ -39,9 +37,6 @@ describe('ItemRevisionCreationRequestedEventHandler', () => {
|
||||
itemRepository = {} as jest.Mocked<ItemRepositoryInterface>
|
||||
itemRepository.findByUuid = jest.fn().mockReturnValue(item)
|
||||
|
||||
revisionService = {} as jest.Mocked<RevisionServiceInterface>
|
||||
revisionService.createRevision = jest.fn()
|
||||
|
||||
event = {} as jest.Mocked<ItemRevisionCreationRequestedEvent>
|
||||
event.createdAt = new Date(1)
|
||||
event.payload = {
|
||||
@@ -68,7 +63,6 @@ describe('ItemRevisionCreationRequestedEventHandler', () => {
|
||||
it('should create a revision for an item', async () => {
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(revisionService.createRevision).toHaveBeenCalled()
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(domainEventFactory.createItemDumpedEvent).toHaveBeenCalled()
|
||||
})
|
||||
@@ -78,7 +72,7 @@ describe('ItemRevisionCreationRequestedEventHandler', () => {
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(revisionService.createRevision).not.toHaveBeenCalled()
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not create a revision for an item if the dump was not created', async () => {
|
||||
|
||||
@@ -9,13 +9,11 @@ import TYPES from '../../Bootstrap/Types'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { ItemBackupServiceInterface } from '../Item/ItemBackupServiceInterface'
|
||||
import { ItemRepositoryInterface } from '../Item/ItemRepositoryInterface'
|
||||
import { RevisionServiceInterface } from '../Revision/RevisionServiceInterface'
|
||||
|
||||
@injectable()
|
||||
export class ItemRevisionCreationRequestedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
@inject(TYPES.ItemRepository) private itemRepository: ItemRepositoryInterface,
|
||||
@inject(TYPES.RevisionService) private revisionService: RevisionServiceInterface,
|
||||
@inject(TYPES.ItemBackupService) private itemBackupService: ItemBackupServiceInterface,
|
||||
@inject(TYPES.DomainEventFactory) private domainEventFactory: DomainEventFactoryInterface,
|
||||
@inject(TYPES.DomainEventPublisher) private domainEventPublisher: DomainEventPublisherInterface,
|
||||
@@ -33,7 +31,5 @@ export class ItemRevisionCreationRequestedEventHandler implements DomainEventHan
|
||||
this.domainEventFactory.createItemDumpedEvent(fileDumpPath, event.meta.correlation.userIdentifier),
|
||||
)
|
||||
}
|
||||
|
||||
await this.revisionService.createRevision(item)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.4.28](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.4.27...@standardnotes/websockets-server@1.4.28) (2022-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/websockets-server
|
||||
|
||||
## [1.4.27](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.4.26...@standardnotes/websockets-server@1.4.27) (2022-11-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/websockets-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/websockets-server",
|
||||
"version": "1.4.27",
|
||||
"version": "1.4.28",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.17.26](https://github.com/standardnotes/server/compare/@standardnotes/workspace-server@1.17.25...@standardnotes/workspace-server@1.17.26) (2022-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/workspace-server
|
||||
|
||||
## [1.17.25](https://github.com/standardnotes/server/compare/@standardnotes/workspace-server@1.17.24...@standardnotes/workspace-server@1.17.25) (2022-11-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/workspace-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/workspace-server",
|
||||
"version": "1.17.25",
|
||||
"version": "1.17.26",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user