import { BasicType, BufferData, DeclareInstruction, Instruction, InstructionType, ObjectData, PrimitiveData, Scope, Tag, Variable, Variables, XmlError } from '@cyklang/core'; import loglevel from 'loglevel'; import { dbRemote, structure, useCykLang } from './cykLang'; import { ComponentModel } from './cykLang' // import { dbRemote, refreshDBSession } from 'src/services/cyklang'; import { componentModelParameter } from './cykReact'; import { useCykStore } from './cykStore'; import { SetupContext, onUnmounted, ref } from 'vue'; import { useRouter } from 'vue-router'; import { AlertException } from './cykRun'; import { WindowManager } from './WindowManager'; const logger = loglevel.getLogger('UploaderComponent.vue'); logger.setLevel('debug'); export const parseParams = async (componentArg: ComponentModel): Promise<{ table_name?: string, field_name?: string, record?: ObjectData, id: BasicType, width?: number, height?: number }> => { const variables = componentArg.objectData?.variables || new Variables() let table_name: string | undefined, field_name: string | undefined, record: ObjectData | undefined, id: BasicType, width: number | undefined, height: number | undefined; try { for (let index = 0; index < variables.length(); index++) { const namedVariable = variables.at(index); if (!namedVariable) continue if (namedVariable.variable.data !== undefined && namedVariable.variable.data !== null) { if (namedVariable.variable.data.type.name === 'string') { const value = (namedVariable.variable.data as PrimitiveData).value as string; switch (namedVariable.name) { case 'table_name': table_name = value; break; case 'field_name': field_name = value; break; } } else if (namedVariable.variable.data.type.name === 'number') { const value = (namedVariable.variable.data as PrimitiveData).value as number; switch (namedVariable.name) { case 'width': width = value; break; case 'height': height = value; break; } } else if (namedVariable.variable.data.type.isObject()) { record = namedVariable.variable.data as ObjectData; } else { alert( 'unknown parameter type ' + namedVariable.variable.data.type.name + ', name ' + name ); } } } // if (table_name === undefined) { // const msg = ' table_name is missing' // alert(msg); // throw msg // } // if (record === undefined) { // const msg = ' record is missing' // alert(msg); // throw msg // } // if (field_name === undefined) { // const msg = ' field_name is missing' // alert(msg); // throw msg // } if (table_name && field_name && record) { const { getDBManager } = useCykLang() const dbManager = await getDBManager() const dbTable = await dbManager.dbTableExist(table_name); if (dbTable === undefined) { const msg = ' table ' + table_name + ' not found' alert(msg); throw msg; } if (!dbTable.objectDataType) throw 'dbTable.objectDataType undefind' if (!dbTable.objectDataType.dbColumns) throw 'dbTable.objectDataType.dbColumns undefined' if (dbTable.objectDataType.dbColumns.primaryColumns.length !== 1) throw 'multiple primary columns is not supported' const primaryColumn = dbTable.objectDataType.dbColumns.primaryColumns[0] const variable = record.variables.getVariable(primaryColumn.name) if (variable === undefined || variable.data === null || variable.data === undefined) { id = undefined } else id = (variable.data as PrimitiveData).value } else { id = componentArg.model?.getString() } } catch (err) { AlertException(err) } return { table_name, field_name, id, record, width, height } }; // 13/08/2023 // model is a JSON meta of the image export function useCykUpload(props: { componentArg: ComponentModel | undefined }, context: SetupContext<('updated')[]>) { const { dbRemote } = useCykLang(); const store = useCykStore(); const isLoading = ref(true); const router = useRouter(); const visible = componentModelParameter(props.componentArg, "visible", true) const url = ref(''); let table_name: string, field_name: string, record: ObjectData, id = ref(), width: number | undefined, height: number | undefined; // // function uploaded // called after image successfully uploaded // const uploaded = (info: any) => { logger.debug('uploaded BEGINS') if ( props.componentArg === undefined || props.componentArg.model === undefined ) return; (async () => { if (props.componentArg?.model === undefined) return const { structure, getDBManager } = useCykLang() const dbManager = await getDBManager() const dbTable = await dbManager.dbTableExist(table_name); if (dbTable === undefined) return; if (!dbTable.objectDataType) throw 'dbTable.objectDataType undefind' if (!dbTable.objectDataType.dbColumns) throw 'dbTable.objectDataType.dbColumns undefined' const colField = dbTable.objectDataType.dbColumns.getColumn(field_name); if (colField === undefined) return; // get metadata from server const metadata_field_name = field_name + "_meta" const url = "/api/table/" + table_name + "/" + id.value + "/" + metadata_field_name + "?mimetype=application%2Fjson" const metadata = await dbRemote.apiServer.get(url) logger.debug('metadata from server', metadata) const objData = structure.scope.js2Data(metadata) props.componentArg.model.data = objData // props.componentArg.model.setValue(ndata); props.componentArg.dirtyManager?.variableChanged(props.componentArg.model) // props.componentArg.variableReact?.variableChanged(props.componentArg.model) context.emit('updated') })(); }; const added = () => { // refreshDBSession(router); }; const headers = () => { return [ { name: 'Authorization', value: 'Bearer ' + store.user.accessJWT }, ]; }; (async () => { try { if (props.componentArg !== undefined) { const { table_name: _table_name, field_name: _field_name, record: _record, id: _id, width: _width, height: _height } = await parseParams(props.componentArg); if (_table_name === undefined || _field_name === undefined || _record === undefined) throw 'parameters incomplete' table_name = _table_name field_name = _field_name record = _record id.value = _id width = _width height = _height let uri = dbRemote.serverURL + '/api/image/' + table_name + '/' + id.value + '/' + field_name if (width && height) { uri = uri + '?width=' + width + '&height=' + height } url.value = new URL(uri).href logger.debug('cykUpload.url', url.value) await props.componentArg.interpolateAttributes() } } catch (err) { AlertException(err) } isLoading.value = false; })(); if (props.componentArg?.model && props.componentArg?.dirtyManager && props.componentArg.objectData?.tag.attributes['IGNORE-DIRTY'] === undefined ) { props.componentArg.dirtyManager.registerVariable(props.componentArg.model, 'Qimg') } onUnmounted(() => { props.componentArg?.objectData?.destroy() }); return { isLoading, visible, url, headers, added, uploaded, id }; } /** * */ export class UploadFileInstructionType extends InstructionType { windowManager: WindowManager; constructor(windowManager: WindowManager) { super('upload_file') this.windowManager = windowManager } async parseInstruction(tag: Tag, scope: Scope): Promise { const inst = new UploadFileInstruction(tag, this) return inst } } /** * */ export class UploadFileInstruction extends Instruction { instructionType: UploadFileInstructionType constructor(tag: Tag, instructionType: UploadFileInstructionType) { super(tag) this.instructionType = instructionType } async execute(outerScope: Scope): Promise { try { const objParams = new ObjectData(structure.objectDataType, new Tag(''), outerScope) for (let index = 0; index < this.tag.childs.length; index++) { let child = this.tag.childs[index] let declareInstruction = new DeclareInstruction(child, structure.objectDataType) try { await declareInstruction.execute(objParams.scope) } catch (err) { throw (new XmlError(String(err), child)) } } const p_meta = objParams.variables.getData('meta') if (! p_meta ) throw `meta is missing` if ( ! p_meta.type.isObject() ) throw `meta should be object but is ${p_meta.type.name}` const metaData = p_meta as ObjectData const filename = metaData.variables.getString('filename') || '' const p_content = objParams.variables.getData('content') if (! p_content) throw `content is missing` if ( p_content.type.name !== 'buffer') throw `content should be buffer but is ${p_content.type.name}` const bufferData = p_content as BufferData const buffer = bufferData.buffer if (! buffer) throw `bufferData.buffer undefined` const p_table_name = objParams.variables.getData('table_name') if (! p_table_name) throw `table_name is missing` if ( p_table_name.type.name !== 'string') throw `table_name should be string but is ${p_table_name.type.name}` const table_name = (p_table_name as PrimitiveData).value as string const p_field_name = objParams.variables.getData('field_name') if (! p_field_name) throw `table_name is missing` if ( p_field_name.type.name !== 'string') throw `table_name should be string but is ${p_field_name.type.name}` const field_name = (p_field_name as PrimitiveData).value as string const p_key = objParams.variables.getData('key') if (! p_key) throw `table_name is missing` if ( p_key.type.name !== 'string') throw `table_name should be string but is ${p_key.type.name}` const key = (p_key as PrimitiveData).value as string const blob = new Blob([ buffer as any ]) const file = new File( [ blob] , filename) const form = new FormData() form.append('uploadFile', file) const route = `/api/file/${table_name}/${encodeURIComponent(key)}/${field_name}` const url = new URL(route, 'https://www.example.com') const result_metadata = await dbRemote.apiServer.post(url.pathname + url.search, form) logger.debug(`result_metadata = ${JSON.stringify(result_metadata)}`) } catch (err) { throw new XmlError(`upload_file : ${String(err)}`, this.tag) } } }