"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateOutput = exports.generateValidate = exports.ApiHandler = exports.ApiDefinition = void 0;
const Joi = require("joi");
const error_1 = require("../../error");
const magic_function_1 = require("../magic.function");
const modules_1 = require("../modules");
const utils_1 = require("../utils");
/**
 * 动态API异步方法
 * @param script
 * @return {*}
 * @constructor
 */
const ApiDefinition = (script) => {
    return (0, utils_1.ScriptFunction)(script, [
        'exit',
        'ctx',
        'importModule',
        'importFunction',
        'BizError',
        'BizErrorEnum',
        'db',
        'cache',
        'logger',
        'query',
        'body',
        'params',
        'header',
    ]);
};
exports.ApiDefinition = ApiDefinition;
/**
 * 接口处理器
 * @param ctx 上下文
 * @param apiDefinition 执行脚本
 * @constructor
 */
const ApiHandler = async (ctx, apiDefinition) => {
    const requestContext = ctx.requestContext;
    const db = await requestContext.getAsync(modules_1.SQLModule);
    const cache = await requestContext.getAsync(modules_1.CacheModule);
    const magicFunction = await requestContext.getAsync(magic_function_1.MagicFunction);
    const logger = ctx.getLogger('magic');
    const importModule = async (moduleName) => {
        return await requestContext.getAsync(moduleName);
    };
    const exit = (httpStatus, message, data) => {
        throw new error_1.HttpExitError(httpStatus, message, data);
    };
    /**
     * 请求参数
     */
    // @ts-ignore
    const { query, body, params, header } = ctx.request;
    try {
        //执行script
        const result = await (0, utils_1.executeScriptFunction)(apiDefinition, exit, ctx, importModule, magicFunction.getFunction.bind(magicFunction), error_1.BizError, error_1.BizErrorEnum, db, cache, logger, query, body, params, header);
        if (result != null) {
            ctx.body = result;
        }
    }
    finally {
        await db.release();
    }
};
exports.ApiHandler = ApiHandler;
/**
 *
 * @param key key
 * @param value 值
 * @param dataType 数据类型
 * @param defaultValue 默认值
 * @param validateType 校验类型
 * @param expression 自定义校验表达式或者正则
 * @param error 异常提示
 * @param description 字段描述
 * @param required 是否必填
 * @param _joi 自定义joi对象
 */
const joiSchema = ({ key, value, dataType, defaultValue, validateType, expression, error, description, }, required = false, _joi) => {
    let joi = _joi;
    if (joi == undefined) {
        //判断类型
        if (dataType === 'String') {
            joi = Joi.string();
        }
        else if (dataType === 'Number') {
            joi = Joi.number();
        }
        else if (dataType === 'Boolean') {
            joi = Joi.boolean();
        }
        else if (dataType === 'Date') {
            joi = Joi.date();
        }
        else if (dataType === 'Object') {
            joi = Joi.object();
        }
        else if (dataType === 'Array') {
            joi = Joi.array();
        }
        else if (dataType === 'Any') {
            joi = Joi.any();
        }
        else {
            throw new error_1.BizError('api数据类型错误');
        }
    }
    if ((0, utils_1.isNotBlank)(value)) {
        joi = joi.example(value);
    }
    //设置默认值
    if ((0, utils_1.isNotBlank)(defaultValue)) {
        if (dataType === 'Boolean') {
            joi = joi.default(defaultValue === 'true'
                ? true
                : defaultValue === 'false'
                    ? false
                    : undefined);
        }
        else {
            joi = joi.default(defaultValue);
        }
    }
    else {
        //设置必填
        if (required) {
            joi = joi.required();
        }
        else {
            if (joi.type === 'string') {
                joi = joi.allow('').allow(null).empty('');
            }
        }
    }
    //自定义校验
    if (validateType > 0) {
        let reg = undefined;
        if (validateType === 2) {
            reg = new RegExp(expression);
        }
        joi = joi.custom(value => {
            if (validateType === 1) {
                try {
                    const validFunc = new Function('value', `return ${expression}`);
                    const flag = validFunc(value);
                    if (typeof flag === 'boolean') {
                        if (!flag) {
                            throw new error_1.ValidationError(`"${key}" ${error}`);
                        }
                    }
                    else {
                        throw new error_1.ValidationError(`校验表达式异常：${expression}，返回值不为Boolean类型`);
                    }
                }
                catch (e) {
                    if (e instanceof error_1.ValidationError) {
                        throw e;
                    }
                    else {
                        throw new error_1.BizError(`校验表达式异常：${expression}`);
                    }
                }
            }
            else if (validateType === 2) {
                if (!reg.test(value)) {
                    throw new error_1.ValidationError(`"${key}" ${error}`);
                }
            }
            return value;
        });
    }
    if ((0, utils_1.isNotBlank)(description)) {
        joi = joi.description(description);
    }
    return joi;
};
/**
 * 解析body数据定义 转换joi
 * @param requestBodyDefinition 数据定义
 */
const parseBodyDefinition = (requestBodyDefinition) => {
    let joi;
    const children = requestBodyDefinition.children;
    if (requestBodyDefinition.dataType === 'Object') {
        let def = undefined;
        if (children && children.length > 0) {
            def = {};
            for (const child of children) {
                def[child.key] = parseBodyDefinition(child);
            }
        }
        joi = joiSchema(requestBodyDefinition, requestBodyDefinition.required, Joi.object(def));
    }
    else if (requestBodyDefinition.dataType === 'Array') {
        const items = [];
        if (children && children.length > 0) {
            const child = children[0];
            if (requestBodyDefinition.required) {
                child.required = true;
            }
            items.push(parseBodyDefinition(child));
        }
        joi = joiSchema(requestBodyDefinition, requestBodyDefinition.required, Joi.array().items(...items));
    }
    else {
        joi = joiSchema(requestBodyDefinition, requestBodyDefinition.required);
    }
    return joi;
};
/**
 * 生成校验参数
 * @param resource 资源
 */
const generateValidate = (resource) => {
    const method = resource.method;
    const { parameters, headers, paths, requestBodyDefinition } = resource.definition;
    const result = {
        body: undefined,
        header: undefined,
        params: undefined,
        query: undefined,
        type: 'json',
    };
    if (parameters && parameters.length > 0) {
        if (method !== 'POST' || (0, utils_1.isNotBlank)(requestBodyDefinition === null || requestBodyDefinition === void 0 ? void 0 : requestBodyDefinition.dataType)) {
            const _query = {};
            for (const parameter of parameters) {
                if (parameter.dataType !== 'File' && parameter.dataType !== 'Files') {
                    _query[parameter.key] = joiSchema(parameter, parameter.required);
                }
            }
            result.query = Joi.object().keys(_query);
        }
        else if (method === 'POST') {
            result.type = 'form';
            const _body = {};
            for (const parameter of parameters) {
                if (parameter.dataType === 'File' || parameter.dataType === 'Files') {
                    result.type = 'multipart';
                    _body[parameter.key] = Joi.any()
                        .meta({ type: 'string', format: 'binary' })
                        .custom(value => {
                        if (parameter.required && value.length === 0) {
                            throw new error_1.ValidationError(`"${parameter.key}" 不能为空`);
                        }
                        return value;
                    });
                    if (parameter.required) {
                        _body[parameter.key] = _body[parameter.key].required();
                    }
                    if ((0, utils_1.isNotBlank)(parameter.description)) {
                        _body[parameter.key] = _body[parameter.key].description(parameter.description);
                    }
                }
                else {
                    _body[parameter.key] = joiSchema(parameter, parameter.required);
                }
            }
            result.body = Joi.object().keys(_body);
        }
    }
    if (method !== 'GET' &&
        (0, utils_1.isNotBlank)(requestBodyDefinition === null || requestBodyDefinition === void 0 ? void 0 : requestBodyDefinition.dataType) &&
        (requestBodyDefinition === null || requestBodyDefinition === void 0 ? void 0 : requestBodyDefinition.requestType) === 'json') {
        result.type = 'json';
        result.body = parseBodyDefinition(requestBodyDefinition);
    }
    if (headers && headers.length > 0) {
        const _header = {};
        for (const header of headers) {
            _header[header.key] = joiSchema(header, header.required);
        }
        result.header = Joi.object().keys(_header);
    }
    if (paths && paths.length > 0) {
        const _params = {};
        for (const path of paths) {
            _params[path.key] = joiSchema(path, true);
        }
        result.params = Joi.object().keys(_params);
    }
    return result;
};
exports.generateValidate = generateValidate;
/**
 * 生成返回定义
 * @param resource 资源
 */
const generateOutput = (resource) => {
    var _a;
    const { responseBodyDefinition } = resource.definition;
    const result = {
        responseType: responseBodyDefinition === null || responseBodyDefinition === void 0 ? void 0 : responseBodyDefinition.responseType,
        body: undefined,
        headers: undefined,
    };
    if ((0, utils_1.isNotBlank)(responseBodyDefinition === null || responseBodyDefinition === void 0 ? void 0 : responseBodyDefinition.dataType) &&
        (responseBodyDefinition === null || responseBodyDefinition === void 0 ? void 0 : responseBodyDefinition.responseType) === 'json') {
        result.body = {};
        result.body = parseBodyDefinition(responseBodyDefinition);
    }
    if ((responseBodyDefinition === null || responseBodyDefinition === void 0 ? void 0 : responseBodyDefinition.headers) &&
        ((_a = responseBodyDefinition === null || responseBodyDefinition === void 0 ? void 0 : responseBodyDefinition.headers) === null || _a === void 0 ? void 0 : _a.length) > 0) {
        const _headers = {};
        for (const { key, value, description } of responseBodyDefinition.headers) {
            _headers[key] = Joi.string().example(value);
            if ((0, utils_1.isNotBlank)(description)) {
                _headers[key] = _headers[key].description(description);
            }
        }
        result.headers = _headers;
    }
    return result;
};
exports.generateOutput = generateOutput;
//# sourceMappingURL=ApiDefinition.js.map