c3d452ad0c
* 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
85 lines
2.3 KiB
TypeScript
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))
|
|
}
|
|
}
|
|
})
|
|
}
|