Files
speckle-server/packages/server/modules/core/helpers/meta.ts
T
Kristaps Fabians Geikins bde148f286 chore(server): migrating fully to ESM (#5042)
* 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
2025-07-14 10:26:19 +03:00

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}`
}
}
}