"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
/** Name_1 格式的 operationId */
exports.dangerousOperationIdReg = /_[0-9]{1,}$/g;
exports.defaultPrefixReg = /^(\/)?api\//g;
exports.defaultBadParamsReg = /[^a-z0-9_.[]$]/gi;
/**
 * 构建 http method + url : oldOperationId 安全映射，用以校正风险 swagger
 */
function operationIdGuard(swagger, config) {
    if (config === void 0) { config = {}; }
    var _a = config.methodUrl2OperationIdMap, savedMethodUrl2OperationIdMap = _a === void 0 ? {} : _a, mode = config.mode, _b = config.prefixReg, prefixReg = _b === void 0 ? exports.defaultPrefixReg : _b, _c = config.badParamsReg, badParamsReg = _c === void 0 ? exports.defaultBadParamsReg : _c, _d = config.methodPrefix, methodPrefix = _d === void 0 ? 'Using' : _d;
    var paths = swagger.paths;
    /**
     * http method + url => api info 映射
     */
    var methodUrl2ApiMap = {};
    /**
     * `XXXX_*` => `XXXX` 对 api 条目分组
     */
    var operationId2ApiMap = {};
    /**
     * 已持久化 `method` + `url` => operationId 映射
     */
    var copySavedMethodUrl2OperationIdMap = tslib_1.__assign({}, savedMethodUrl2OperationIdMap);
    var isRelationMapped = !!Object.keys(copySavedMethodUrl2OperationIdMap).length;
    /**
     * 错误日志：例如参数错误、危险operationId
     */
    var errors = [];
    /**
     * 警告日志：根据锁定关系自动校正日志
     */
    var warnings = [];
    /**
     * 自动推断出来的锁定映射关系
     */
    var suggestions = {};
    var isSafe = mode === 'safe';
    var isStrict = mode === 'strict';
    /**
     * 风险 operationId 数组
     */
    var dangers = Object.keys(Object.keys(paths).reduce(function (dangerousOperationIdMap, url) {
        var apiItem = paths[url];
        Object.keys(apiItem).forEach(function (method) {
            var api = apiItem[method];
            var operationId = api.operationId, _a = api.parameters, parameters = _a === void 0 ? [] : _a;
            // 参数格式校验
            parameters.forEach(function (_a) {
                var _b = _a.name, name = _b === void 0 ? '' : _b, pType = _a.in, _c = _a.description, description = _c === void 0 ? '' : _c;
                if (name.match(badParamsReg)) {
                    errors.push("\u3010\u9519\u8BEF\u3011\u63A5\u53E3 \"" + method + " " + url + "\" \"" + pType + "\" \u53C2\u6570 \"" + name + "\" \"" + description + "\" \u4E0D\u7B26\u5408\u89C4\u8303\uFF0C\u8BF7\u8054\u7CFB\u63A5\u53E3\u65B9\u4FEE\u6539");
                }
            });
            if (operationId) {
                // `url`Using`Method` 作为 operationId，唯一固定
                api.privateOperationId =
                    url
                        .replace(prefixReg, '')
                        .replace(/(^\/|})/g, '')
                        .replace(/[/{_-]{1,}([^/])/g, function (mat, u) { return u.toUpperCase(); }) +
                        methodPrefix +
                        method[0].toUpperCase() +
                        method.substring(1);
                // IMP: 严格模式 & 新项目，不再无谓校验 operationId 是否存在风险，直接使用 privateOperationId
                if (isStrict) {
                    api.operationId = api.privateOperationId;
                }
                else {
                    // url -> api 映射
                    var methodUrl = method.toLocaleLowerCase() + " " + url;
                    api.privateMethodUrl = methodUrl;
                    methodUrl2ApiMap[methodUrl] = api;
                    // 格式化 `XXXX_*` 为 `XXXX`
                    var dangerousOperationId = operationId.replace(exports.dangerousOperationIdReg, '');
                    // 如果出现 `XXXX_*` 则标记 `XXXX` 存在风险
                    if (("" + (operationId || '')).match(exports.dangerousOperationIdReg)) {
                        dangerousOperationIdMap[dangerousOperationId] = '';
                    }
                    // 将 api 按 `XXXX` 维度分组
                    operationId2ApiMap[dangerousOperationId] =
                        operationId2ApiMap[dangerousOperationId] || [];
                    operationId2ApiMap[dangerousOperationId].push(api);
                }
            }
        });
        return dangerousOperationIdMap;
    }, {}));
    if (errors.length) {
        return {
            errors: errors,
            warnings: warnings,
            suggestions: []
        };
    }
    /**
     * 校验 or 修复 api operationId 风险
     */
    var validateOrFixOperationId = function (api, errors) {
        var _a = api.operationId, operationId = _a === void 0 ? '' : _a, _b = api.privateMethodUrl, privateMethodUrl = _b === void 0 ? '' : _b, privateOperationId = api.privateOperationId;
        var savedOperationId = copySavedMethodUrl2OperationIdMap[privateMethodUrl];
        if (!savedOperationId) {
            errors.push("\u3010\u9519\u8BEF\u3011\u65B9\u6CD5 \"" + operationId + "\" & \"" + privateMethodUrl + "\" \u5B58\u5728\u4E0D\u53EF\u63A7\u98CE\u9669\uFF0C\u9700\u9501\u5B9A\u6620\u5C04\u5173\u7CFB");
            // 增量情况下，使用 `url` + `method`【privateOperationId】 替代原有 `operationId`，避免重复
            suggestions[privateMethodUrl] = isRelationMapped ? privateOperationId : operationId;
        }
        else if (savedOperationId !== operationId) {
            /**
             * 尝试自动人工干涉
             *  干涉配置:
             *    get /api/get   => get
             *    get /api/get-1 => get_1
             *  实际返回
             *    get /api/get   => get_1
             *    get /api/get-1 => get
             *  干涉结果
             *    get /api/get   => get
             *    get /api/get-1 => get_1
             */
            warnings.push("\u3010\u8B66\u544A\u3011\u65B9\u6CD5 \"" + operationId + "\" \u4E0E \"" + privateMethodUrl + "\" \u4E0D\u5339\u914D\uFF0C\u81EA\u52A8\u7EA0\u6B63\u4E3A \"" + savedOperationId + "\"");
            // 校正
            api.operationId = savedOperationId;
            delete api.privateMethodUrl;
        } // else 映射关系匹配
        // 风险已处理
        delete copySavedMethodUrl2OperationIdMap[privateMethodUrl];
    };
    // 对危险分组进行校验或修复
    dangers.reduce(function (errors, dangerousOperationId) {
        var group = operationId2ApiMap[dangerousOperationId];
        group.forEach(function (api) { return validateOrFixOperationId(api, errors); });
        return errors;
    }, errors);
    // 对剩余映射进行清理、映射对应的 api 进行校验或修复
    Object.keys(copySavedMethodUrl2OperationIdMap).reduce(function (errors, methodUrl) {
        var mappedPperationId = copySavedMethodUrl2OperationIdMap[methodUrl];
        var api = methodUrl2ApiMap[methodUrl];
        // 已移除的 api
        if (!api) {
            // 向下兼容老 `strict` 模式，不再抛出映射过期提示
            if (!isStrict) {
                errors.push("\u3010\u9519\u8BEF\u3011\u65B9\u6CD5 \"" + mappedPperationId + "\" \u5BF9\u5E94\u7684 \"" + methodUrl + "\" \u4F3C\u4E4E\u5DF2\u88AB\u79FB\u9664\u6216\u8005\u53D1\u751F\u4E86\u53D8\u5316\uFF0C\u8BF7\u66F4\u65B0");
            }
        }
        else {
            // 需要修复的 api
            validateOrFixOperationId(api, errors);
        }
        return errors;
    }, errors);
    // 仅安全模式生成替换提示
    if (!errors.length && !Object.keys(suggestions).length && isSafe) {
        try {
            Object.keys(paths).forEach(function (url) {
                var apiItem = paths[url];
                Object.keys(apiItem).forEach(function (method) {
                    var api = apiItem[method];
                    var operationId = api.operationId, privateOperationId = api.privateOperationId;
                    if (operationId) {
                        // `url` + Using` + Method` 作为 operationId
                        api.operationId = privateOperationId;
                        // 处置重复 operationId
                        if (operationId in suggestions) {
                            throw Error("\u3010\u9519\u8BEF\u3011\u6620\u5C04\u5185\u68C0\u6D4B\u5230\u91CD\u590D " + operationId + "\uFF0C\u65E0\u6CD5\u751F\u6210\u6B63\u786E\u65E7\u3001\u65B0\u65B9\u6CD5\u66FF\u6362\u6620\u5C04\u5173\u7CFB");
                        }
                        else {
                            suggestions[operationId] = privateOperationId;
                        }
                    }
                });
            });
        }
        catch (e) {
            return {
                errors: [e.message],
                warnings: [],
                suggestions: []
            };
        }
        return {
            errors: errors,
            warnings: warnings,
            suggestions: Object.keys(suggestions).length
                ? [
                    '旧 => 新 方法替换映射关系，请按建议顺序降序逐一替换',
                    Object.keys(suggestions)
                        .sort(function (a, b) {
                        return -(parseInt(a.split('_')[1]) || 0) + (parseInt(b.split('_')[1]) || 0);
                    })
                        .reduce(function (s, id) {
                        s[id] = suggestions[id];
                        s["Params" + id] = "Params" + s[id];
                        return s;
                    }, {})
                ]
                : []
        };
    }
    return {
        errors: errors,
        warnings: warnings,
        suggestions: Object.keys(suggestions).length
            ? [
                '锁定映射建议，添加到 service 配置',
                {
                    guardConfig: {
                        methodUrl2OperationIdMap: suggestions
                    }
                }
            ]
            : []
    };
}
exports.operationIdGuard = operationIdGuard;
exports.DefaultUnstableTagsReg = /^[a-z-0-9_$A-Z]+-controller$/g;
exports.DefaultValidTagsReg = /^[a-z-0-9_$]+$/gi;
exports.DefaultValidDefinitionReg = /^[a-z-0-9_$«»,]+$/gi;
exports.DefaultValidUrlReg = /api/g;
/**
 * 严格模式特有校验逻辑
 */
function strictModeGuard(swagger, config) {
    var _a = swagger.tags, tags = _a === void 0 ? [] : _a, _b = swagger.definitions, definitions = _b === void 0 ? {} : _b, paths = swagger.paths, basePath = swagger.basePath;
    var mode = config.mode, _c = config.unstableTagsReg, unstableTagsReg = _c === void 0 ? exports.DefaultUnstableTagsReg : _c, _d = config.validTagsReg, validTagsReg = _d === void 0 ? exports.DefaultValidTagsReg : _d, _e = config.validDefinitionReg, validDefinitionReg = _e === void 0 ? exports.DefaultValidDefinitionReg : _e, _f = config.validUrlReg, validUrlReg = _f === void 0 ? exports.DefaultValidUrlReg : _f;
    var info = {
        errors: Array(),
        warnings: Array()
    };
    if (mode === 'strict') {
        // 校验 basePath
        var isPathValid = !!(basePath && basePath.match(validUrlReg));
        // @cc: tags 是否符合规范
        tags.reduce(function (errors, tag) {
            var _a = tag.name, name = _a === void 0 ? '' : _a;
            if (name.match(unstableTagsReg)) {
                errors.push("\u3010\u9519\u8BEF\u3011 tags \"" + JSON.stringify(tag, null, 2) + "\" \u547D\u540D\u4E0D\u7B26\u5408\u89C4\u8303\u6216\u7531\u4E8E @Api \u88C5\u9970\u5668\u672A\u6DFB\u52A0 tags\uFF0C\u5B58\u5728\u4E0D\u53EF\u63A7\u98CE\u9669\uFF0C\u8BF7\u8054\u7CFB\u63A5\u53E3\u65B9\u4FEE\u6539");
            }
            if (!name.match(validTagsReg)) {
                errors.push("\u3010\u9519\u8BEF\u3011 tags \"" + JSON.stringify(tag, null, 2) + "\" \u547D\u540D\u5305\u542B\u975E\u82F1\u6587\u5B57\u7B26\uFF0C@Api \u88C5\u9970\u5668\u4E0D\u80FD\u6DFB\u52A0\u975E\u6CD5 tags\uFF0C\u8BF7\u8054\u7CFB\u63A5\u53E3\u65B9\u4FEE\u6539");
            }
            return errors;
        }, info.errors);
        // @cc: definitions 是否符合规范
        Object.keys(definitions).reduce(function (errors, dto) {
            if (!dto.match(validDefinitionReg)) {
                errors.push("\u3010\u9519\u8BEF\u3011 definitions name - DTO \"" + dto + "\" \u547D\u540D\u5305\u542B\u975E\u82F1\u6587\u5B57\u7B26\uFF0C\u8BF7\u8054\u7CFB\u63A5\u53E3\u65B9\u4FEE\u6539");
            }
            return errors;
        }, info.errors);
        // 单独校验每个 url 是否符合规范
        if (!isPathValid) {
            Object.keys(paths).reduce(function (errors, url) {
                if (!url.match(validUrlReg)) {
                    errors.push("\u3010\u9519\u8BEF\u3011 url \"" + JSON.stringify(url, null, 2) + "\" \u4E0D\u7B26\u5408\u89C4\u8303 " + validUrlReg + "\uFF0C\u8BF7\u8054\u7CFB\u63A5\u53E3\u65B9\u4FEE\u6539");
                }
                return errors;
            }, info.errors);
        }
    }
    return Promise.resolve(info);
}
exports.strictModeGuard = strictModeGuard;
//# sourceMappingURL=guard.js.map