All files / src/storage StorageReader.ts

71.69% Statements 38/53
44.44% Branches 12/27
92.3% Functions 12/13
80.85% Lines 38/47

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 16957x 57x                           57x       27651x     72x       25x     166x       117x   117x                                                                                   146x       47x                   475x 475x 475x       475x 475x     475x                     12913x 12913x 12913x 12913x       12913x 12913x 12913x     12913x         51525x 13583x   37942x 51525x       12913x 12913x       205x 205x 205x   205x         205x 205x     205x                          
import { sdk, table, validateSecondsSinceEpoch, verifyOneOrNone, verifyTruthy } from "../index.client";
import { getSyncChunk } from "./methods/getSyncChunk";
 
/**
 * The `StorageReader` abstract class is the base of the concrete wallet storage provider classes.
 * 
 * It is the minimal interface required to read all wallet state records and is the base class for sync readers.
 * 
 * The next class in the heirarchy is the `StorageReaderWriter` which supports sync readers and writers.
 * 
 * The last class in the heirarchy is the `Storage` class which supports all active wallet operations.
 * 
 * The ability to construct a properly configured instance of this class implies authentication.
 * As such there are no user specific authenticated access checks implied in the implementation of any of these methods. 
 */
export abstract class StorageReader implements sdk.StorageSyncReader {
    chain: sdk.Chain
    _settings?: table.Settings
    whenLastAccess?: Date
    get dbtype(): DBType | undefined { return this._settings?.dbtype }
 
    constructor(options: StorageReaderOptions) {
        this.chain = options.chain
    }
 
    isAvailable(): boolean {
        return !!this._settings
    }
    async makeAvailable(): Promise<table.Settings> {
        return this._settings = await this.readSettings()
    }
 
    getSettings() : table.Settings {
        Iif (!this._settings)
            throw new sdk.WERR_INVALID_OPERATION('must call "makeAvailable" before accessing "settings"');
        return this._settings
    }
 
    isStorageProvider(): boolean { return false }
 
    abstract destroy(): Promise<void>
 
    abstract transaction<T>(scope: (trx: sdk.TrxToken) => Promise<T>, trx?: sdk.TrxToken): Promise<T>
 
    abstract readSettings(trx?: sdk.TrxToken): Promise<table.Settings>
 
    abstract findCertificateFields(args: sdk.FindCertificateFieldsArgs): Promise<table.CertificateField[]>
    abstract findCertificates(args: sdk.FindCertificatesArgs): Promise<table.Certificate[]>
    abstract findCommissions(args: sdk.FindCommissionsArgs): Promise<table.Commission[]>
    abstract findMonitorEvents(args: sdk.FindMonitorEventsArgs): Promise<table.MonitorEvent[]>
    abstract findOutputBaskets(args: sdk.FindOutputBasketsArgs): Promise<table.OutputBasket[]>
    abstract findOutputs(args: sdk.FindOutputsArgs): Promise<table.Output[]>
    abstract findOutputTags(args: sdk.FindOutputTagsArgs): Promise<table.OutputTag[]>
    abstract findSyncStates(args: sdk.FindSyncStatesArgs): Promise<table.SyncState[]>
    abstract findTransactions(args: sdk.FindTransactionsArgs): Promise<table.Transaction[]>
    abstract findTxLabels(args: sdk.FindTxLabelsArgs): Promise<table.TxLabel[]>
    abstract findUsers(args: sdk.FindUsersArgs ): Promise<table.User[]>
 
    abstract countCertificateFields(args: sdk.FindCertificateFieldsArgs) : Promise<number>
    abstract countCertificates(args: sdk.FindCertificatesArgs) : Promise<number>
    abstract countCommissions(args: sdk.FindCommissionsArgs) : Promise<number>
    abstract countMonitorEvents(args: sdk.FindMonitorEventsArgs): Promise<number>
    abstract countOutputBaskets(args: sdk.FindOutputBasketsArgs) : Promise<number>
    abstract countOutputs(args: sdk.FindOutputsArgs) : Promise<number>
    abstract countOutputTags(args: sdk.FindOutputTagsArgs) : Promise<number>
    abstract countSyncStates(args: sdk.FindSyncStatesArgs): Promise<number>
    abstract countTransactions(args: sdk.FindTransactionsArgs) : Promise<number>
    abstract countTxLabels(args: sdk.FindTxLabelsArgs) : Promise<number>
    abstract countUsers(args: sdk.FindUsersArgs) : Promise<number>
 
 
    abstract getProvenTxsForUser(args: sdk.FindForUserSincePagedArgs) : Promise<table.ProvenTx[]>
    abstract getProvenTxReqsForUser(args: sdk.FindForUserSincePagedArgs) : Promise<table.ProvenTxReq[]>
    abstract getTxLabelMapsForUser(args: sdk.FindForUserSincePagedArgs) : Promise<table.TxLabelMap[]>
    abstract getOutputTagMapsForUser(args: sdk.FindForUserSincePagedArgs) : Promise<table.OutputTagMap[]>
 
    async findUserByIdentityKey(key: string) : Promise<table.User| undefined> {
        return verifyOneOrNone(await this.findUsers({ partial: { identityKey: key } }))
    }
 
    async getSyncChunk(args: sdk.RequestSyncChunkArgs): Promise<sdk.SyncChunk> {
        return getSyncChunk(this, args)
    }
 
    /**
     * Force dates to strings on SQLite and Date objects on MySQL
     * @param date 
     * @returns 
     */
    validateEntityDate(date: Date | string | number)
        : Date | string {
        Iif (!this.dbtype) throw new sdk.WERR_INTERNAL('must call verifyReadyForDatabaseAccess first')
        let r: Date | string = this.validateDate(date)
        switch (this.dbtype) {
            case 'MySQL':
                break
            case 'SQLite':
                r = r.toISOString()
                break
            default: throw new sdk.WERR_INTERNAL(`Invalid dateScheme ${this.dbtype}`)
        }
        return r
    }
 
    /**
     * 
     * @param date 
     * @param useNowAsDefault if true and date is null or undefiend, set to current time.
     * @returns 
     */
    validateOptionalEntityDate(date: Date | string | number | null | undefined, useNowAsDefault?: boolean)
        : Date | string | undefined {
        Iif (!this.dbtype) throw new sdk.WERR_INTERNAL('must call verifyReadyForDatabaseAccess first')
        let r: Date | string | undefined = this.validateOptionalDate(date)
        Iif (!r && useNowAsDefault) r = new Date()
        switch (this.dbtype) {
            case 'MySQL':
                break
            case 'SQLite':
                if (r)
                    r = r.toISOString()
                break
            default: throw new sdk.WERR_INTERNAL(`Invalid dateScheme ${this.dbtype}`)
        }
        return r
    }
 
    validateDate(date: Date | string | number): Date {
        let r: Date
        if (date instanceof Date)
            r = date
        else
            r = new Date(date)
        return r
    }
 
    validateOptionalDate(date: Date | string | number | null | undefined): Date | undefined {
        Iif (date === null || date === undefined) return undefined
        return this.validateDate(date)
    }
 
    validateDateForWhere(date: Date | string | number): Date | string | number {
        Iif (!this.dbtype) throw new sdk.WERR_INTERNAL('must call verifyReadyForDatabaseAccess first')
        Iif (typeof date === 'number') date = validateSecondsSinceEpoch(date)
        const vdate = verifyTruthy(this.validateDate(date))
        let r: Date | string | number
        switch (this.dbtype) {
            case 'MySQL':
                r = vdate
                break
            case 'SQLite':
                r = vdate.toISOString()
                break
            default: throw new sdk.WERR_INTERNAL(`Invalid dateScheme ${this.dbtype}`)
        }
        return r
    }
 
}
 
export interface StorageReaderOptions {
    chain: sdk.Chain
}
 
export type DBType = 'SQLite' | 'MySQL'
 
type DbEntityTimeStamp<T extends sdk.EntityTimeStamp> = {
  [K in keyof T]: T[K] extends Date ? Date | string : T[K]
}