Skip to content

Commit

Permalink
refactor: add base class for Stores (#2493)
Browse files Browse the repository at this point in the history
* refactor: add base abstract classes for kv and event stores

* fix: revert filename change for macos e2e github action
  • Loading branch information
EmiM authored May 16, 2024
1 parent 51c9648 commit 5c5d9c8
Show file tree
Hide file tree
Showing 14 changed files with 146 additions and 169 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/e2e-mac.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:

- name: FILE_NAME env
working-directory: ./packages/desktop/dist
run: echo "FILE_NAME="Quiet-$VERSION-arm64.dmg"" >> $GITHUB_ENV
run: echo "FILE_NAME="Quiet-$VERSION.dmg"" >> $GITHUB_ENV

- name: List dist dir content
working-directory: ./packages/desktop/dist
Expand All @@ -50,7 +50,7 @@ jobs:
run: hdiutil mount $FILE_NAME

- name: Add App file to applications
run: cd ~ && cp -R "/Volumes/Quiet $VERSION-arm64/Quiet.app" /Applications
run: cd ~ && cp -R "/Volumes/Quiet $VERSION/Quiet.app" /Applications

- name: Run invitation link test - Includes 2 separate application clients
uses: nick-fields/retry@14672906e672a08bd6eeb15720e9ed3ce869cdd4 # v2.9.0
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* Cleanup data directory at end of e2e tests
* Update github workflows for PR gating ([#2487](https://github.com/TryQuiet/quiet/issues/2487))
* Don't create duplicate CSRs when joining a community under certain circumstances ([#2321](https://github.com/TryQuiet/quiet/issues/2321))
* Add abstract base classes for stores ([#2407](https://github.com/TryQuiet/quiet/issues/2407))

[2.2.0]

Expand Down
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 {
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 @@ -158,10 +158,10 @@ describe('RegistrationService', () => {
const peerId = await PeerId.create()
const ipfs = await create()
const loadAllCertificates = async () => {
return await certificatesStore.loadAllCertificates()
return await certificatesStore.getEntries()
}
const saveCertificate = async (payload: SaveCertificatePayload) => {
await certificatesStore.addCertificate(payload.certificate)
await certificatesStore.addEntry(payload.certificate)
}

await orbitDb.create(peerId, ipfs)
Expand Down Expand Up @@ -202,7 +202,7 @@ describe('RegistrationService', () => {

await new Promise(r => setTimeout(r, 2000))

expect((await certificatesStore.loadAllCertificates()).length).toEqual(1)
expect((await certificatesStore.getEntries()).length).toEqual(1)

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

const logger = createLogger('store')

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> {
logger.info('Closing', this.getAddress().path)
await this.store?.close()
logger.info('Closed', this.getAddress().path)
}

abstract init(): Promise<void>
abstract clean(): void
}

export abstract class KeyValueStoreBase<V> extends StoreBase<V, KeyValueStore<V>> {
protected store: KeyValueStore<V> | undefined
abstract setEntry(key: string, value: V): Promise<V>
abstract getEntry(key?: string): V | null
}

export abstract class EventStoreBase<V> extends StoreBase<V, EventStore<V>> {
protected store: EventStore<V> | undefined
abstract addEntry(value: V): Promise<string>
abstract getEntries(): Promise<V[]>
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,15 @@ 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))
}

await certificatesRequestsStore.getCsrs()
await certificatesRequestsStore.getEntries()

const filteredCsrs = await certificatesRequestsStore.getCsrs()
const filteredCsrs = await certificatesRequestsStore.getEntries()

expect(filteredCsrs.length).toEqual(allCsrs.length - 1)
expect(filteredCsrs).toEqual([
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,17 @@
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 { createLogger } from '../../common/logger'
import { EventStoreBase } from '../base.store'

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

private readonly logger = createLogger(CertificatesRequestsStore.name)
export class CertificatesRequestsStore extends EventStoreBase<string> {
protected readonly logger = createLogger(CertificatesRequestsStore.name)

constructor(private readonly orbitDbService: OrbitDb) {
super()
Expand Down Expand Up @@ -46,23 +43,14 @@ export class CertificatesRequestsStore extends EventEmitter {

public async loadedCertificateRequests() {
this.emit(StorageEvents.CSRS_STORED, {
csrs: await this.getCsrs(),
csrs: await this.getEntries(),
})
}

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

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.info('Adding CSR to database')
await this.store?.add(csr)
return csr
}

public async validateUserCsr(csr: string) {
Expand All @@ -88,9 +76,9 @@ export class CertificatesRequestsStore extends EventEmitter {
return validationErrors
}

public async getCsrs() {
public async getEntries() {
const filteredCsrsMap: Map<string, string> = new Map()
const allEntries = this.store
const allEntries = this.getStore()
.iterator({ limit: -1 })
.collect()
.map(e => {
Expand Down Expand Up @@ -122,9 +110,7 @@ export class CertificatesRequestsStore extends EventEmitter {
}

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

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

certificatesStore.updateMetadata(communityMetadata)

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

const certificates = await certificatesStore.getCertificates()
const certificates = await certificatesStore.getEntries()

expect(certificates).toContain(certificate)
})
Expand All @@ -146,9 +146,9 @@ describe('CertificatesStore', () => {

certificatesStore.updateMetadata(communityMetadata)

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

const certificates = await certificatesStore.getCertificates()
const certificates = await certificatesStore.getEntries()

expect(certificates).not.toContain(certificate)
})
Expand All @@ -159,9 +159,9 @@ describe('CertificatesStore', () => {

certificatesStore.updateMetadata(communityMetadata)

jest.spyOn(certificatesStore, 'getCertificates').mockResolvedValue([certificate1, certificate2])
jest.spyOn(certificatesStore, 'getEntries').mockResolvedValue([certificate1, certificate2])

const certificates = await certificatesStore.loadAllCertificates()
const certificates = await certificatesStore.getEntries()

expect(certificates).toContain(certificate1)
expect(certificates).toContain(certificate2)
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,7 +1,5 @@
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'
import {
keyFromCertificate,
Expand All @@ -16,16 +14,16 @@ import { CertificateData } from '../../registration/registration.functions'
import { OrbitDb } from '../orbitDb/orbitDb.service'
import { Injectable } from '@nestjs/common'
import { createLogger } from '../../common/logger'
import { EventStoreBase } from '../base.store'

@Injectable()
export class CertificatesStore extends EventEmitter {
public store: EventStore<string>
export class CertificatesStore extends EventStoreBase<string> {
protected readonly logger = createLogger(CertificatesStore.name)

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

private readonly logger = createLogger(CertificatesStore.name)

constructor(private readonly orbitDbService: OrbitDb) {
super()
this.filteredCertificatesMapping = new Map()
Expand Down Expand Up @@ -65,29 +63,14 @@ export class CertificatesStore extends EventEmitter {

public async loadedCertificates() {
this.emit(StorageEvents.CERTIFICATES_STORED, {
certificates: await this.getCertificates(),
certificates: await this.getEntries(),
})
}

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

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

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

public async loadAllCertificates() {
const certificates = await this.getCertificates()
return certificates
return certificate
}

public updateMetadata(metadata: CommunityMetadata) {
Expand Down Expand Up @@ -147,14 +130,9 @@ export class CertificatesStore extends EventEmitter {
* as specified in the comment section of
* https://github.com/TryQuiet/quiet/issues/1899
*/
public async getCertificates(): Promise<string[]> {
public async getEntries(): Promise<string[]> {
this.logger.info('Getting certificates')
if (!this.store) {
this.logger.warn('No store found!')
return []
}

const allCertificates = this.store
const allCertificates = this.getStore()
.iterator({ limit: -1 })
.collect()
.map(e => e.payload.value)
Expand Down Expand Up @@ -199,16 +177,14 @@ export class CertificatesStore extends EventEmitter {
if (cache) return cache

// Perform cryptographic operations and populate cache
await this.getCertificates()
await this.getEntries()

// Return desired data from updated cache
return this.usernameMapping.get(pubkey)
}

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

// @ts-ignore
this.logger.info('Cleaning certificates store')
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,8 +109,8 @@ describe('CommmunityMetadataStore', () => {

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

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

expect(ret).toStrictEqual(undefined)
expect(meta).toEqual(undefined)
expect(communityMetadataStore.setEntry(metaInvalid.id, metaInvalid)).rejects.toThrow()
const meta = communityMetadataStore.getEntry()
expect(meta).toEqual(null)
})
})

Expand Down
Loading

0 comments on commit 5c5d9c8

Please sign in to comment.