bde148f286
* wip * some extra fixes * stuff kinda works? * need to figure out mocks * need to figure out mocks * fix db listener * gqlgen fix * minor gqlgen watch adjustment * lint fixes * delete old codegen file * converting migrations to ESM * getModuleDIrectory * vitest sort of works * added back ts-vitest * resolve gql double load * fixing test timeout configs * TSC lint fix * fix automate tests * moar debugging * debugging * more debugging * codegen update * server works * yargs migrated * chore(server): getting rid of global mocks for Server ESM (#5046) * got rid of email mock * got rid of comment mocks * got rid of multi region mocks * got rid of stripe mock * admin override mock updated * removed final mock * fixing import.meta.resolve calls * another import.meta.resolve fix * added requested test * nyc ESM fix * removed unneeded deps + linting * yarn lock forgot to commit * tryna fix flakyness * email capture util fix * sendEmail fix * fix TSX check * sender transporter fix + CR comments * merge main fix * test fixx * circleci fix * gqlgen bigint fix * error formatter fix * more error formatting improvements * esmloader added to Dockerfile * more dockerfile fixes * bg jobs fix
125 lines
3.3 KiB
TypeScript
125 lines
3.3 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
|
|
import { Nullable } from '@speckle/shared'
|
|
import { SchemaConfig, MetaSchemaConfig } from '@/modules/core/dbSchema'
|
|
import { camelCase, isString } from 'lodash-es'
|
|
import { Knex } from 'knex'
|
|
|
|
/**
|
|
* All meta records must follow this interface
|
|
*/
|
|
export interface BaseMetaRecord<V = any> {
|
|
key: string
|
|
value: V
|
|
createdAt: Date
|
|
updatedAt: Date
|
|
}
|
|
|
|
/**
|
|
* Helpers that simplify working with a DB table's associated meta table, if one exists
|
|
*/
|
|
export function metaHelpers<
|
|
R extends BaseMetaRecord,
|
|
S extends SchemaConfig<any, any, MetaSchemaConfig<any, keyof BaseMetaRecord, any>>
|
|
>(table: S, knex: Knex) {
|
|
const db = <RR extends object = R>() => knex<RR>(table.meta.name)
|
|
|
|
return {
|
|
/**
|
|
* Get a single value
|
|
*/
|
|
get: async <RR extends R = R>(
|
|
id: string,
|
|
key: keyof S['meta']['metaKey']
|
|
): Promise<Nullable<RR>> => {
|
|
const q = db()
|
|
.where(table.meta.col.key, <string>key)
|
|
.andWhere(table.meta.parentIdentityCol, id)
|
|
.first()
|
|
const res = (await q) as Nullable<RR>
|
|
return res
|
|
},
|
|
/**
|
|
* Get multiple values at once, keyed by ID
|
|
* E.g.: {
|
|
* "1234": {
|
|
* "foo": ...,
|
|
* "bar": ...,
|
|
* }
|
|
* }
|
|
*/
|
|
getMultiple: async <RR extends R = R>(
|
|
requests: Array<{ id: string; key: keyof S['meta']['metaKey'] }>
|
|
) => {
|
|
const meta = table.meta.withoutTablePrefix
|
|
const q = db()
|
|
.select<Array<RR>>('*')
|
|
.whereIn(
|
|
table.meta.col.key,
|
|
requests.map((r) => <string>r.key)
|
|
)
|
|
.whereIn(
|
|
table.meta.parentIdentityCol,
|
|
requests.map((r) => r.id)
|
|
)
|
|
|
|
const results = await q
|
|
const ret: Record<string, Record<string, RR>> = {}
|
|
for (const result of results) {
|
|
const resultId = (result as Record<string, string>)[meta.parentIdentityCol]
|
|
if (!ret[resultId]) {
|
|
ret[resultId] = {}
|
|
}
|
|
|
|
const identityValues = ret[resultId]
|
|
identityValues[result.key] = result as RR
|
|
}
|
|
|
|
return ret
|
|
},
|
|
/**
|
|
* Set a value
|
|
*/
|
|
set: async <RR extends R = R>(
|
|
id: string,
|
|
key: keyof S['meta']['metaKey'],
|
|
val: any
|
|
) => {
|
|
const meta = table.meta.withoutTablePrefix
|
|
const q = db<any>()
|
|
.insert({
|
|
[meta.parentIdentityCol]: id,
|
|
[meta.col.key]: key,
|
|
[meta.col.value]: isString(val) ? JSON.stringify(val) : val,
|
|
[meta.col.updatedAt]: new Date()
|
|
})
|
|
.onConflict([meta.parentIdentityCol, meta.col.key])
|
|
.merge([meta.col.value, meta.col.updatedAt])
|
|
.returning('*')
|
|
const [newEntry] = (await q) as RR[]
|
|
return newEntry
|
|
},
|
|
/**
|
|
* Delete meta entry entirely
|
|
*/
|
|
delete: async (id: string, key: keyof S['meta']['metaKey']) => {
|
|
const q = db()
|
|
.where(table.meta.col.key, <string>key)
|
|
.andWhere(table.meta.parentIdentityCol, id)
|
|
.del()
|
|
const res = await q
|
|
return res > 0
|
|
},
|
|
/**
|
|
* Get unique GQL ID for the meta record
|
|
*/
|
|
getGraphqlId: (record: R) => {
|
|
const metaName = camelCase(table.meta.name)
|
|
const entityId = (record as Record<string, unknown>)[
|
|
table.meta.parentIdentityCol
|
|
] as string
|
|
return `MetaValue/${metaName}/${entityId}/${record.key}`
|
|
}
|
|
}
|
|
}
|