Files
speckle-server/packages/server/modules/core/rest/speckleObjectsStream.ts
T
Gergő Jedlicska c3d452ad0c Gergo/cxpla 278 v2 server api endpoint with masking support (#5414)
* WIP: streaming masking endpoint

* feat(objects): working object streaming with filtering

* feat(object-loader): use the new objects endpoint

* fix(server): allow downloading of public project data

* feat(objectloader-2): include object masking in objectloader

* feat(objectloader2): make attribute masking optional

* fix(server): remove unused imports
2025-09-18 15:56:41 +02:00

85 lines
2.3 KiB
TypeScript

import { ensureError } from '@speckle/shared'
import { omit, pick } from 'lodash-es'
import type { TransformCallback } from 'stream'
import { Transform } from 'stream'
/**
* A stream that converts database objects stream to "{id}\t{data_json}\n" stream or a json stream of obj.data fields
*/
class SpeckleObjectsStream extends Transform {
simpleText: boolean
isFirstObject: boolean
constructor(simpleText: boolean) {
super({ writableObjectMode: true })
this.simpleText = simpleText
if (!this.simpleText) this.push('[')
this.isFirstObject = true
}
_transform(
dbObj: { dataText: string; id: string; data?: Record<string, unknown> },
_encoding: BufferEncoding,
callback: TransformCallback
) {
let objData = dbObj.dataText
if (objData === undefined) objData = JSON.stringify(dbObj.data)
try {
if (this.simpleText) {
this.push(`${dbObj.id}\t`)
this.push(objData)
this.push('\n')
} else {
// JSON output
if (!this.isFirstObject) this.push(',')
this.push(objData)
this.isFirstObject = false
}
callback()
} catch (e) {
callback(ensureError(e))
}
}
_flush(callback: TransformCallback) {
if (!this.simpleText) this.push(']')
callback()
}
}
export { SpeckleObjectsStream }
export const objectDataTransformFactory = ({
attributeMask
}: {
attributeMask?: { include: string[] } | { exclude: string[] }
}) => {
let objectTransform: ((dataText: string) => string) | null
if (attributeMask) {
let objectFilter: (obj: unknown, props: string[]) => unknown
let filteredAttributes: string[]
if ('include' in attributeMask) {
objectFilter = pick
filteredAttributes = attributeMask.include
}
if ('exclude' in attributeMask) {
objectFilter = omit
filteredAttributes = attributeMask.exclude
}
objectTransform = (dataText: string) =>
JSON.stringify(objectFilter(JSON.parse(dataText), filteredAttributes))
}
return new Transform({
writableObjectMode: true,
transform({ dataText, id }: { dataText: string; id: string }, _, callback) {
try {
const objectDataString = objectTransform ? objectTransform(dataText) : dataText
callback(null, `${id}\t${objectDataString}\n`)
} catch (err) {
callback(ensureError(err))
}
}
})
}