utils_ezworks_prepare-file-delete.js

import { saveJSON } from "../../adapters/ezworks/com-api/ds";
import { encodeHex } from "../crypto/encode-hex";
import { generateUUIDv4 } from "../crypto/generate-uuidv4";

/**
 * @typedef prepareFileDeleteOptions
 * @property {nexacro.Dataset} dataset    삭제한 항목이 존재하는 데이터 셋
 * @property {string}          target     대상 테이블
 * @property {string[]}        keyCols    Deprecated. PK 키 값
 * @property {function}        fileKeyFn  파일 키 함수
 * @property {nexacro.Form}    [form]     트랜잭션 등에 사용되는 기준이 되는 form. 생략 시
 *                                        dataset의
 *                                        상위 form을 사용함
 */

/**
 * 삭제할 데이터셋의 대상 파일 UUID를 찾습니다.
 *
 * @function prepareFileDelete
 * @param {prepareFileDeleteOptions} options
 * @returns {Promise<function>}
 * 성공 시 파일 삭제 실행 함수 반환
 * @throws {Error}
 * @memberof $f
 * @example
 * this.fn_cansave = function (obj, e) {
 *     this.delFuncPromise = $f.prepareFileDelete({
 *         dataset: this.ds_main,
 *         target: "CSB_ANNO_MATER",
 *         fileKeyFn: (c) => [c("YY"), c("SEQ"), "1"],
 *     });
 *     return $mpt.confirmSave(this.ds_main, this.validatorMain);
 * };
 *
 * this.fn_save = async function () {
 *     try {
 *         await (
 *             await this.delFuncPromise
 *         )();
 *         await this.fn_saveMain();
 *     } catch (e) {
 *         this.pop.alert("저장 중 오류가 발생하였습니다.");
 *         return;
 *     }
 *
 *     this.fn_search();
 *     return $mpt.alertSaved();
 * };
 *
 */
export async function prepareFileDelete(options) {
    const { dataset, target, keyCols, fileKeyFn, form } = options;
    if (!dataset || !target) {
        throw new Error(
            "searchDelFileUuid: invalid arguments - dataset and target are required",
        );
    }
    if (!keyCols && !fileKeyFn) {
        throw new Error(
            "searchDelFileUuid: either keyCols or fileKeyFn must be provided",
        );
    }

    const workForm = form || dataset.parent;
    const delRowCnt = dataset.getDeletedRowCount();

    // 삭제할 대상이 없는 경우 마침
    if (delRowCnt === 0) {
        return () => {};
    }

    // 요청용 데이터셋 생성 및 초기화
    const argsDsName = "_ds_searchDelFileUuid";
    const argsDs = createTempDataset(workForm, argsDsName);
    argsDs.addColumn("FILE_TARGT_IDEN", "string");

    for (let i = 0; i < delRowCnt; i++) {
        if (fileKeyFn) {
            const fileTargtIdenList = parseTargetFunction(
                fileKeyFn,
                dataset,
                i,
            );

            fileTargtIdenList.forEach((fileTargtIden) => {
                const rowIdx = argsDs.addRow();
                argsDs.setColumn(rowIdx, "FILE_TARGT_IDEN", fileTargtIden);
            });
        } else {
            const fileTargtIden = keyCols
                .map((col) => dataset.getDeletedColumn(i, col))
                .join("[^]");
            const rowIdx = argsDs.addRow();
            argsDs.setColumn(rowIdx, "FILE_TARGT_IDEN", fileTargtIden);
        }
    }

    // 응답용 데이터셋 생성 및 초기화
    const respDsName = "_ds_schDelOutDs";
    const respDs = createTempDataset(workForm, respDsName);

    try {
        await workForm.tx.search({
            action: "basic",
            svcId: "searchDelFileUuid" + argsDsName,
            sqlId: "@f_fileUtil.fileSearch",
            outDs: respDsName,
            opInDs: argsDsName,
            param: {
                FILE_TARGT: target,
            },
        });

        const obj = saveJSON(respDs);
        const jsonStr = JSON.stringify(obj);

        return async function (uuidList) {
            if (uuidList.length === 0) {
                return null;
            }

            return this.tx.load(
                {
                    action: "basic",
                    svcId: `filedel_${encodeHex(generateUUIDv4())}`,
                    param: {
                        UUID_LIST: uuidList,
                    },
                },
                "/ezFile/basic/del",
            );
        }.bind(workForm, jsonStr);
    } catch (e) {
        throw e;
    } finally {
        // 데이터셋 삭제
        workForm.removeChild(argsDsName);
        argsDs.destroy();
        workForm.removeChild(respDsName);
        respDs.destroy();
    }
}

/**
 * 고유한 데이터셋을 생성하고 폼에 추가합니다.
 *
 * @param {nexacro.Form} form    데이터셋을 추가할 폼
 * @param {string}       dsName  생성할 데이터셋의 이름
 * @returns {nexacro.Dataset} 생성된 데이터셋
 * @returns {string[]}        filetargtiden 배열
 * @throws {Error} 동일한 이름의 데이터셋이 이미 존재하는 경우
 * @access private
 */
function createTempDataset(form, dsName) {
    if (form[dsName]) {
        throw new Error("searchDelFileUuid: duplicate call is not allowed");
    }

    const ds = new Dataset(dsName, form);

    form.addChild(dsName, ds);

    return ds;
}

function parseTargetFunction(fn, ds, rowIdx) {
    if (typeof fn !== "function") {
        return [];
    }

    const getDelColumnFn = (col) => ds.getDeletedColumn(rowIdx, col);

    let fnResult = fn(getDelColumnFn);

    if (Array.isArray(fnResult)) {
        if (fnResult.length === 0) {
            fnResult = [[]];
        }

        // 1차원 배열인 경우 2차원 단일행 배열로 변환
        else if (!Array.isArray(fnResult[0])) {
            fnResult = [fnResult];
        }
    }

    if (Array.isArray(fnResult)) {
        return fnResult.map((arr) => arr.join("[^]"));
    }

    return [];
}