Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: add base class for Stores #2493

Merged
merged 12 commits into from
May 16, 2024
5 changes: 0 additions & 5 deletions packages/backend/src/nest/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@ export interface PublicChannelsRepo {
eventsAttached: boolean
}

export interface DirectMessagesRepo {
EmiM marked this conversation as resolved.
Show resolved Hide resolved
db: EventStore<string>
eventsAttached: boolean
}

export type ChannelInfoResponse = Record<string, PublicChannel>

export class StorageOptions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ describe('RegistrationService', () => {
return await certificatesStore.loadAllCertificates()
}
const saveCertificate = async (payload: SaveCertificatePayload) => {
await certificatesStore.addCertificate(payload.certificate)
await certificatesStore.addEntry(payload.certificate)
}

await orbitDb.create(peerId, ipfs)
Expand Down
27 changes: 27 additions & 0 deletions packages/backend/src/nest/storage/base.store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import KeyValueStore from 'orbit-db-kvstore'
import Store from 'orbit-db-store'
import EventStore from 'orbit-db-eventstore'
import { EventEmitter } from 'events'

export default abstract class StoreBase<V, S extends KeyValueStore<V> | EventStore<V>> extends EventEmitter {
protected abstract store: S | undefined

getStore() {
if (!this.store) {
throw new Error('Store not initialized')
}
return this.store
}

getAddress(): Store['address'] {
return this.getStore().address
}

async close(): Promise<void> {
await this.store?.close()
EmiM marked this conversation as resolved.
Show resolved Hide resolved
}

abstract init(): Promise<void>
abstract addEntry(entry: V): Promise<V | undefined>
EmiM marked this conversation as resolved.
Show resolved Hide resolved
abstract clean(): void
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ describe('CertificatesRequestsStore', () => {
]

for (const csr of allCsrs) {
await certificatesRequestsStore.addUserCsr(csr)
await certificatesRequestsStore.addEntry(csr)
// This should not be there, there's bug in orbitdb, it breaks if we add entries without artificial sleep, tho it's awaited.
// https://github.com/TryQuiet/quiet/issues/2121
await new Promise<void>(resolve => setTimeout(() => resolve(), 500))
Expand All @@ -103,7 +103,7 @@ describe('CertificatesRequestsStore', () => {
const spy = jest.fn()

certificatesRequestsStore.on(StorageEvents.CSRS_STORED, spy)
await replicatedEvent(certificatesRequestsStore.store)
await replicatedEvent(certificatesRequestsStore.getStore())

expect(spy).toBeCalledTimes(1)
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import { getCrypto } from 'pkijs'
import { EventEmitter } from 'events'
import EventStore from 'orbit-db-eventstore'
import { NoCryptoEngineError } from '@quiet/types'
import { loadCSR, keyFromCertificate } from '@quiet/identity'
import { CsrReplicatedPromiseValues, StorageEvents } from '../storage.types'
import { StorageEvents } from '../storage.types'
import { validate } from 'class-validator'
import { UserCsrData } from '../../registration/registration.functions'
import { Injectable } from '@nestjs/common'
import { OrbitDb } from '../orbitDb/orbitDb.service'
import Logger from '../../common/logger'
import StoreBase from '../base.store'

@Injectable()
export class CertificatesRequestsStore extends EventEmitter {
public store: EventStore<string>

private readonly logger = Logger(CertificatesRequestsStore.name)
export class CertificatesRequestsStore extends StoreBase<string, EventStore<string>> {
protected readonly logger = Logger(CertificatesRequestsStore.name)
protected store: EventStore<string> | undefined

constructor(private readonly orbitDbService: OrbitDb) {
super()
Expand Down Expand Up @@ -51,19 +50,10 @@ export class CertificatesRequestsStore extends EventEmitter {
})
}

public async close() {
this.logger('Closing...')
await this.store?.close()
this.logger('Closed')
}

public getAddress() {
return this.store?.address
}

public async addUserCsr(csr: string) {
await this.store.add(csr)
return true
public async addEntry(csr: string): Promise<string> {
this.logger('Adding CSR to database')
await this.store?.add(csr)
return csr
}

public static async validateUserCsr(csr: string) {
Expand Down Expand Up @@ -93,7 +83,7 @@ export class CertificatesRequestsStore extends EventEmitter {
const filteredCsrsMap: Map<string, string> = new Map()
// @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options'
await this.store.load({ fetchEntryTimeout: 15000 })
const allEntries = this.store
const allEntries = this.getStore()
.iterator({ limit: -1 })
.collect()
.map(e => {
Expand Down Expand Up @@ -126,8 +116,6 @@ export class CertificatesRequestsStore extends EventEmitter {

public clean() {
// FIXME: Add correct typings on object fields.

// @ts-ignore
this.store = undefined
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ describe('CertificatesStore', () => {

certificatesStore.updateMetadata(communityMetadata)

await certificatesStore.addCertificate(certificate)
await certificatesStore.addEntry(certificate)

const certificates = await certificatesStore.getCertificates()

Expand All @@ -146,7 +146,7 @@ describe('CertificatesStore', () => {

certificatesStore.updateMetadata(communityMetadata)

await certificatesStore.addCertificate(certificate)
await certificatesStore.addEntry(certificate)

const certificates = await certificatesStore.getCertificates()

Expand All @@ -172,7 +172,7 @@ describe('CertificatesStore', () => {

certificatesStore.updateMetadata(communityMetadata)

await certificatesStore.addCertificate(certificate)
await certificatesStore.addEntry(certificate)

const result = await certificatesStore.getCertificateUsername(pubkey)
expect(result).toBe(username)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { getCrypto } from 'pkijs'
import { EventEmitter } from 'events'
import { StorageEvents } from '../storage.types'
import EventStore from 'orbit-db-eventstore'
import { CommunityMetadata, NoCryptoEngineError } from '@quiet/types'
Expand All @@ -16,16 +15,17 @@ import { CertificateData } from '../../registration/registration.functions'
import { OrbitDb } from '../orbitDb/orbitDb.service'
import { Injectable } from '@nestjs/common'
import Logger from '../../common/logger'
import StoreBase from '../base.store'

@Injectable()
export class CertificatesStore extends EventEmitter {
public store: EventStore<string>
export class CertificatesStore extends StoreBase<string, EventStore<string>> {
protected readonly logger = Logger(CertificatesStore.name)
protected store: EventStore<string> | undefined

private metadata: CommunityMetadata | undefined
private filteredCertificatesMapping: Map<string, Partial<UserData>>
private usernameMapping: Map<string, string>

private readonly logger = Logger(CertificatesStore.name)

constructor(private readonly orbitDbService: OrbitDb) {
super()
this.filteredCertificatesMapping = new Map()
Expand Down Expand Up @@ -70,18 +70,10 @@ export class CertificatesStore extends EventEmitter {
})
}

public async close() {
await this.store?.close()
}

public getAddress() {
return this.store?.address
}

public async addCertificate(certificate: string) {
public async addEntry(certificate: string): Promise<string> {
this.logger('Adding user certificate')
await this.store?.add(certificate)
return true
return certificate
}

public async loadAllCertificates() {
Expand Down Expand Up @@ -206,8 +198,6 @@ export class CertificatesStore extends EventEmitter {

public clean() {
// FIXME: Add correct typings on object fields.

// @ts-ignore
this.store = undefined
this.metadata = undefined
this.filteredCertificatesMapping = new Map()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ describe('CommmunityMetadataStore', () => {

describe('updateCommunityMetadata', () => {
test('updates community metadata if the metadata is valid', async () => {
const ret = await communityMetadataStore.updateCommunityMetadata(metaValid)
const ret = await communityMetadataStore.addEntry(metaValid)
const meta = communityMetadataStore.getCommunityMetadata()

expect(ret).toStrictEqual(metaValidWithOwnerId)
Expand All @@ -121,7 +121,7 @@ describe('CommmunityMetadataStore', () => {
...metaValid,
rootCa: 'Something invalid!',
}
const ret = await communityMetadataStore.updateCommunityMetadata(metaInvalid)
const ret = await communityMetadataStore.addEntry(metaInvalid)
const meta = communityMetadataStore.getCommunityMetadata()

expect(ret).toStrictEqual(undefined)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { EventEmitter } from 'events'
import KeyValueStore from 'orbit-db-kvstore'
import { IdentityProvider } from 'orbit-db-identity-provider'
// @ts-ignore Hacking around ipfs-log not exporting Entry
Expand All @@ -10,14 +9,15 @@ import { KeyValueIndex } from '../orbitDb/keyValueIndex'
import { LocalDbService } from '../../local-db/local-db.service'
import { OrbitDb } from '../orbitDb/orbitDb.service'
import { Injectable } from '@nestjs/common'
import createLogger from '../../common/logger'
import Logger from '../../common/logger'
import { constructPartial } from '@quiet/common'
import StoreBase from '../base.store'

const logger = createLogger('CommunityMetadataStore')
const logger = Logger('communityMetadataStore')

@Injectable()
export class CommunityMetadataStore extends EventEmitter {
public store: KeyValueStore<CommunityMetadata>
export class CommunityMetadataStore extends StoreBase<CommunityMetadata, KeyValueStore<CommunityMetadata>> {
protected store: KeyValueStore<CommunityMetadata> | undefined

constructor(
private readonly orbitDbService: OrbitDb,
Expand Down Expand Up @@ -85,15 +85,7 @@ export class CommunityMetadataStore extends EventEmitter {
logger('Loaded community metadata to memory')
}

public getAddress() {
return this.store?.address
}

public async close() {
await this.store?.close()
}

public async updateCommunityMetadata(newMeta: CommunityMetadata): Promise<CommunityMetadata | undefined> {
public async addEntry(newMeta: CommunityMetadata): Promise<CommunityMetadata | undefined> {
try {
// TODO: Also check OrbitDB identity when updating community metadata
const valid = await CommunityMetadataStore.validateCommunityMetadata(newMeta)
Expand All @@ -109,7 +101,7 @@ export class CommunityMetadataStore extends EventEmitter {

// FIXME: update community metadata if it has changed (so that
// we can migrate community metadata easily)
const oldMeta = this.store.get(newMeta.id)
const oldMeta = this.getStore().get(newMeta.id)
if (oldMeta?.ownerCertificate && oldMeta?.rootCa) {
return oldMeta
}
Expand Down Expand Up @@ -137,7 +129,7 @@ export class CommunityMetadataStore extends EventEmitter {
// validateCommunityMetadataEntry and so validation may pass in
// this method, but still the entry is not added to the internal
// index. How can we detect that?
await this.store.put(meta.id, meta)
await this.getStore().put(meta.id, meta)

return meta
} catch (err) {
Expand Down Expand Up @@ -208,7 +200,7 @@ export class CommunityMetadataStore extends EventEmitter {
}

public getCommunityMetadata(): CommunityMetadata | undefined {
const metadata = Object.values(this.store.all)
const metadata = Object.values(this.getStore().all)

if (metadata.length > 0) {
return metadata[0]
Expand All @@ -217,8 +209,6 @@ export class CommunityMetadataStore extends EventEmitter {

public clean() {
// FIXME: Add correct typings on object fields.

// @ts-ignore
this.store = undefined
}
}
Expand Down
22 changes: 11 additions & 11 deletions packages/backend/src/nest/storage/storage.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,20 @@ import { LocalDBKeys } from '../local-db/local-db.types'
import { LocalDbService } from '../local-db/local-db.service'
import { LazyModuleLoader } from '@nestjs/core'
import Logger from '../common/logger'
import { DirectMessagesRepo, PublicChannelsRepo } from '../common/types'
import { PublicChannelsRepo } from '../common/types'
import { removeFiles, removeDirs, createPaths } from '../common/utils'
import { DBOptions, StorageEvents } from './storage.types'
import { CertificatesStore } from './certificates/certificates.store'
import { CertificatesRequestsStore } from './certifacteRequests/certificatesRequestsStore'
import { OrbitDb } from './orbitDb/orbitDb.service'
import { CommunityMetadataStore } from './communityMetadata/communityMetadata.store'
import { UserProfileStore } from './userProfile/userProfile.store'
import StoreBase from './base.store'
import Store from 'orbit-db-store'

@Injectable()
export class StorageService extends EventEmitter {
public publicChannelsRepos: Map<string, PublicChannelsRepo> = new Map()
public directMessagesRepos: Map<string, DirectMessagesRepo> = new Map()
private publicKeysMap: Map<string, CryptoKey> = new Map()

public certificates: EventStore<string>
Expand Down Expand Up @@ -309,7 +310,7 @@ export class StorageService extends EventEmitter {
}

public async updateCommunityMetadata(communityMetadata: CommunityMetadata): Promise<CommunityMetadata | undefined> {
EmiM marked this conversation as resolved.
Show resolved Hide resolved
const meta = await this.communityMetadataStore?.updateCommunityMetadata(communityMetadata)
const meta = await this.communityMetadataStore?.addEntry(communityMetadata)
if (meta) {
this.certificatesStore.updateMetadata(meta)
}
Expand Down Expand Up @@ -736,13 +737,12 @@ export class StorageService extends EventEmitter {
return false
}
this.logger('Saving certificate...')
const result = await this.certificatesStore.addCertificate(payload.certificate)
return result
await this.certificatesStore.addEntry(payload.certificate)
return true
}

public async saveCSR(payload: SaveCSRPayload): Promise<boolean> {
const result = await this.certificatesRequestsStore.addUserCsr(payload.csr)
return result
public async saveCSR(payload: SaveCSRPayload): Promise<void> {
await this.certificatesRequestsStore.addEntry(payload.csr)
}

/**
Expand Down Expand Up @@ -802,7 +802,7 @@ export class StorageService extends EventEmitter {
/**
* Check if given username is already in use
*/
const certificates = this.getAllEventLogEntries(this.certificatesStore.store)
const certificates = this.getAllEventLogEntries(this.certificatesStore.getStore())
for (const cert of certificates) {
const parsedCert = parseCertificate(cert)
const certUsername = getCertFieldValue(parsedCert, CertFieldsTypes.nickName)
Expand Down Expand Up @@ -836,7 +836,7 @@ export class StorageService extends EventEmitter {
}

public async addUserProfile(profile: UserProfile) {
await this.userProfileStore.addUserProfile(profile)
await this.userProfileStore.addEntry(profile)
}

public async checkIfFileExist(filepath: string): Promise<boolean> {
Expand All @@ -854,7 +854,6 @@ export class StorageService extends EventEmitter {
this.messageThreads = undefined
// @ts-ignore
this.publicChannelsRepos = new Map()
this.directMessagesRepos = new Map()
this.publicKeysMap = new Map()
// @ts-ignore
this.ipfs = null
Expand All @@ -865,5 +864,6 @@ export class StorageService extends EventEmitter {
this.certificatesRequestsStore.clean()
this.certificatesStore.clean()
this.communityMetadataStore.clean()
this.userProfileStore.clean()
}
}
Loading
Loading