feat: add revisions service to home server (#613)
* feat: add revisions service to home server * fix: make e2e test suite on home server non-optional * fix(auth): specs
This commit is contained in:
parent
4b8a9e448a
commit
c70040fe5d
27 changed files with 573 additions and 483 deletions
4
.github/workflows/common-e2e.yml
vendored
4
.github/workflows/common-e2e.yml
vendored
|
@ -50,7 +50,7 @@ jobs:
|
|||
run: yarn dlx mocha-headless-chrome --timeout 1200000 -f http://localhost:9001/mocha/test.html
|
||||
|
||||
e2e-home-server:
|
||||
name: (WIP - Home Server) E2E Test Suite
|
||||
name: (Home Server) E2E Test Suite
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
|
@ -85,6 +85,7 @@ jobs:
|
|||
sed -i "s/PSEUDO_KEY_PARAMS_KEY=/PSEUDO_KEY_PARAMS_KEY=$(openssl rand -hex 32)/g" packages/home-server/.env
|
||||
echo "ACCESS_TOKEN_AGE=4" >> packages/home-server/.env
|
||||
echo "REFRESH_TOKEN_AGE=7" >> packages/home-server/.env
|
||||
echo "REVISIONS_FREQUENCY=5" >> packages/home-server/.env
|
||||
|
||||
- name: Run Server
|
||||
run: nohup yarn workspace @standardnotes/home-server start &
|
||||
|
@ -95,5 +96,4 @@ jobs:
|
|||
run: docker/is-available.sh http://localhost:3123 $(pwd)/logs
|
||||
|
||||
- name: Run E2E Test Suite
|
||||
continue-on-error: true
|
||||
run: yarn dlx mocha-headless-chrome --timeout 1200000 -f http://localhost:9001/mocha/test.html?skip_paid_features=true
|
||||
|
|
11
.pnp.cjs
generated
11
.pnp.cjs
generated
|
@ -4626,6 +4626,7 @@ const RAW_RUNTIME_STATE =
|
|||
["@standardnotes/auth-server", "workspace:packages/auth"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||
["@standardnotes/revisions-server", "workspace:packages/revisions"],\
|
||||
["@standardnotes/syncing-server", "workspace:packages/syncing-server"],\
|
||||
["@types/cors", "npm:2.8.13"],\
|
||||
["@types/express", "npm:4.17.17"],\
|
||||
|
@ -4733,7 +4734,7 @@ const RAW_RUNTIME_STATE =
|
|||
["reflect-metadata", "npm:0.1.13"],\
|
||||
["sqlite3", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:5.1.6"],\
|
||||
["ts-jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.1.0"],\
|
||||
["typeorm", "virtual:67ad3a1ca34e24ce4821cc48979e98af0c3e5dd7aabc7ad0b5d22d1d977d6f943f81c9f141a420105ebdc61ef777e508a96c7946081decd98f8c30543d468b33#npm:0.3.15"],\
|
||||
["typeorm", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:0.3.15"],\
|
||||
["typescript", "patch:typescript@npm%3A5.0.4#optional!builtin<compat/typescript>::version=5.0.4&hash=b5f058"],\
|
||||
["winston", "npm:3.8.2"]\
|
||||
],\
|
||||
|
@ -4925,7 +4926,7 @@ const RAW_RUNTIME_STATE =
|
|||
["reflect-metadata", "npm:0.1.13"],\
|
||||
["sqlite3", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:5.1.6"],\
|
||||
["ts-jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.1.0"],\
|
||||
["typeorm", "virtual:67ad3a1ca34e24ce4821cc48979e98af0c3e5dd7aabc7ad0b5d22d1d977d6f943f81c9f141a420105ebdc61ef777e508a96c7946081decd98f8c30543d468b33#npm:0.3.15"],\
|
||||
["typeorm", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:0.3.15"],\
|
||||
["typescript", "patch:typescript@npm%3A5.0.4#optional!builtin<compat/typescript>::version=5.0.4&hash=b5f058"],\
|
||||
["ua-parser-js", "npm:1.0.35"],\
|
||||
["uuid", "npm:9.0.0"],\
|
||||
|
@ -15166,10 +15167,10 @@ const RAW_RUNTIME_STATE =
|
|||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["virtual:67ad3a1ca34e24ce4821cc48979e98af0c3e5dd7aabc7ad0b5d22d1d977d6f943f81c9f141a420105ebdc61ef777e508a96c7946081decd98f8c30543d468b33#npm:0.3.15", {\
|
||||
"packageLocation": "./.yarn/__virtual__/typeorm-virtual-91f15b21d5/0/cache/typeorm-npm-0.3.15-20a6c4f754-db890f14cb.zip/node_modules/typeorm/",\
|
||||
["virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:0.3.15", {\
|
||||
"packageLocation": "./.yarn/__virtual__/typeorm-virtual-7fe891193c/0/cache/typeorm-npm-0.3.15-20a6c4f754-db890f14cb.zip/node_modules/typeorm/",\
|
||||
"packageDependencies": [\
|
||||
["typeorm", "virtual:67ad3a1ca34e24ce4821cc48979e98af0c3e5dd7aabc7ad0b5d22d1d977d6f943f81c9f141a420105ebdc61ef777e508a96c7946081decd98f8c30543d468b33#npm:0.3.15"],\
|
||||
["typeorm", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:0.3.15"],\
|
||||
["@google-cloud/spanner", null],\
|
||||
["@sap/hana-client", null],\
|
||||
["@sqltools/formatter", "npm:1.2.5"],\
|
||||
|
|
|
@ -28,7 +28,7 @@ export class RevisionsControllerV2 extends BaseHttpController {
|
|||
)
|
||||
}
|
||||
|
||||
@httpGet('/:id')
|
||||
@httpGet('/:uuid')
|
||||
async getRevision(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callRevisionsServer(
|
||||
request,
|
||||
|
@ -37,12 +37,12 @@ export class RevisionsControllerV2 extends BaseHttpController {
|
|||
'GET',
|
||||
'items/:itemUuid/revisions/:id',
|
||||
request.params.itemUuid,
|
||||
request.params.id,
|
||||
request.params.uuid,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@httpDelete('/:id')
|
||||
@httpDelete('/:uuid')
|
||||
async deleteRevision(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callRevisionsServer(
|
||||
request,
|
||||
|
@ -51,7 +51,7 @@ export class RevisionsControllerV2 extends BaseHttpController {
|
|||
'DELETE',
|
||||
'items/:itemUuid/revisions/:id',
|
||||
request.params.itemUuid,
|
||||
request.params.id,
|
||||
request.params.uuid,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -66,7 +66,12 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
|
|||
throw new Error('Revisions service not found')
|
||||
}
|
||||
|
||||
await service.handleRequest(request, response, endpointOrMethodIdentifier)
|
||||
const serviceResponse = (await service.handleRequest(request, response, endpointOrMethodIdentifier)) as {
|
||||
statusCode: number
|
||||
json: Record<string, unknown>
|
||||
}
|
||||
|
||||
this.sendDecoratedResponse(response, serviceResponse)
|
||||
}
|
||||
|
||||
async callSyncingServer(request: never, response: never, endpointOrMethodIdentifier: string): Promise<void> {
|
||||
|
|
|
@ -58,6 +58,10 @@ export class EndpointResolver implements EndpointResolverInterface {
|
|||
['[POST]:items/sync', 'sync.items.sync'],
|
||||
['[POST]:items/check-integrity', 'sync.items.check_integrity'],
|
||||
['[GET]:items/:uuid', 'sync.items.get_item'],
|
||||
// Revisions Controller V2
|
||||
['[GET]:items/:itemUuid/revisions', 'revisions.revisions.getRevisions'],
|
||||
['[GET]:items/:itemUuid/revisions/:id', 'revisions.revisions.getRevision'],
|
||||
['[DELETE]:items/:itemUuid/revisions/:id', 'revisions.revisions.deleteRevision'],
|
||||
])
|
||||
|
||||
resolveEndpointOrMethodIdentifier(method: string, endpoint: string, ...params: string[]): string {
|
||||
|
|
|
@ -64,16 +64,12 @@ describe('InversifyExpressSessionController', () => {
|
|||
},
|
||||
})
|
||||
|
||||
await createController().refresh(request, response)
|
||||
const httpResult = <results.JsonResult>await createController().refresh(request, response)
|
||||
const result = await httpResult.executeAsync()
|
||||
|
||||
expect(response.send).toHaveBeenCalledWith({
|
||||
session: {
|
||||
access_token: '1231',
|
||||
refresh_token: '2341',
|
||||
access_expiration: 123123,
|
||||
refresh_expiration: 123123,
|
||||
},
|
||||
})
|
||||
expect(await result.content.readAsStringAsync()).toEqual(
|
||||
'{"session":{"access_token":"1231","refresh_token":"2341","access_expiration":123123,"refresh_expiration":123123}}',
|
||||
)
|
||||
})
|
||||
|
||||
it('should return bad request if tokens are missing from refresh token request', async () => {
|
||||
|
@ -113,14 +109,15 @@ describe('InversifyExpressSessionController', () => {
|
|||
}
|
||||
request.body.uuid = '123'
|
||||
|
||||
await createController().deleteSession(request, response)
|
||||
const httpResult = <results.JsonResult>await createController().deleteSession(request, response)
|
||||
const result = await httpResult.executeAsync()
|
||||
|
||||
expect(deleteSessionForUser.execute).toBeCalledWith({
|
||||
userUuid: '123',
|
||||
sessionUuid: '123',
|
||||
})
|
||||
|
||||
expect(response.status).toHaveBeenCalledWith(204)
|
||||
expect(result.statusCode).toEqual(204)
|
||||
})
|
||||
|
||||
it('should not delete a specific session is current session has read only access', async () => {
|
||||
|
@ -205,15 +202,16 @@ describe('InversifyExpressSessionController', () => {
|
|||
uuid: '234',
|
||||
},
|
||||
}
|
||||
await createController().deleteAllSessions(request, response)
|
||||
|
||||
const httpResult = <results.JsonResult>await createController().deleteAllSessions(request, response)
|
||||
const result = await httpResult.executeAsync()
|
||||
|
||||
expect(deletePreviousSessionsForUser.execute).toHaveBeenCalledWith({
|
||||
userUuid: '123',
|
||||
currentSessionUuid: '234',
|
||||
})
|
||||
|
||||
expect(response.status).toHaveBeenCalledWith(204)
|
||||
expect(response.send).toHaveBeenCalled()
|
||||
expect(result.statusCode).toEqual(204)
|
||||
})
|
||||
|
||||
it('should not delete all sessions if current sessions has read only access', async () => {
|
||||
|
|
|
@ -32,7 +32,7 @@ export class InversifyExpressSessionController extends BaseHttpController {
|
|||
}
|
||||
|
||||
@httpDelete('/', TYPES.Auth_RequiredCrossServiceTokenMiddleware, TYPES.Auth_SessionMiddleware)
|
||||
async deleteSession(request: Request, response: Response): Promise<results.JsonResult | void> {
|
||||
async deleteSession(request: Request, response: Response): Promise<results.JsonResult | results.StatusCodeResult> {
|
||||
if (response.locals.readOnlyAccess) {
|
||||
return this.json(
|
||||
{
|
||||
|
@ -84,11 +84,15 @@ export class InversifyExpressSessionController extends BaseHttpController {
|
|||
}
|
||||
|
||||
response.setHeader('x-invalidate-cache', response.locals.user.uuid)
|
||||
response.status(204).send()
|
||||
|
||||
return this.statusCode(204)
|
||||
}
|
||||
|
||||
@httpDelete('/all', TYPES.Auth_RequiredCrossServiceTokenMiddleware, TYPES.Auth_SessionMiddleware)
|
||||
async deleteAllSessions(_request: Request, response: Response): Promise<results.JsonResult | void> {
|
||||
async deleteAllSessions(
|
||||
_request: Request,
|
||||
response: Response,
|
||||
): Promise<results.JsonResult | results.StatusCodeResult> {
|
||||
if (response.locals.readOnlyAccess) {
|
||||
return this.json(
|
||||
{
|
||||
|
@ -118,11 +122,12 @@ export class InversifyExpressSessionController extends BaseHttpController {
|
|||
})
|
||||
|
||||
response.setHeader('x-invalidate-cache', response.locals.user.uuid)
|
||||
response.status(204).send()
|
||||
|
||||
return this.statusCode(204)
|
||||
}
|
||||
|
||||
@httpPost('/refresh')
|
||||
async refresh(request: Request, response: Response): Promise<results.JsonResult | void> {
|
||||
async refresh(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
if (!request.body.access_token || !request.body.refresh_token) {
|
||||
return this.json(
|
||||
{
|
||||
|
@ -152,7 +157,7 @@ export class InversifyExpressSessionController extends BaseHttpController {
|
|||
}
|
||||
|
||||
response.setHeader('x-invalidate-cache', result.userUuid as string)
|
||||
response.send({
|
||||
return this.json({
|
||||
session: result.sessionPayload,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -32,14 +32,15 @@ export class InversifyExpressSubscriptionInvitesController extends BaseHttpContr
|
|||
}
|
||||
|
||||
@httpPost('/:inviteUuid/accept', TYPES.Auth_RequiredCrossServiceTokenMiddleware)
|
||||
async acceptInvite(request: Request, response: Response): Promise<void> {
|
||||
async acceptInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.subscriptionInvitesController.acceptInvite({
|
||||
api: request.query.api as ApiVersion,
|
||||
inviteUuid: request.params.inviteUuid,
|
||||
})
|
||||
|
||||
response.setHeader('x-invalidate-cache', response.locals.user.uuid)
|
||||
response.status(result.status).send(result.data)
|
||||
|
||||
return this.json(result.data, result.status)
|
||||
}
|
||||
|
||||
@httpGet('/:inviteUuid/decline')
|
||||
|
|
|
@ -99,7 +99,8 @@ describe('InversifyExpressUsersController', () => {
|
|||
|
||||
updateUser.execute = jest.fn().mockReturnValue({ success: true, authResponse: { foo: 'bar' } })
|
||||
|
||||
await createController().update(request, response)
|
||||
const httpResponse = <results.JsonResult>await createController().update(request, response)
|
||||
const result = await httpResponse.executeAsync()
|
||||
|
||||
expect(updateUser.execute).toHaveBeenCalledWith({
|
||||
apiVersion: '20190520',
|
||||
|
@ -112,7 +113,7 @@ describe('InversifyExpressUsersController', () => {
|
|||
},
|
||||
})
|
||||
|
||||
expect(response.send).toHaveBeenCalledWith({ foo: 'bar' })
|
||||
expect(await result.content.readAsStringAsync()).toEqual('{"foo":"bar"}')
|
||||
})
|
||||
|
||||
it('should not update user if session has read only access', async () => {
|
||||
|
@ -310,7 +311,8 @@ describe('InversifyExpressUsersController', () => {
|
|||
|
||||
changeCredentials.execute = jest.fn().mockReturnValue({ success: true, authResponse: { foo: 'bar' } })
|
||||
|
||||
await createController().changeCredentials(request, response)
|
||||
const httpResponse = <results.JsonResult>await createController().changeCredentials(request, response)
|
||||
const result = await httpResponse.executeAsync()
|
||||
|
||||
expect(changeCredentials.execute).toHaveBeenCalledWith({
|
||||
apiVersion: '20190520',
|
||||
|
@ -326,7 +328,7 @@ describe('InversifyExpressUsersController', () => {
|
|||
|
||||
expect(clearLoginAttempts.execute).toHaveBeenCalled()
|
||||
|
||||
expect(response.send).toHaveBeenCalledWith({ foo: 'bar' })
|
||||
expect(await result.content.readAsStringAsync()).toEqual('{"foo":"bar"}')
|
||||
})
|
||||
|
||||
it('should not change a password if session has read only access', async () => {
|
||||
|
|
|
@ -42,7 +42,7 @@ export class InversifyExpressUsersController extends BaseHttpController {
|
|||
}
|
||||
|
||||
@httpPatch('/:userId', TYPES.Auth_RequiredCrossServiceTokenMiddleware)
|
||||
async update(request: Request, response: Response): Promise<results.JsonResult | void> {
|
||||
async update(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
if (response.locals.readOnlyAccess) {
|
||||
return this.json(
|
||||
{
|
||||
|
@ -83,9 +83,8 @@ export class InversifyExpressUsersController extends BaseHttpController {
|
|||
|
||||
if (updateResult.success) {
|
||||
response.setHeader('x-invalidate-cache', response.locals.user.uuid)
|
||||
response.send(updateResult.authResponse)
|
||||
|
||||
return
|
||||
return this.json(updateResult.authResponse)
|
||||
}
|
||||
|
||||
return this.json(
|
||||
|
@ -157,7 +156,7 @@ export class InversifyExpressUsersController extends BaseHttpController {
|
|||
}
|
||||
|
||||
@httpPut('/:userId/attributes/credentials', TYPES.Auth_RequiredCrossServiceTokenMiddleware)
|
||||
async changeCredentials(request: Request, response: Response): Promise<results.JsonResult | void> {
|
||||
async changeCredentials(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
if (response.locals.readOnlyAccess) {
|
||||
return this.json(
|
||||
{
|
||||
|
@ -245,6 +244,7 @@ export class InversifyExpressUsersController extends BaseHttpController {
|
|||
await this.clearLoginAttempts.execute({ email: response.locals.user.email })
|
||||
|
||||
response.setHeader('x-invalidate-cache', response.locals.user.uuid)
|
||||
response.send(changeCredentialsResult.authResponse)
|
||||
|
||||
return this.json(changeCredentialsResult.authResponse)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { Service as ApiGatewayService, TYPES as ApiGatewayTYPES } from '@standar
|
|||
import { DirectCallDomainEventPublisher } from '@standardnotes/domain-events-infra'
|
||||
import { Service as AuthService } from '@standardnotes/auth-server'
|
||||
import { Service as SyncingService } from '@standardnotes/syncing-server'
|
||||
import { Service as RevisionsService } from '@standardnotes/revisions-server'
|
||||
import { Container } from 'inversify'
|
||||
import { InversifyExpressServer } from 'inversify-express-utils'
|
||||
import helmet from 'helmet'
|
||||
|
@ -24,11 +25,13 @@ const startServer = async (): Promise<void> => {
|
|||
const apiGatewayService = new ApiGatewayService(serviceContainer)
|
||||
const authService = new AuthService(serviceContainer, controllerContainer, directCallDomainEventPublisher)
|
||||
const syncingService = new SyncingService(serviceContainer, controllerContainer, directCallDomainEventPublisher)
|
||||
const revisionsService = new RevisionsService(serviceContainer, controllerContainer, directCallDomainEventPublisher)
|
||||
|
||||
const container = Container.merge(
|
||||
(await apiGatewayService.getContainer()) as Container,
|
||||
(await authService.getContainer()) as Container,
|
||||
(await syncingService.getContainer()) as Container,
|
||||
(await revisionsService.getContainer()) as Container,
|
||||
)
|
||||
|
||||
const env: Env = new Env()
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
"@standardnotes/auth-server": "workspace:^",
|
||||
"@standardnotes/domain-core": "workspace:^",
|
||||
"@standardnotes/domain-events-infra": "workspace:^",
|
||||
"@standardnotes/revisions-server": "workspace:^",
|
||||
"@standardnotes/syncing-server": "workspace:^",
|
||||
"cors": "2.8.5",
|
||||
"dotenv": "^16.0.1",
|
||||
|
|
|
@ -9,20 +9,20 @@ import * as winston from 'winston'
|
|||
import { InversifyExpressServer } from 'inversify-express-utils'
|
||||
import TYPES from '../src/Bootstrap/Types'
|
||||
import { Env } from '../src/Bootstrap/Env'
|
||||
import { ServerContainerConfigLoader } from '../src/Bootstrap/ServerContainerConfigLoader'
|
||||
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||
|
||||
import '../src/Infra/InversifyExpress/InversifyExpressRevisionsController'
|
||||
import '../src/Infra/InversifyExpress/InversifyExpressHealthCheckController'
|
||||
|
||||
const container = new ServerContainerConfigLoader()
|
||||
const container = new ContainerConfigLoader()
|
||||
void container.load().then((container) => {
|
||||
const env: Env = container.get(TYPES.Env)
|
||||
const env: Env = container.get(TYPES.Revisions_Env)
|
||||
|
||||
const server = new InversifyExpressServer(container)
|
||||
|
||||
server.setConfig((app) => {
|
||||
app.use((_request: Request, response: Response, next: NextFunction) => {
|
||||
response.setHeader('X-Revisions-Version', container.get(TYPES.VERSION))
|
||||
response.setHeader('X-Revisions-Version', container.get(TYPES.Revisions_VERSION))
|
||||
next()
|
||||
})
|
||||
app.use(json())
|
||||
|
@ -30,7 +30,7 @@ void container.load().then((container) => {
|
|||
app.use(cors())
|
||||
})
|
||||
|
||||
const logger: winston.Logger = container.get(TYPES.Logger)
|
||||
const logger: winston.Logger = container.get(TYPES.Revisions_Logger)
|
||||
|
||||
server.setErrorConfig((app) => {
|
||||
app.use((error: Record<string, unknown>, _request: Request, response: Response, _next: NextFunction) => {
|
||||
|
|
|
@ -7,17 +7,19 @@ import { Logger } from 'winston'
|
|||
import TYPES from '../src/Bootstrap/Types'
|
||||
import { Env } from '../src/Bootstrap/Env'
|
||||
import { DomainEventSubscriberFactoryInterface } from '@standardnotes/domain-events'
|
||||
import { WorkerContainerConfigLoader } from '../src/Bootstrap/WorkerContainerConfigLoader'
|
||||
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||
|
||||
const container = new WorkerContainerConfigLoader()
|
||||
const container = new ContainerConfigLoader()
|
||||
void container.load().then((container) => {
|
||||
const env: Env = new Env()
|
||||
env.load()
|
||||
|
||||
const logger: Logger = container.get(TYPES.Logger)
|
||||
const logger: Logger = container.get(TYPES.Revisions_Logger)
|
||||
|
||||
logger.info('Starting worker...')
|
||||
|
||||
const subscriberFactory: DomainEventSubscriberFactoryInterface = container.get(TYPES.DomainEventSubscriberFactory)
|
||||
const subscriberFactory: DomainEventSubscriberFactoryInterface = container.get(
|
||||
TYPES.Revisions_DomainEventSubscriberFactory,
|
||||
)
|
||||
subscriberFactory.create().start()
|
||||
})
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
import { MapperInterface } from '@standardnotes/domain-core'
|
||||
import { Container, interfaces } from 'inversify'
|
||||
import { Repository } from 'typeorm'
|
||||
import * as winston from 'winston'
|
||||
|
||||
import { Revision } from '../Domain/Revision/Revision'
|
||||
import { RevisionMetadata } from '../Domain/Revision/RevisionMetadata'
|
||||
import { RevisionRepositoryInterface } from '../Domain/Revision/RevisionRepositoryInterface'
|
||||
import { TypeORMRevisionRepository } from '../Infra/TypeORM/TypeORMRevisionRepository'
|
||||
import { TypeORMRevision } from '../Infra/TypeORM/TypeORMRevision'
|
||||
import { RevisionMetadataPersistenceMapper } from '../Mapping/RevisionMetadataPersistenceMapper'
|
||||
import { RevisionPersistenceMapper } from '../Mapping/RevisionPersistenceMapper'
|
||||
import { AppDataSource } from './DataSource'
|
||||
import { Env } from './Env'
|
||||
import TYPES from './Types'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const newrelicFormatter = require('@newrelic/winston-enricher')
|
||||
|
||||
export class CommonContainerConfigLoader {
|
||||
async load(): Promise<Container> {
|
||||
const env: Env = new Env()
|
||||
env.load()
|
||||
|
||||
const container = new Container({
|
||||
defaultScope: 'Singleton',
|
||||
})
|
||||
|
||||
await AppDataSource.initialize()
|
||||
|
||||
container.bind<Env>(TYPES.Env).toConstantValue(env)
|
||||
|
||||
container.bind<winston.Logger>(TYPES.Logger).toDynamicValue((context: interfaces.Context) => {
|
||||
const env: Env = context.container.get(TYPES.Env)
|
||||
|
||||
const newrelicWinstonFormatter = newrelicFormatter(winston)
|
||||
const winstonFormatters = [winston.format.splat(), winston.format.json()]
|
||||
if (env.get('NEW_RELIC_ENABLED', true) === 'true') {
|
||||
winstonFormatters.push(newrelicWinstonFormatter())
|
||||
}
|
||||
|
||||
const logger = winston.createLogger({
|
||||
level: env.get('LOG_LEVEL') || 'info',
|
||||
format: winston.format.combine(...winstonFormatters),
|
||||
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL') || 'info' })],
|
||||
defaultMeta: { service: 'revisions' },
|
||||
})
|
||||
|
||||
return logger
|
||||
})
|
||||
|
||||
container.bind(TYPES.NEW_RELIC_ENABLED).toConstantValue(env.get('NEW_RELIC_ENABLED', true))
|
||||
container.bind(TYPES.VERSION).toConstantValue(env.get('VERSION'))
|
||||
|
||||
// Map
|
||||
container
|
||||
.bind<MapperInterface<RevisionMetadata, TypeORMRevision>>(TYPES.RevisionMetadataPersistenceMapper)
|
||||
.toDynamicValue(() => new RevisionMetadataPersistenceMapper())
|
||||
container
|
||||
.bind<MapperInterface<Revision, TypeORMRevision>>(TYPES.RevisionPersistenceMapper)
|
||||
.toDynamicValue(() => new RevisionPersistenceMapper())
|
||||
|
||||
// ORM
|
||||
container
|
||||
.bind<Repository<TypeORMRevision>>(TYPES.ORMRevisionRepository)
|
||||
.toDynamicValue(() => AppDataSource.getRepository(TypeORMRevision))
|
||||
|
||||
// Repositories
|
||||
container
|
||||
.bind<RevisionRepositoryInterface>(TYPES.RevisionRepository)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new TypeORMRevisionRepository(
|
||||
context.container.get(TYPES.ORMRevisionRepository),
|
||||
context.container.get(TYPES.RevisionMetadataPersistenceMapper),
|
||||
context.container.get(TYPES.RevisionPersistenceMapper),
|
||||
context.container.get(TYPES.Logger),
|
||||
)
|
||||
})
|
||||
|
||||
return container
|
||||
}
|
||||
}
|
351
packages/revisions/src/Bootstrap/Container.ts
Normal file
351
packages/revisions/src/Bootstrap/Container.ts
Normal file
|
@ -0,0 +1,351 @@
|
|||
import { ControllerContainer, ControllerContainerInterface, MapperInterface } from '@standardnotes/domain-core'
|
||||
import { Container, interfaces } from 'inversify'
|
||||
import { Repository } from 'typeorm'
|
||||
import * as winston from 'winston'
|
||||
|
||||
import { Revision } from '../Domain/Revision/Revision'
|
||||
import { RevisionMetadata } from '../Domain/Revision/RevisionMetadata'
|
||||
import { RevisionRepositoryInterface } from '../Domain/Revision/RevisionRepositoryInterface'
|
||||
import { TypeORMRevisionRepository } from '../Infra/TypeORM/TypeORMRevisionRepository'
|
||||
import { TypeORMRevision } from '../Infra/TypeORM/TypeORMRevision'
|
||||
import { RevisionMetadataPersistenceMapper } from '../Mapping/RevisionMetadataPersistenceMapper'
|
||||
import { RevisionPersistenceMapper } from '../Mapping/RevisionPersistenceMapper'
|
||||
import { AppDataSource } from './DataSource'
|
||||
import { Env } from './Env'
|
||||
import TYPES from './Types'
|
||||
import { TokenDecoderInterface, CrossServiceTokenData, TokenDecoder } from '@standardnotes/security'
|
||||
import { TimerInterface, Timer } from '@standardnotes/time'
|
||||
import { ApiGatewayAuthMiddleware } from '../Infra/InversifyExpress/Middleware/ApiGatewayAuthMiddleware'
|
||||
import { RevisionsController } from '../Controller/RevisionsController'
|
||||
import { DeleteRevision } from '../Domain/UseCase/DeleteRevision/DeleteRevision'
|
||||
import { GetRequiredRoleToViewRevision } from '../Domain/UseCase/GetRequiredRoleToViewRevision/GetRequiredRoleToViewRevision'
|
||||
import { GetRevision } from '../Domain/UseCase/GetRevision/GetRevision'
|
||||
import { GetRevisionsMetada } from '../Domain/UseCase/GetRevisionsMetada/GetRevisionsMetada'
|
||||
import { RevisionHttpMapper } from '../Mapping/RevisionHttpMapper'
|
||||
import { RevisionMetadataHttpMapper } from '../Mapping/RevisionMetadataHttpMapper'
|
||||
import { S3Client } from '@aws-sdk/client-s3'
|
||||
import { SQSClient, SQSClientConfig } from '@aws-sdk/client-sqs'
|
||||
import {
|
||||
DomainEventMessageHandlerInterface,
|
||||
DomainEventHandlerInterface,
|
||||
DomainEventSubscriberFactoryInterface,
|
||||
} from '@standardnotes/domain-events'
|
||||
import {
|
||||
SQSNewRelicEventMessageHandler,
|
||||
SQSEventMessageHandler,
|
||||
SQSDomainEventSubscriberFactory,
|
||||
DirectCallEventMessageHandler,
|
||||
DirectCallDomainEventPublisher,
|
||||
} from '@standardnotes/domain-events-infra'
|
||||
import { DumpRepositoryInterface } from '../Domain/Dump/DumpRepositoryInterface'
|
||||
import { AccountDeletionRequestedEventHandler } from '../Domain/Handler/AccountDeletionRequestedEventHandler'
|
||||
import { ItemDumpedEventHandler } from '../Domain/Handler/ItemDumpedEventHandler'
|
||||
import { RevisionsCopyRequestedEventHandler } from '../Domain/Handler/RevisionsCopyRequestedEventHandler'
|
||||
import { CopyRevisions } from '../Domain/UseCase/CopyRevisions/CopyRevisions'
|
||||
import { FSDumpRepository } from '../Infra/FS/FSDumpRepository'
|
||||
import { S3DumpRepository } from '../Infra/S3/S3ItemDumpRepository'
|
||||
import { RevisionItemStringMapper } from '../Mapping/RevisionItemStringMapper'
|
||||
import { InversifyExpressRevisionsController } from '../Infra/InversifyExpress/InversifyExpressRevisionsController'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const newrelicFormatter = require('@newrelic/winston-enricher')
|
||||
|
||||
export class ContainerConfigLoader {
|
||||
async load(configuration?: {
|
||||
controllerConatiner?: ControllerContainerInterface
|
||||
directCallDomainEventPublisher?: DirectCallDomainEventPublisher
|
||||
}): Promise<Container> {
|
||||
const directCallDomainEventPublisher =
|
||||
configuration?.directCallDomainEventPublisher ?? new DirectCallDomainEventPublisher()
|
||||
|
||||
const env: Env = new Env()
|
||||
env.load()
|
||||
|
||||
const isConfiguredForHomeServer = env.get('DB_TYPE') === 'sqlite'
|
||||
|
||||
const container = new Container({
|
||||
defaultScope: 'Singleton',
|
||||
})
|
||||
|
||||
await AppDataSource.initialize()
|
||||
|
||||
container.bind<Env>(TYPES.Revisions_Env).toConstantValue(env)
|
||||
|
||||
container.bind<winston.Logger>(TYPES.Revisions_Logger).toDynamicValue((context: interfaces.Context) => {
|
||||
const env: Env = context.container.get(TYPES.Revisions_Env)
|
||||
|
||||
const newrelicWinstonFormatter = newrelicFormatter(winston)
|
||||
const winstonFormatters = [winston.format.splat(), winston.format.json()]
|
||||
if (env.get('NEW_RELIC_ENABLED', true) === 'true') {
|
||||
winstonFormatters.push(newrelicWinstonFormatter())
|
||||
}
|
||||
|
||||
const logger = winston.createLogger({
|
||||
level: env.get('LOG_LEVEL') || 'info',
|
||||
format: winston.format.combine(...winstonFormatters),
|
||||
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL') || 'info' })],
|
||||
defaultMeta: { service: 'revisions' },
|
||||
})
|
||||
|
||||
return logger
|
||||
})
|
||||
|
||||
container.bind(TYPES.Revisions_NEW_RELIC_ENABLED).toConstantValue(env.get('NEW_RELIC_ENABLED', true))
|
||||
container.bind(TYPES.Revisions_VERSION).toConstantValue(env.get('VERSION'))
|
||||
|
||||
// Map
|
||||
container
|
||||
.bind<MapperInterface<RevisionMetadata, TypeORMRevision>>(TYPES.Revisions_RevisionMetadataPersistenceMapper)
|
||||
.toDynamicValue(() => new RevisionMetadataPersistenceMapper())
|
||||
container
|
||||
.bind<MapperInterface<Revision, TypeORMRevision>>(TYPES.Revisions_RevisionPersistenceMapper)
|
||||
.toDynamicValue(() => new RevisionPersistenceMapper())
|
||||
|
||||
// ORM
|
||||
container
|
||||
.bind<Repository<TypeORMRevision>>(TYPES.Revisions_ORMRevisionRepository)
|
||||
.toDynamicValue(() => AppDataSource.getRepository(TypeORMRevision))
|
||||
|
||||
// Repositories
|
||||
container
|
||||
.bind<RevisionRepositoryInterface>(TYPES.Revisions_RevisionRepository)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new TypeORMRevisionRepository(
|
||||
context.container.get(TYPES.Revisions_ORMRevisionRepository),
|
||||
context.container.get(TYPES.Revisions_RevisionMetadataPersistenceMapper),
|
||||
context.container.get(TYPES.Revisions_RevisionPersistenceMapper),
|
||||
context.container.get(TYPES.Revisions_Logger),
|
||||
)
|
||||
})
|
||||
|
||||
container.bind<TimerInterface>(TYPES.Revisions_Timer).toDynamicValue(() => new Timer())
|
||||
|
||||
container
|
||||
.bind<GetRequiredRoleToViewRevision>(TYPES.Revisions_GetRequiredRoleToViewRevision)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new GetRequiredRoleToViewRevision(context.container.get(TYPES.Revisions_Timer))
|
||||
})
|
||||
|
||||
// Map
|
||||
container
|
||||
.bind<
|
||||
MapperInterface<
|
||||
Revision,
|
||||
{
|
||||
uuid: string
|
||||
item_uuid: string
|
||||
content: string | null
|
||||
content_type: string
|
||||
items_key_id: string | null
|
||||
enc_item_key: string | null
|
||||
auth_hash: string | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
>
|
||||
>(TYPES.Revisions_RevisionHttpMapper)
|
||||
.toDynamicValue(() => new RevisionHttpMapper())
|
||||
container
|
||||
.bind<
|
||||
MapperInterface<
|
||||
RevisionMetadata,
|
||||
{
|
||||
uuid: string
|
||||
content_type: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
>
|
||||
>(TYPES.Revisions_RevisionMetadataHttpMapper)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new RevisionMetadataHttpMapper(context.container.get(TYPES.Revisions_GetRequiredRoleToViewRevision))
|
||||
})
|
||||
|
||||
// use cases
|
||||
container
|
||||
.bind<GetRevisionsMetada>(TYPES.Revisions_GetRevisionsMetada)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new GetRevisionsMetada(context.container.get(TYPES.Revisions_RevisionRepository))
|
||||
})
|
||||
container.bind<GetRevision>(TYPES.Revisions_GetRevision).toDynamicValue((context: interfaces.Context) => {
|
||||
return new GetRevision(context.container.get(TYPES.Revisions_RevisionRepository))
|
||||
})
|
||||
container.bind<DeleteRevision>(TYPES.Revisions_DeleteRevision).toDynamicValue((context: interfaces.Context) => {
|
||||
return new DeleteRevision(context.container.get(TYPES.Revisions_RevisionRepository))
|
||||
})
|
||||
|
||||
// env vars
|
||||
container.bind(TYPES.Revisions_AUTH_JWT_SECRET).toConstantValue(env.get('AUTH_JWT_SECRET'))
|
||||
|
||||
// Controller
|
||||
container
|
||||
.bind<ControllerContainerInterface>(TYPES.Revisions_ControllerContainer)
|
||||
.toConstantValue(configuration?.controllerConatiner ?? new ControllerContainer())
|
||||
|
||||
container
|
||||
.bind<RevisionsController>(TYPES.Revisions_RevisionsController)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new RevisionsController(
|
||||
context.container.get(TYPES.Revisions_GetRevisionsMetada),
|
||||
context.container.get(TYPES.Revisions_GetRevision),
|
||||
context.container.get(TYPES.Revisions_DeleteRevision),
|
||||
context.container.get(TYPES.Revisions_RevisionHttpMapper),
|
||||
context.container.get(TYPES.Revisions_RevisionMetadataHttpMapper),
|
||||
context.container.get(TYPES.Revisions_Logger),
|
||||
)
|
||||
})
|
||||
|
||||
container
|
||||
.bind<TokenDecoderInterface<CrossServiceTokenData>>(TYPES.Revisions_CrossServiceTokenDecoder)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new TokenDecoder<CrossServiceTokenData>(context.container.get(TYPES.Revisions_AUTH_JWT_SECRET))
|
||||
})
|
||||
|
||||
container
|
||||
.bind<ApiGatewayAuthMiddleware>(TYPES.Revisions_ApiGatewayAuthMiddleware)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new ApiGatewayAuthMiddleware(
|
||||
context.container.get(TYPES.Revisions_CrossServiceTokenDecoder),
|
||||
context.container.get(TYPES.Revisions_Logger),
|
||||
)
|
||||
})
|
||||
|
||||
// Map
|
||||
container
|
||||
.bind<MapperInterface<Revision, string>>(TYPES.Revisions_RevisionItemStringMapper)
|
||||
.toDynamicValue(() => new RevisionItemStringMapper())
|
||||
|
||||
container
|
||||
.bind<DumpRepositoryInterface>(TYPES.Revisions_DumpRepository)
|
||||
.toConstantValue(
|
||||
env.get('S3_AWS_REGION', true)
|
||||
? new S3DumpRepository(
|
||||
container.get(TYPES.Revisions_S3_BACKUP_BUCKET_NAME),
|
||||
container.get(TYPES.Revisions_S3),
|
||||
container.get(TYPES.Revisions_RevisionItemStringMapper),
|
||||
container.get(TYPES.Revisions_Logger),
|
||||
)
|
||||
: new FSDumpRepository(container.get(TYPES.Revisions_RevisionItemStringMapper)),
|
||||
)
|
||||
|
||||
if (!isConfiguredForHomeServer) {
|
||||
// env vars
|
||||
container.bind(TYPES.Revisions_SQS_QUEUE_URL).toConstantValue(env.get('SQS_QUEUE_URL'))
|
||||
container.bind(TYPES.Revisions_S3_AWS_REGION).toConstantValue(env.get('S3_AWS_REGION', true))
|
||||
container.bind(TYPES.Revisions_S3_BACKUP_BUCKET_NAME).toConstantValue(env.get('S3_BACKUP_BUCKET_NAME', true))
|
||||
|
||||
container.bind<SQSClient>(TYPES.Revisions_SQS).toDynamicValue((context: interfaces.Context) => {
|
||||
const env: Env = context.container.get(TYPES.Revisions_Env)
|
||||
|
||||
const sqsConfig: SQSClientConfig = {
|
||||
region: env.get('SQS_AWS_REGION'),
|
||||
}
|
||||
if (env.get('SQS_ENDPOINT', true)) {
|
||||
sqsConfig.endpoint = env.get('SQS_ENDPOINT', true)
|
||||
}
|
||||
if (env.get('SQS_ACCESS_KEY_ID', true) && env.get('SQS_SECRET_ACCESS_KEY', true)) {
|
||||
sqsConfig.credentials = {
|
||||
accessKeyId: env.get('SQS_ACCESS_KEY_ID', true),
|
||||
secretAccessKey: env.get('SQS_SECRET_ACCESS_KEY', true),
|
||||
}
|
||||
}
|
||||
|
||||
return new SQSClient(sqsConfig)
|
||||
})
|
||||
|
||||
container.bind<S3Client | undefined>(TYPES.Revisions_S3).toDynamicValue((context: interfaces.Context) => {
|
||||
const env: Env = context.container.get(TYPES.Revisions_Env)
|
||||
|
||||
let s3Client = undefined
|
||||
if (env.get('S3_AWS_REGION', true)) {
|
||||
s3Client = new S3Client({
|
||||
apiVersion: 'latest',
|
||||
region: env.get('S3_AWS_REGION', true),
|
||||
})
|
||||
}
|
||||
|
||||
return s3Client
|
||||
})
|
||||
}
|
||||
|
||||
// use cases
|
||||
container.bind<CopyRevisions>(TYPES.Revisions_CopyRevisions).toDynamicValue((context: interfaces.Context) => {
|
||||
return new CopyRevisions(context.container.get(TYPES.Revisions_RevisionRepository))
|
||||
})
|
||||
|
||||
// Handlers
|
||||
container
|
||||
.bind<ItemDumpedEventHandler>(TYPES.Revisions_ItemDumpedEventHandler)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new ItemDumpedEventHandler(
|
||||
context.container.get(TYPES.Revisions_DumpRepository),
|
||||
context.container.get(TYPES.Revisions_RevisionRepository),
|
||||
)
|
||||
})
|
||||
container
|
||||
.bind<AccountDeletionRequestedEventHandler>(TYPES.Revisions_AccountDeletionRequestedEventHandler)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new AccountDeletionRequestedEventHandler(
|
||||
context.container.get(TYPES.Revisions_RevisionRepository),
|
||||
context.container.get(TYPES.Revisions_Logger),
|
||||
)
|
||||
})
|
||||
container
|
||||
.bind<RevisionsCopyRequestedEventHandler>(TYPES.Revisions_RevisionsCopyRequestedEventHandler)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new RevisionsCopyRequestedEventHandler(
|
||||
context.container.get(TYPES.Revisions_CopyRevisions),
|
||||
context.container.get(TYPES.Revisions_Logger),
|
||||
)
|
||||
})
|
||||
|
||||
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
|
||||
['ITEM_DUMPED', container.get(TYPES.Revisions_ItemDumpedEventHandler)],
|
||||
['ACCOUNT_DELETION_REQUESTED', container.get(TYPES.Revisions_AccountDeletionRequestedEventHandler)],
|
||||
['REVISIONS_COPY_REQUESTED', container.get(TYPES.Revisions_RevisionsCopyRequestedEventHandler)],
|
||||
])
|
||||
|
||||
if (isConfiguredForHomeServer) {
|
||||
const directCallEventMessageHandler = new DirectCallEventMessageHandler(
|
||||
eventHandlers,
|
||||
container.get(TYPES.Revisions_Logger),
|
||||
)
|
||||
directCallDomainEventPublisher.register(directCallEventMessageHandler)
|
||||
container
|
||||
.bind<DomainEventMessageHandlerInterface>(TYPES.Revisions_DomainEventMessageHandler)
|
||||
.toConstantValue(directCallEventMessageHandler)
|
||||
} else {
|
||||
container
|
||||
.bind<DomainEventMessageHandlerInterface>(TYPES.Revisions_DomainEventMessageHandler)
|
||||
.toConstantValue(
|
||||
env.get('NEW_RELIC_ENABLED', true) === 'true'
|
||||
? new SQSNewRelicEventMessageHandler(eventHandlers, container.get(TYPES.Revisions_Logger))
|
||||
: new SQSEventMessageHandler(eventHandlers, container.get(TYPES.Revisions_Logger)),
|
||||
)
|
||||
|
||||
container
|
||||
.bind<DomainEventSubscriberFactoryInterface>(TYPES.Revisions_DomainEventSubscriberFactory)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new SQSDomainEventSubscriberFactory(
|
||||
context.container.get(TYPES.Revisions_SQS),
|
||||
context.container.get(TYPES.Revisions_SQS_QUEUE_URL),
|
||||
context.container.get(TYPES.Revisions_DomainEventMessageHandler),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// Inversify Controllers
|
||||
if (isConfiguredForHomeServer) {
|
||||
container
|
||||
.bind<InversifyExpressRevisionsController>(TYPES.Revisions_InversifyExpressRevisionsController)
|
||||
.toConstantValue(
|
||||
new InversifyExpressRevisionsController(
|
||||
container.get(TYPES.Revisions_RevisionsController),
|
||||
container.get(TYPES.Revisions_ControllerContainer),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return container
|
||||
}
|
||||
}
|
|
@ -15,55 +15,62 @@ const maxQueryExecutionTime = env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
|
|||
? +env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
|
||||
: 45_000
|
||||
|
||||
const inReplicaMode = env.get('DB_REPLICA_HOST', true) ? true : false
|
||||
const commonDataSourceOptions = {
|
||||
maxQueryExecutionTime,
|
||||
entities: [TypeORMRevision],
|
||||
migrations: [`${__dirname}/../../migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
|
||||
migrationsRun: true,
|
||||
logging: <LoggerOptions>env.get('DB_DEBUG_LEVEL', true) ?? 'info',
|
||||
}
|
||||
|
||||
const replicationConfig = {
|
||||
master: {
|
||||
host: env.get('DB_HOST'),
|
||||
port: parseInt(env.get('DB_PORT')),
|
||||
username: env.get('DB_USERNAME'),
|
||||
password: env.get('DB_PASSWORD'),
|
||||
database: env.get('DB_DATABASE'),
|
||||
},
|
||||
slaves: [
|
||||
{
|
||||
host: env.get('DB_REPLICA_HOST', true),
|
||||
let dataSource: DataSource
|
||||
if (isConfiguredForMySQL) {
|
||||
const inReplicaMode = env.get('DB_REPLICA_HOST', true) ? true : false
|
||||
|
||||
const replicationConfig = {
|
||||
master: {
|
||||
host: env.get('DB_HOST'),
|
||||
port: parseInt(env.get('DB_PORT')),
|
||||
username: env.get('DB_USERNAME'),
|
||||
password: env.get('DB_PASSWORD'),
|
||||
database: env.get('DB_DATABASE'),
|
||||
},
|
||||
],
|
||||
removeNodeErrorCount: 10,
|
||||
restoreNodeTimeout: 5,
|
||||
slaves: [
|
||||
{
|
||||
host: env.get('DB_REPLICA_HOST', true),
|
||||
port: parseInt(env.get('DB_PORT')),
|
||||
username: env.get('DB_USERNAME'),
|
||||
password: env.get('DB_PASSWORD'),
|
||||
database: env.get('DB_DATABASE'),
|
||||
},
|
||||
],
|
||||
removeNodeErrorCount: 10,
|
||||
restoreNodeTimeout: 5,
|
||||
}
|
||||
|
||||
const mySQLDataSourceOptions: MysqlConnectionOptions = {
|
||||
...commonDataSourceOptions,
|
||||
type: 'mysql',
|
||||
charset: 'utf8mb4',
|
||||
supportBigNumbers: true,
|
||||
bigNumberStrings: false,
|
||||
replication: inReplicaMode ? replicationConfig : undefined,
|
||||
host: inReplicaMode ? undefined : env.get('DB_HOST'),
|
||||
port: inReplicaMode ? undefined : parseInt(env.get('DB_PORT')),
|
||||
username: inReplicaMode ? undefined : env.get('DB_USERNAME'),
|
||||
password: inReplicaMode ? undefined : env.get('DB_PASSWORD'),
|
||||
database: inReplicaMode ? undefined : env.get('DB_DATABASE'),
|
||||
}
|
||||
|
||||
dataSource = new DataSource(mySQLDataSourceOptions)
|
||||
} else {
|
||||
const sqliteDataSourceOptions: SqliteConnectionOptions = {
|
||||
...commonDataSourceOptions,
|
||||
type: 'sqlite',
|
||||
database: `data/${env.get('DB_DATABASE')}.sqlite`,
|
||||
}
|
||||
|
||||
dataSource = new DataSource(sqliteDataSourceOptions)
|
||||
}
|
||||
|
||||
const commonDataSourceOptions = {
|
||||
maxQueryExecutionTime,
|
||||
entities: [TypeORMRevision],
|
||||
migrations: [`dist/migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
|
||||
migrationsRun: true,
|
||||
logging: <LoggerOptions>env.get('DB_DEBUG_LEVEL'),
|
||||
}
|
||||
|
||||
const mySQLDataSourceOptions: MysqlConnectionOptions = {
|
||||
...commonDataSourceOptions,
|
||||
type: 'mysql',
|
||||
charset: 'utf8mb4',
|
||||
supportBigNumbers: true,
|
||||
bigNumberStrings: false,
|
||||
replication: inReplicaMode ? replicationConfig : undefined,
|
||||
host: inReplicaMode ? undefined : env.get('DB_HOST'),
|
||||
port: inReplicaMode ? undefined : parseInt(env.get('DB_PORT')),
|
||||
username: inReplicaMode ? undefined : env.get('DB_USERNAME'),
|
||||
password: inReplicaMode ? undefined : env.get('DB_PASSWORD'),
|
||||
database: inReplicaMode ? undefined : env.get('DB_DATABASE'),
|
||||
}
|
||||
|
||||
const sqliteDataSourceOptions: SqliteConnectionOptions = {
|
||||
...commonDataSourceOptions,
|
||||
type: 'sqlite',
|
||||
database: `data/${env.get('DB_DATABASE')}.sqlite`,
|
||||
}
|
||||
|
||||
export const AppDataSource = new DataSource(isConfiguredForMySQL ? mySQLDataSourceOptions : sqliteDataSourceOptions)
|
||||
export const AppDataSource = dataSource
|
||||
|
|
|
@ -1,112 +0,0 @@
|
|||
import { Timer, TimerInterface } from '@standardnotes/time'
|
||||
import { Container, interfaces } from 'inversify'
|
||||
import { MapperInterface } from '@standardnotes/domain-core'
|
||||
|
||||
import TYPES from './Types'
|
||||
import { RevisionsController } from '../Controller/RevisionsController'
|
||||
import { GetRevisionsMetada } from '../Domain/UseCase/GetRevisionsMetada/GetRevisionsMetada'
|
||||
import { RevisionMetadata } from '../Domain/Revision/RevisionMetadata'
|
||||
import { Revision } from '../Domain/Revision/Revision'
|
||||
import { GetRevision } from '../Domain/UseCase/GetRevision/GetRevision'
|
||||
import { DeleteRevision } from '../Domain/UseCase/DeleteRevision/DeleteRevision'
|
||||
import { RevisionHttpMapper } from '../Mapping/RevisionHttpMapper'
|
||||
import { RevisionMetadataHttpMapper } from '../Mapping/RevisionMetadataHttpMapper'
|
||||
import { GetRequiredRoleToViewRevision } from '../Domain/UseCase/GetRequiredRoleToViewRevision/GetRequiredRoleToViewRevision'
|
||||
import { CommonContainerConfigLoader } from './CommonContainerConfigLoader'
|
||||
import { ApiGatewayAuthMiddleware } from '../Controller/ApiGatewayAuthMiddleware'
|
||||
import { CrossServiceTokenData, TokenDecoder, TokenDecoderInterface } from '@standardnotes/security'
|
||||
import { Env } from './Env'
|
||||
|
||||
export class ServerContainerConfigLoader extends CommonContainerConfigLoader {
|
||||
override async load(): Promise<Container> {
|
||||
const container = await super.load()
|
||||
|
||||
const env: Env = container.get(TYPES.Env)
|
||||
|
||||
container.bind<TimerInterface>(TYPES.Timer).toDynamicValue(() => new Timer())
|
||||
|
||||
container
|
||||
.bind<GetRequiredRoleToViewRevision>(TYPES.GetRequiredRoleToViewRevision)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new GetRequiredRoleToViewRevision(context.container.get(TYPES.Timer))
|
||||
})
|
||||
|
||||
// Map
|
||||
container
|
||||
.bind<
|
||||
MapperInterface<
|
||||
Revision,
|
||||
{
|
||||
uuid: string
|
||||
item_uuid: string
|
||||
content: string | null
|
||||
content_type: string
|
||||
items_key_id: string | null
|
||||
enc_item_key: string | null
|
||||
auth_hash: string | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
>
|
||||
>(TYPES.RevisionHttpMapper)
|
||||
.toDynamicValue(() => new RevisionHttpMapper())
|
||||
container
|
||||
.bind<
|
||||
MapperInterface<
|
||||
RevisionMetadata,
|
||||
{
|
||||
uuid: string
|
||||
content_type: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
>
|
||||
>(TYPES.RevisionMetadataHttpMapper)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new RevisionMetadataHttpMapper(context.container.get(TYPES.GetRequiredRoleToViewRevision))
|
||||
})
|
||||
|
||||
// use cases
|
||||
container.bind<GetRevisionsMetada>(TYPES.GetRevisionsMetada).toDynamicValue((context: interfaces.Context) => {
|
||||
return new GetRevisionsMetada(context.container.get(TYPES.RevisionRepository))
|
||||
})
|
||||
container.bind<GetRevision>(TYPES.GetRevision).toDynamicValue((context: interfaces.Context) => {
|
||||
return new GetRevision(context.container.get(TYPES.RevisionRepository))
|
||||
})
|
||||
container.bind<DeleteRevision>(TYPES.DeleteRevision).toDynamicValue((context: interfaces.Context) => {
|
||||
return new DeleteRevision(context.container.get(TYPES.RevisionRepository))
|
||||
})
|
||||
|
||||
// env vars
|
||||
container.bind(TYPES.AUTH_JWT_SECRET).toConstantValue(env.get('AUTH_JWT_SECRET'))
|
||||
|
||||
// Controller
|
||||
container.bind<RevisionsController>(TYPES.RevisionsController).toDynamicValue((context: interfaces.Context) => {
|
||||
return new RevisionsController(
|
||||
context.container.get(TYPES.GetRevisionsMetada),
|
||||
context.container.get(TYPES.GetRevision),
|
||||
context.container.get(TYPES.DeleteRevision),
|
||||
context.container.get(TYPES.RevisionHttpMapper),
|
||||
context.container.get(TYPES.RevisionMetadataHttpMapper),
|
||||
context.container.get(TYPES.Logger),
|
||||
)
|
||||
})
|
||||
|
||||
container
|
||||
.bind<TokenDecoderInterface<CrossServiceTokenData>>(TYPES.CrossServiceTokenDecoder)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new TokenDecoder<CrossServiceTokenData>(context.container.get(TYPES.AUTH_JWT_SECRET))
|
||||
})
|
||||
|
||||
container
|
||||
.bind<ApiGatewayAuthMiddleware>(TYPES.ApiGatewayAuthMiddleware)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new ApiGatewayAuthMiddleware(
|
||||
context.container.get(TYPES.CrossServiceTokenDecoder),
|
||||
context.container.get(TYPES.Logger),
|
||||
)
|
||||
})
|
||||
|
||||
return container
|
||||
}
|
||||
}
|
42
packages/revisions/src/Bootstrap/Service.ts
Normal file
42
packages/revisions/src/Bootstrap/Service.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
import {
|
||||
ControllerContainerInterface,
|
||||
ServiceContainerInterface,
|
||||
ServiceIdentifier,
|
||||
ServiceInterface,
|
||||
} from '@standardnotes/domain-core'
|
||||
|
||||
import { ContainerConfigLoader } from './Container'
|
||||
import { DirectCallDomainEventPublisher } from '@standardnotes/domain-events-infra'
|
||||
|
||||
export class Service implements ServiceInterface {
|
||||
constructor(
|
||||
private serviceContainer: ServiceContainerInterface,
|
||||
private controllerContainer: ControllerContainerInterface,
|
||||
private directCallDomainEventPublisher: DirectCallDomainEventPublisher,
|
||||
) {
|
||||
this.serviceContainer.register(this.getId(), this)
|
||||
}
|
||||
|
||||
async handleRequest(request: never, response: never, endpointOrMethodIdentifier: string): Promise<unknown> {
|
||||
const method = this.controllerContainer.get(endpointOrMethodIdentifier)
|
||||
|
||||
if (!method) {
|
||||
throw new Error(`Method ${endpointOrMethodIdentifier} not found`)
|
||||
}
|
||||
|
||||
return method(request, response)
|
||||
}
|
||||
|
||||
async getContainer(): Promise<unknown> {
|
||||
const config = new ContainerConfigLoader()
|
||||
|
||||
return config.load({
|
||||
controllerConatiner: this.controllerContainer,
|
||||
directCallDomainEventPublisher: this.directCallDomainEventPublisher,
|
||||
})
|
||||
}
|
||||
|
||||
getId(): ServiceIdentifier {
|
||||
return ServiceIdentifier.create(ServiceIdentifier.NAMES.Revisions).getValue()
|
||||
}
|
||||
}
|
|
@ -1,46 +1,49 @@
|
|||
const TYPES = {
|
||||
DBConnection: Symbol.for('DBConnection'),
|
||||
Logger: Symbol.for('Logger'),
|
||||
SQS: Symbol.for('SQS'),
|
||||
S3: Symbol.for('S3'),
|
||||
Env: Symbol.for('Env'),
|
||||
Revisions_DBConnection: Symbol.for('Revisions_DBConnection'),
|
||||
Revisions_Logger: Symbol.for('Revisions_Logger'),
|
||||
Revisions_SQS: Symbol.for('Revisions_SQS'),
|
||||
Revisions_S3: Symbol.for('Revisions_S3'),
|
||||
Revisions_Env: Symbol.for('Revisions_Env'),
|
||||
// Map
|
||||
RevisionMetadataPersistenceMapper: Symbol.for('RevisionMetadataPersistenceMapper'),
|
||||
RevisionPersistenceMapper: Symbol.for('RevisionPersistenceMapper'),
|
||||
RevisionItemStringMapper: Symbol.for('RevisionItemStringMapper'),
|
||||
RevisionHttpMapper: Symbol.for('RevisionHttpMapper'),
|
||||
RevisionMetadataHttpMapper: Symbol.for('RevisionMetadataHttpMapper'),
|
||||
Revisions_RevisionMetadataPersistenceMapper: Symbol.for('Revisions_RevisionMetadataPersistenceMapper'),
|
||||
Revisions_RevisionPersistenceMapper: Symbol.for('Revisions_RevisionPersistenceMapper'),
|
||||
Revisions_RevisionItemStringMapper: Symbol.for('Revisions_RevisionItemStringMapper'),
|
||||
Revisions_RevisionHttpMapper: Symbol.for('Revisions_RevisionHttpMapper'),
|
||||
Revisions_RevisionMetadataHttpMapper: Symbol.for('Revisions_RevisionMetadataHttpMapper'),
|
||||
// ORM
|
||||
ORMRevisionRepository: Symbol.for('ORMRevisionRepository'),
|
||||
Revisions_ORMRevisionRepository: Symbol.for('Revisions_ORMRevisionRepository'),
|
||||
// Repositories
|
||||
RevisionRepository: Symbol.for('RevisionRepository'),
|
||||
DumpRepository: Symbol.for('DumpRepository'),
|
||||
Revisions_RevisionRepository: Symbol.for('Revisions_RevisionRepository'),
|
||||
Revisions_DumpRepository: Symbol.for('Revisions_DumpRepository'),
|
||||
// env vars
|
||||
AUTH_JWT_SECRET: Symbol.for('AUTH_JWT_SECRET'),
|
||||
SQS_QUEUE_URL: Symbol.for('SQS_QUEUE_URL'),
|
||||
SQS_AWS_REGION: Symbol.for('SQS_AWS_REGION'),
|
||||
S3_AWS_REGION: Symbol.for('S3_AWS_REGION'),
|
||||
S3_BACKUP_BUCKET_NAME: Symbol.for('S3_BACKUP_BUCKET_NAME'),
|
||||
NEW_RELIC_ENABLED: Symbol.for('NEW_RELIC_ENABLED'),
|
||||
VERSION: Symbol.for('VERSION'),
|
||||
Revisions_AUTH_JWT_SECRET: Symbol.for('Revisions_AUTH_JWT_SECRET'),
|
||||
Revisions_SQS_QUEUE_URL: Symbol.for('Revisions_SQS_QUEUE_URL'),
|
||||
Revisions_SQS_AWS_REGION: Symbol.for('Revisions_SQS_AWS_REGION'),
|
||||
Revisions_S3_AWS_REGION: Symbol.for('Revisions_S3_AWS_REGION'),
|
||||
Revisions_S3_BACKUP_BUCKET_NAME: Symbol.for('Revisions_S3_BACKUP_BUCKET_NAME'),
|
||||
Revisions_NEW_RELIC_ENABLED: Symbol.for('Revisions_NEW_RELIC_ENABLED'),
|
||||
Revisions_VERSION: Symbol.for('Revisions_VERSION'),
|
||||
// use cases
|
||||
GetRevisionsMetada: Symbol.for('GetRevisionsMetada'),
|
||||
GetRevision: Symbol.for('GetRevision'),
|
||||
DeleteRevision: Symbol.for('DeleteRevision'),
|
||||
CopyRevisions: Symbol.for('CopyRevisions'),
|
||||
GetRequiredRoleToViewRevision: Symbol.for('GetRequiredRoleToViewRevision'),
|
||||
Revisions_GetRevisionsMetada: Symbol.for('Revisions_GetRevisionsMetada'),
|
||||
Revisions_GetRevision: Symbol.for('Revisions_GetRevision'),
|
||||
Revisions_DeleteRevision: Symbol.for('Revisions_DeleteRevision'),
|
||||
Revisions_CopyRevisions: Symbol.for('Revisions_CopyRevisions'),
|
||||
Revisions_GetRequiredRoleToViewRevision: Symbol.for('Revisions_GetRequiredRoleToViewRevision'),
|
||||
// Controller
|
||||
RevisionsController: Symbol.for('RevisionsController'),
|
||||
ApiGatewayAuthMiddleware: Symbol.for('ApiGatewayAuthMiddleware'),
|
||||
Revisions_ControllerContainer: Symbol.for('Revisions_ControllerContainer'),
|
||||
Revisions_RevisionsController: Symbol.for('Revisions_RevisionsController'),
|
||||
Revisions_ApiGatewayAuthMiddleware: Symbol.for('Revisions_ApiGatewayAuthMiddleware'),
|
||||
// Handlers
|
||||
ItemDumpedEventHandler: Symbol.for('ItemDumpedEventHandler'),
|
||||
AccountDeletionRequestedEventHandler: Symbol.for('AccountDeletionRequestedEventHandler'),
|
||||
RevisionsCopyRequestedEventHandler: Symbol.for('RevisionsCopyRequestedEventHandler'),
|
||||
Revisions_ItemDumpedEventHandler: Symbol.for('Revisions_ItemDumpedEventHandler'),
|
||||
Revisions_AccountDeletionRequestedEventHandler: Symbol.for('Revisions_AccountDeletionRequestedEventHandler'),
|
||||
Revisions_RevisionsCopyRequestedEventHandler: Symbol.for('Revisions_RevisionsCopyRequestedEventHandler'),
|
||||
// Services
|
||||
CrossServiceTokenDecoder: Symbol.for('CrossServiceTokenDecoder'),
|
||||
DomainEventSubscriberFactory: Symbol.for('DomainEventSubscriberFactory'),
|
||||
DomainEventMessageHandler: Symbol.for('DomainEventMessageHandler'),
|
||||
Timer: Symbol.for('Timer'),
|
||||
Revisions_CrossServiceTokenDecoder: Symbol.for('Revisions_CrossServiceTokenDecoder'),
|
||||
Revisions_DomainEventSubscriberFactory: Symbol.for('Revisions_DomainEventSubscriberFactory'),
|
||||
Revisions_DomainEventMessageHandler: Symbol.for('Revisions_DomainEventMessageHandler'),
|
||||
Revisions_Timer: Symbol.for('Revisions_Timer'),
|
||||
// Inversify Express Controllers
|
||||
Revisions_InversifyExpressRevisionsController: Symbol.for('Revisions_InversifyExpressRevisionsController'),
|
||||
}
|
||||
|
||||
export default TYPES
|
||||
|
|
|
@ -1,155 +0,0 @@
|
|||
import { SQSClient, SQSClientConfig } from '@aws-sdk/client-sqs'
|
||||
import { S3Client } from '@aws-sdk/client-s3'
|
||||
import { Container, interfaces } from 'inversify'
|
||||
import {
|
||||
DomainEventHandlerInterface,
|
||||
DomainEventMessageHandlerInterface,
|
||||
DomainEventSubscriberFactoryInterface,
|
||||
} from '@standardnotes/domain-events'
|
||||
import {
|
||||
SQSDomainEventSubscriberFactory,
|
||||
SQSEventMessageHandler,
|
||||
SQSNewRelicEventMessageHandler,
|
||||
} from '@standardnotes/domain-events-infra'
|
||||
import { MapperInterface } from '@standardnotes/domain-core'
|
||||
|
||||
import TYPES from './Types'
|
||||
import { Revision } from '../Domain/Revision/Revision'
|
||||
import { RevisionItemStringMapper } from '../Mapping/RevisionItemStringMapper'
|
||||
import { ItemDumpedEventHandler } from '../Domain/Handler/ItemDumpedEventHandler'
|
||||
import { DumpRepositoryInterface } from '../Domain/Dump/DumpRepositoryInterface'
|
||||
import { S3DumpRepository } from '../Infra/S3/S3ItemDumpRepository'
|
||||
import { FSDumpRepository } from '../Infra/FS/FSDumpRepository'
|
||||
import { AccountDeletionRequestedEventHandler } from '../Domain/Handler/AccountDeletionRequestedEventHandler'
|
||||
import { RevisionsCopyRequestedEventHandler } from '../Domain/Handler/RevisionsCopyRequestedEventHandler'
|
||||
import { CopyRevisions } from '../Domain/UseCase/CopyRevisions/CopyRevisions'
|
||||
import { CommonContainerConfigLoader } from './CommonContainerConfigLoader'
|
||||
import { Env } from './Env'
|
||||
|
||||
export class WorkerContainerConfigLoader extends CommonContainerConfigLoader {
|
||||
override async load(): Promise<Container> {
|
||||
const container = await super.load()
|
||||
|
||||
const env: Env = container.get(TYPES.Env)
|
||||
|
||||
container.bind<SQSClient>(TYPES.SQS).toDynamicValue((context: interfaces.Context) => {
|
||||
const env: Env = context.container.get(TYPES.Env)
|
||||
|
||||
const sqsConfig: SQSClientConfig = {
|
||||
region: env.get('SQS_AWS_REGION'),
|
||||
}
|
||||
if (env.get('SQS_ENDPOINT', true)) {
|
||||
sqsConfig.endpoint = env.get('SQS_ENDPOINT', true)
|
||||
}
|
||||
if (env.get('SQS_ACCESS_KEY_ID', true) && env.get('SQS_SECRET_ACCESS_KEY', true)) {
|
||||
sqsConfig.credentials = {
|
||||
accessKeyId: env.get('SQS_ACCESS_KEY_ID', true),
|
||||
secretAccessKey: env.get('SQS_SECRET_ACCESS_KEY', true),
|
||||
}
|
||||
}
|
||||
|
||||
return new SQSClient(sqsConfig)
|
||||
})
|
||||
|
||||
container.bind<S3Client | undefined>(TYPES.S3).toDynamicValue((context: interfaces.Context) => {
|
||||
const env: Env = context.container.get(TYPES.Env)
|
||||
|
||||
let s3Client = undefined
|
||||
if (env.get('S3_AWS_REGION', true)) {
|
||||
s3Client = new S3Client({
|
||||
apiVersion: 'latest',
|
||||
region: env.get('S3_AWS_REGION', true),
|
||||
})
|
||||
}
|
||||
|
||||
return s3Client
|
||||
})
|
||||
|
||||
// Map
|
||||
container
|
||||
.bind<MapperInterface<Revision, string>>(TYPES.RevisionItemStringMapper)
|
||||
.toDynamicValue(() => new RevisionItemStringMapper())
|
||||
|
||||
// env vars
|
||||
container.bind(TYPES.SQS_QUEUE_URL).toConstantValue(env.get('SQS_QUEUE_URL'))
|
||||
container.bind(TYPES.S3_AWS_REGION).toConstantValue(env.get('S3_AWS_REGION', true))
|
||||
container.bind(TYPES.S3_BACKUP_BUCKET_NAME).toConstantValue(env.get('S3_BACKUP_BUCKET_NAME', true))
|
||||
|
||||
container.bind<DumpRepositoryInterface>(TYPES.DumpRepository).toDynamicValue((context: interfaces.Context) => {
|
||||
const env: Env = context.container.get(TYPES.Env)
|
||||
|
||||
if (env.get('S3_AWS_REGION', true)) {
|
||||
return new S3DumpRepository(
|
||||
context.container.get(TYPES.S3_BACKUP_BUCKET_NAME),
|
||||
context.container.get(TYPES.S3),
|
||||
context.container.get(TYPES.RevisionItemStringMapper),
|
||||
context.container.get(TYPES.Logger),
|
||||
)
|
||||
} else {
|
||||
return new FSDumpRepository(context.container.get(TYPES.RevisionItemStringMapper))
|
||||
}
|
||||
})
|
||||
|
||||
// use cases
|
||||
container.bind<CopyRevisions>(TYPES.CopyRevisions).toDynamicValue((context: interfaces.Context) => {
|
||||
return new CopyRevisions(context.container.get(TYPES.RevisionRepository))
|
||||
})
|
||||
|
||||
// Handlers
|
||||
container
|
||||
.bind<ItemDumpedEventHandler>(TYPES.ItemDumpedEventHandler)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new ItemDumpedEventHandler(
|
||||
context.container.get(TYPES.DumpRepository),
|
||||
context.container.get(TYPES.RevisionRepository),
|
||||
)
|
||||
})
|
||||
container
|
||||
.bind<AccountDeletionRequestedEventHandler>(TYPES.AccountDeletionRequestedEventHandler)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new AccountDeletionRequestedEventHandler(
|
||||
context.container.get(TYPES.RevisionRepository),
|
||||
context.container.get(TYPES.Logger),
|
||||
)
|
||||
})
|
||||
container
|
||||
.bind<RevisionsCopyRequestedEventHandler>(TYPES.RevisionsCopyRequestedEventHandler)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new RevisionsCopyRequestedEventHandler(
|
||||
context.container.get(TYPES.CopyRevisions),
|
||||
context.container.get(TYPES.Logger),
|
||||
)
|
||||
})
|
||||
|
||||
container
|
||||
.bind<DomainEventMessageHandlerInterface>(TYPES.DomainEventMessageHandler)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
const env: Env = context.container.get(TYPES.Env)
|
||||
|
||||
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
|
||||
['ITEM_DUMPED', context.container.get(TYPES.ItemDumpedEventHandler)],
|
||||
['ACCOUNT_DELETION_REQUESTED', context.container.get(TYPES.AccountDeletionRequestedEventHandler)],
|
||||
['REVISIONS_COPY_REQUESTED', context.container.get(TYPES.RevisionsCopyRequestedEventHandler)],
|
||||
])
|
||||
|
||||
const handler =
|
||||
env.get('NEW_RELIC_ENABLED', true) === 'true'
|
||||
? new SQSNewRelicEventMessageHandler(eventHandlers, context.container.get(TYPES.Logger))
|
||||
: new SQSEventMessageHandler(eventHandlers, context.container.get(TYPES.Logger))
|
||||
|
||||
return handler
|
||||
})
|
||||
|
||||
container
|
||||
.bind<DomainEventSubscriberFactoryInterface>(TYPES.DomainEventSubscriberFactory)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new SQSDomainEventSubscriberFactory(
|
||||
context.container.get(TYPES.SQS),
|
||||
context.container.get(TYPES.SQS_QUEUE_URL),
|
||||
context.container.get(TYPES.DomainEventMessageHandler),
|
||||
)
|
||||
})
|
||||
|
||||
return container
|
||||
}
|
||||
}
|
1
packages/revisions/src/Bootstrap/index.ts
Normal file
1
packages/revisions/src/Bootstrap/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './Service'
|
|
@ -1,14 +1,22 @@
|
|||
import { Request, Response } from 'express'
|
||||
import { BaseHttpController, controller, httpDelete, httpGet, results } from 'inversify-express-utils'
|
||||
import { inject } from 'inversify'
|
||||
import { ControllerContainerInterface } from '@standardnotes/domain-core'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { RevisionsController } from '../../Controller/RevisionsController'
|
||||
|
||||
@controller('/items/:itemUuid/revisions', TYPES.ApiGatewayAuthMiddleware)
|
||||
@controller('/items/:itemUuid/revisions', TYPES.Revisions_ApiGatewayAuthMiddleware)
|
||||
export class InversifyExpressRevisionsController extends BaseHttpController {
|
||||
constructor(@inject(TYPES.RevisionsController) private revisionsController: RevisionsController) {
|
||||
constructor(
|
||||
@inject(TYPES.Revisions_RevisionsController) private revisionsController: RevisionsController,
|
||||
@inject(TYPES.Revisions_ControllerContainer) private controllerContainer: ControllerContainerInterface,
|
||||
) {
|
||||
super()
|
||||
|
||||
this.controllerContainer.register('revisions.revisions.getRevisions', this.getRevisions.bind(this))
|
||||
this.controllerContainer.register('revisions.revisions.getRevision', this.getRevision.bind(this))
|
||||
this.controllerContainer.register('revisions.revisions.deleteRevision', this.deleteRevision.bind(this))
|
||||
}
|
||||
|
||||
@httpGet('/')
|
||||
|
|
|
@ -12,7 +12,10 @@ export class RevisionMetadataPersistenceMapper implements MapperInterface<Revisi
|
|||
}
|
||||
const contentType = contentTypeOrError.getValue()
|
||||
|
||||
const datesOrError = Dates.create(projection.createdAt, projection.updatedAt)
|
||||
const createdAt = projection.createdAt instanceof Date ? projection.createdAt : new Date(projection.createdAt)
|
||||
const updatedAt = projection.updatedAt instanceof Date ? projection.updatedAt : new Date(projection.updatedAt)
|
||||
|
||||
const datesOrError = Dates.create(createdAt, updatedAt)
|
||||
if (datesOrError.isFailed()) {
|
||||
throw new Error(`Could not create dates: ${datesOrError.getError()}`)
|
||||
}
|
||||
|
|
1
packages/revisions/src/index.ts
Normal file
1
packages/revisions/src/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './Bootstrap'
|
|
@ -4249,6 +4249,7 @@ __metadata:
|
|||
"@standardnotes/auth-server": "workspace:^"
|
||||
"@standardnotes/domain-core": "workspace:^"
|
||||
"@standardnotes/domain-events-infra": "workspace:^"
|
||||
"@standardnotes/revisions-server": "workspace:^"
|
||||
"@standardnotes/syncing-server": "workspace:^"
|
||||
"@types/cors": "npm:^2.8.9"
|
||||
"@types/express": "npm:^4.17.14"
|
||||
|
@ -4313,7 +4314,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@standardnotes/revisions-server@workspace:packages/revisions":
|
||||
"@standardnotes/revisions-server@workspace:^, @standardnotes/revisions-server@workspace:packages/revisions":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@standardnotes/revisions-server@workspace:packages/revisions"
|
||||
dependencies:
|
||||
|
|
Loading…
Reference in a new issue