feat: add shared vaults model. (#631)

Co-authored-by: Mo <mo@standardnotes.com>
This commit is contained in:
Karol Sójko 2023-06-30 12:44:27 +02:00 committed by GitHub
parent 060206ddd4
commit 38e77f04be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 209 additions and 3 deletions

3
.pnp.cjs generated
View file

@ -5179,6 +5179,7 @@ const RAW_RUNTIME_STATE =
["@standardnotes/responses", "npm:1.13.24"],\
["@standardnotes/security", "workspace:packages/security"],\
["@standardnotes/settings", "workspace:packages/settings"],\
["@standardnotes/sncrypto-node", "workspace:packages/sncrypto-node"],\
["@standardnotes/time", "workspace:packages/time"],\
["@types/cors", "npm:2.8.13"],\
["@types/dotenv", "npm:8.2.0"],\
@ -5188,6 +5189,7 @@ const RAW_RUNTIME_STATE =
["@types/newrelic", "npm:9.14.0"],\
["@types/node", "npm:20.2.5"],\
["@types/prettyjson", "npm:0.0.30"],\
["@types/semver", "npm:7.5.0"],\
["@types/ua-parser-js", "npm:0.7.36"],\
["@types/uuid", "npm:8.3.4"],\
["@typescript-eslint/eslint-plugin", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:5.59.8"],\
@ -5210,6 +5212,7 @@ const RAW_RUNTIME_STATE =
["prettier", "npm:2.8.8"],\
["prettyjson", "npm:1.2.5"],\
["reflect-metadata", "npm:0.1.13"],\
["semver", "npm:7.5.1"],\
["sqlite3", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:5.1.6"],\
["ts-jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.1.0"],\
["typeorm", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:0.3.16"],\

View file

@ -20,7 +20,7 @@ export class Dates extends ValueObject<DatesProps> {
return Result.fail<Dates>(`Could not create Dates. Creation date should be a date object, given: ${createdAt}`)
}
if (!(updatedAt instanceof Date)) {
return Result.fail<Dates>(`Could not create Dates. Update date should be a date object, given: ${createdAt}`)
return Result.fail<Dates>(`Could not create Dates. Update date should be a date object, given: ${updatedAt}`)
}
return Result.ok<Dates>(new Dates({ createdAt, updatedAt }))

View file

@ -0,0 +1,30 @@
import { Result } from '../Core/Result'
import { ValueObject } from '../Core/ValueObject'
import { TimestampsProps } from './TimestampsProps'
export class Timestamps extends ValueObject<TimestampsProps> {
get createdAt(): number {
return this.props.createdAt
}
get updatedAt(): number {
return this.props.updatedAt
}
private constructor(props: TimestampsProps) {
super(props)
}
static create(createdAt: number, updatedAt: number): Result<Timestamps> {
if (isNaN(createdAt)) {
return Result.fail<Timestamps>(
`Could not create Timestamps. Creation date should be a number, given: ${createdAt}`,
)
}
if (isNaN(updatedAt)) {
return Result.fail<Timestamps>(`Could not create Timestamps. Update date should be a number, given: ${updatedAt}`)
}
return Result.ok<Timestamps>(new Timestamps({ createdAt, updatedAt }))
}
}

View file

@ -0,0 +1,4 @@
export interface TimestampsProps {
createdAt: number
updatedAt: number
}

View file

@ -17,6 +17,8 @@ export * from './Common/RoleName'
export * from './Common/RoleNameProps'
export * from './Common/RoleNameCollection'
export * from './Common/RoleNameCollectionProps'
export * from './Common/Timestamps'
export * from './Common/TimestampsProps'
export * from './Common/Username'
export * from './Common/UsernameProps'
export * from './Common/Uuid'

View file

@ -20,6 +20,9 @@ DB_TYPE=mysql
REDIS_URL=redis://cache
CACHE_TYPE=redis
VALET_TOKEN_SECRET=change-me-!
VALET_TOKEN_TTL=1000
SNS_TOPIC_ARN=
SNS_AWS_REGION=
SQS_QUEUE_URL=

View file

@ -24,7 +24,8 @@
"start": "yarn node dist/bin/server.js",
"worker": "yarn node dist/bin/worker.js",
"content-size": "yarn node dist/bin/content.js",
"upgrade:snjs": "yarn ncu -u '@standardnotes/*'"
"upgrade:snjs": "yarn ncu -u '@standardnotes/*'",
"migrate": "yarn clean && yarn build && yarn typeorm migration:run -d dist/src/Bootstrap/DataSource.js"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.332.0",
@ -38,6 +39,7 @@
"@standardnotes/responses": "^1.13.9",
"@standardnotes/security": "workspace:*",
"@standardnotes/settings": "workspace:*",
"@standardnotes/sncrypto-node": "workspace:*",
"@standardnotes/time": "workspace:*",
"axios": "^1.1.3",
"cors": "2.8.5",
@ -51,6 +53,7 @@
"nodemon": "^2.0.19",
"prettyjson": "^1.2.5",
"reflect-metadata": "0.1.13",
"semver": "^7.5.1",
"sqlite3": "^5.1.6",
"typeorm": "^0.3.15",
"ua-parser-js": "^1.0.32",
@ -65,6 +68,7 @@
"@types/jsonwebtoken": "^9.0.1",
"@types/node": "^20.2.5",
"@types/prettyjson": "^0.0.30",
"@types/semver": "^7.5.0",
"@types/ua-parser-js": "^0.7.36",
"@types/uuid": "^8.3.0",
"@typescript-eslint/eslint-plugin": "^5.59.2",

View file

@ -0,0 +1,17 @@
import { Entity, Result, UniqueEntityId } from '@standardnotes/domain-core'
import { SharedVaultProps } from './SharedVaultProps'
export class SharedVault extends Entity<SharedVaultProps> {
get id(): UniqueEntityId {
return this._id
}
private constructor(props: SharedVaultProps, id?: UniqueEntityId) {
super(props, id)
}
static create(props: SharedVaultProps, id?: UniqueEntityId): Result<SharedVault> {
return Result.ok<SharedVault>(new SharedVault(props, id))
}
}

View file

@ -0,0 +1,8 @@
import { Uuid, Timestamps } from '@standardnotes/domain-core'
export interface SharedVaultProps {
userUuid: Uuid
fileUploadBytesUsed: number
fileUploadBytesLimit: number
timestamps: Timestamps
}

View file

@ -0,0 +1,7 @@
import { SharedVault } from './SharedVault'
export interface SharedVaultsRepositoryInterface {
findByUuid(uuid: string): Promise<SharedVault | null>
save(sharedVault: SharedVault): Promise<void>
remove(sharedVault: SharedVault): Promise<void>
}

View file

@ -0,0 +1,38 @@
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
@Entity({ name: 'shared_vaults' })
export class TypeORMSharedVault {
@PrimaryGeneratedColumn('uuid')
declare uuid: string
@Column({
name: 'user_uuid',
length: 36,
})
@Index('index_shared_vaults_on_user_uuid')
declare userUuid: string
@Column({
name: 'file_upload_bytes_used',
type: 'int',
})
declare fileUploadBytesUsed: number
@Column({
name: 'file_upload_bytes_limit',
type: 'int',
})
declare fileUploadBytesLimit: number
@Column({
name: 'created_at_timestamp',
type: 'bigint',
})
declare createdAtTimestamp: number
@Column({
name: 'updated_at_timestamp',
type: 'bigint',
})
declare updatedAtTimestamp: number
}

View file

@ -0,0 +1,38 @@
import { Repository } from 'typeorm'
import { MapperInterface } from '@standardnotes/domain-core'
import { SharedVaultsRepositoryInterface } from '../../Domain/SharedVault/SharedVaultsRepositoryInterface'
import { TypeORMSharedVault } from './TypeORMSharedVault'
import { SharedVault } from '../../Domain/SharedVault/SharedVault'
export class TypeORMSharedVaultRepository implements SharedVaultsRepositoryInterface {
constructor(
private ormRepository: Repository<TypeORMSharedVault>,
private mapper: MapperInterface<SharedVault, TypeORMSharedVault>,
) {}
async save(sharedVault: SharedVault): Promise<void> {
const persistence = this.mapper.toProjection(sharedVault)
await this.ormRepository.save(persistence)
}
async findByUuid(uuid: string): Promise<SharedVault | null> {
const persistence = await this.ormRepository
.createQueryBuilder('shared_vault')
.where('shared_vault.uuid = :uuid', {
uuid,
})
.getOne()
if (persistence === null) {
return null
}
return this.mapper.toDomain(persistence)
}
async remove(sharedVault: SharedVault): Promise<void> {
await this.ormRepository.remove(this.mapper.toProjection(sharedVault))
}
}

View file

@ -0,0 +1,49 @@
import { Timestamps, MapperInterface, UniqueEntityId, Uuid } from '@standardnotes/domain-core'
import { SharedVault } from '../Domain/SharedVault/SharedVault'
import { TypeORMSharedVault } from '../Infra/TypeORM/TypeORMSharedVault'
export class SharedVaultPersistenceMapper implements MapperInterface<SharedVault, TypeORMSharedVault> {
toDomain(projection: TypeORMSharedVault): SharedVault {
const userUuidOrError = Uuid.create(projection.userUuid)
if (userUuidOrError.isFailed()) {
throw new Error(`Failed to create shared vault from projection: ${userUuidOrError.getError()}`)
}
const userUuid = userUuidOrError.getValue()
const timestampsOrError = Timestamps.create(projection.createdAtTimestamp, projection.updatedAtTimestamp)
if (timestampsOrError.isFailed()) {
throw new Error(`Failed to create shared vault from projection: ${timestampsOrError.getError()}`)
}
const timestamps = timestampsOrError.getValue()
const sharedVaultOrError = SharedVault.create(
{
userUuid,
fileUploadBytesUsed: projection.fileUploadBytesUsed,
fileUploadBytesLimit: projection.fileUploadBytesLimit,
timestamps,
},
new UniqueEntityId(projection.uuid),
)
if (sharedVaultOrError.isFailed()) {
throw new Error(`Failed to create shared vault from projection: ${sharedVaultOrError.getError()}`)
}
const sharedVault = sharedVaultOrError.getValue()
return sharedVault
}
toProjection(domain: SharedVault): TypeORMSharedVault {
const typeorm = new TypeORMSharedVault()
typeorm.uuid = domain.id.toString()
typeorm.userUuid = domain.props.userUuid.value
typeorm.fileUploadBytesUsed = domain.props.fileUploadBytesUsed
typeorm.fileUploadBytesLimit = domain.props.fileUploadBytesLimit
typeorm.createdAtTimestamp = domain.props.timestamps.createdAt
typeorm.updatedAtTimestamp = domain.props.timestamps.updatedAt
return typeorm
}
}

View file

@ -4088,6 +4088,7 @@ __metadata:
"@standardnotes/responses": "npm:^1.13.9"
"@standardnotes/security": "workspace:*"
"@standardnotes/settings": "workspace:*"
"@standardnotes/sncrypto-node": "workspace:*"
"@standardnotes/time": "workspace:*"
"@types/cors": "npm:^2.8.9"
"@types/dotenv": "npm:^8.2.0"
@ -4097,6 +4098,7 @@ __metadata:
"@types/newrelic": "npm:^9.13.0"
"@types/node": "npm:^20.2.5"
"@types/prettyjson": "npm:^0.0.30"
"@types/semver": "npm:^7.5.0"
"@types/ua-parser-js": "npm:^0.7.36"
"@types/uuid": "npm:^8.3.0"
"@typescript-eslint/eslint-plugin": "npm:^5.59.2"
@ -4119,6 +4121,7 @@ __metadata:
prettier: "npm:^2.8.8"
prettyjson: "npm:^1.2.5"
reflect-metadata: "npm:0.1.13"
semver: "npm:^7.5.1"
sqlite3: "npm:^5.1.6"
ts-jest: "npm:^29.1.0"
typeorm: "npm:^0.3.15"
@ -4656,7 +4659,7 @@ __metadata:
languageName: node
linkType: hard
"@types/semver@npm:^7.3.12":
"@types/semver@npm:^7.3.12, @types/semver@npm:^7.5.0":
version: 7.5.0
resolution: "@types/semver@npm:7.5.0"
checksum: dac255fae68157aec375fdb79d483a161c1b9c58e0ab9e18936dd1e9b89dd0ff85d64e482b1505de7e17455b404a0a530c4f9ddd6f21d333c2311c0068687b14