UNPKG

t-comm

Version:

专业、稳定、纯粹的工具库

1,152 lines (1,149 loc) 40.1 kB
import _slicedToArray from '@babel/runtime/helpers/slicedToArray'; import _regeneratorRuntime from '@babel/runtime/regenerator'; import { _ as __awaiter } from '../tslib.es6-848120af.js'; import { resolveBaseUrl, instance, encodeProject } from './helper.mjs'; import { resolveUserIds } from './user.mjs'; import 'axios'; /** * 从 TGit MR 的 URL 中解析出项目名称和 MR ID * * iid 与 id 的区别: * - iid:项目内的序号,从 1 开始递增,即 URL 和 Web 界面上看到的数字(如 /merge_requests/112 中的 112) * - id:MR 在整个 TGit 系统中的全局唯一 ID,不同项目间不重复 * * 本函数从 URL 中解析出的数字是 iid(项目内序号),不是全局 id。 * 如需获取全局 id,可使用 queryMRByIId 通过 iid 查询 MR 详情后获取。 * * @param {string} url TGit MR 的 URL,如 https://git.woa.com/group/project/-/merge_requests/112 * @returns {{ projectName: string, mrIid: string } | null} 解析结果(mrIid 为 URL 中的 iid),解析失败返回 null * @example * * // URL 中的 112 是 iid(项目内序号) * parseMRUrl('https://git.woa.com/pmd-mobile/ai-tool/rd-ai-common/-/merge_requests/112/notes') * // => { projectName: 'pmd-mobile/ai-tool/rd-ai-common', mrIid: '112' } * * parseMRUrl('https://git.woa.com/coecology/xd/-/merge_requests/169') * // => { projectName: 'coecology/xd', mrIid: '169' } */ function parseMRUrl(url) { // 匹配格式:https://git.woa.com/{projectPath}/-/merge_requests/{mrId}[/...] var match = url.match(/https?:\/\/[^/]+\/(.+?)\/-\/merge_requests\/(\d+)/); if (!match) { return null; } return { projectName: match[1], mrIid: match[2] }; } /** * 创建MR * @param {object} options 输入配置 * @param {string} options.projectName 项目名称 * @param {string} options.privateToken 密钥 * @param {string} options.sourceBranch 源分支 * @param {string} options.targetBranch 目标分支 * @param {number} [options.approverRule=1] 审批规则 * @param {number} [options.necessaryApproverRule=0] 必要审批规则 * @returns {Promise<object>} 请求Promise * @example * * createMR({ * projectName: 't-comm', * privateToken: 'xxxxx', * sourceBranch: 'master', * targetBranch: 'release', * }).then((resp) => { * * }) */ function createMR(_ref) { var projectName = _ref.projectName, privateToken = _ref.privateToken, sourceBranch = _ref.sourceBranch, targetBranch = _ref.targetBranch, _ref$approverRule = _ref.approverRule, approverRule = _ref$approverRule === void 0 ? 1 : _ref$approverRule, _ref$necessaryApprove = _ref.necessaryApproverRule, necessaryApproverRule = _ref$necessaryApprove === void 0 ? 0 : _ref$necessaryApprove, baseUrl = _ref.baseUrl, titlePrefix = _ref.titlePrefix, customTitle = _ref.title, description = _ref.description, assigneeList = _ref.assigneeList, reviewerList = _ref.reviewerList; return __awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee() { var url, defaultTitle, finalTitle, effectiveReviewerUsernames, _yield$Promise$all, _yield$Promise$all2, assigneeIds, reviewerIds, payload, _yield$instance, data; return _regeneratorRuntime.wrap(function (_context) { while (1) switch (_context.prev = _context.next) { case 0: if (!(!sourceBranch || !targetBranch || !projectName)) { _context.next = 1; break; } return _context.abrupt("return", Promise.reject(new Error('projectName, sourceBranch, targetBranch is required'))); case 1: url = resolveBaseUrl(baseUrl, "/api/v3/projects/".concat(encodeProject(projectName), "/merge_requests")); // 标题:优先使用显式传入的 title,其次走 titlePrefix + 默认模板 defaultTitle = "".concat(sourceBranch, " => ").concat(targetBranch); finalTitle = customTitle || (titlePrefix ? "".concat(titlePrefix, " ").concat(defaultTitle) : defaultTitle); // assignee / reviewer:显式传入且非空才触发解析,避免无意覆盖后端默认 effectiveReviewerUsernames = reviewerList !== null && reviewerList !== void 0 ? reviewerList : assigneeList; _context.next = 2; return Promise.all([assigneeList && assigneeList.length > 0 ? resolveUserIds({ usernames: assigneeList, privateToken: privateToken, baseUrl: baseUrl }) : Promise.resolve([]), effectiveReviewerUsernames && effectiveReviewerUsernames.length > 0 ? resolveUserIds({ usernames: effectiveReviewerUsernames, privateToken: privateToken, baseUrl: baseUrl }) : Promise.resolve([])]); case 2: _yield$Promise$all = _context.sent; _yield$Promise$all2 = _slicedToArray(_yield$Promise$all, 2); assigneeIds = _yield$Promise$all2[0]; reviewerIds = _yield$Promise$all2[1]; payload = { source_branch: sourceBranch, target_branch: targetBranch, title: finalTitle, approver_rule: approverRule, necessary_approver_rule: necessaryApproverRule }; if (description !== undefined) { payload.description = description; } if (assigneeIds.length > 0) { // 工蜂 v3 的 assignee_id 只接受单个数字 payload.assignee_id = Number(assigneeIds[0]); } if (reviewerIds.length > 0) { // 工蜂 v3 的 reviewers 接受逗号分隔的数字字符串 payload.reviewers = reviewerIds.join(','); } _context.next = 3; return instance({ url: url, method: 'POST', data: payload, headers: { 'PRIVATE-TOKEN': privateToken } }); case 3: _yield$instance = _context.sent; data = _yield$instance.data; return _context.abrupt("return", data); case 4: case "end": return _context.stop(); } }, _callee); })); } /** * 获取MR列表 * @param {object} options 输入配置 * @param {string} options.projectName 项目名称 * @param {string} options.privateToken 密钥 * @returns {Promise<object>} 请求Promise * @example * * getMrList({ * projectName: 't-comm', * privateToken: 'xxxxx', * }).then((resp) => { * * }) */ function getMrList(_ref2) { var projectName = _ref2.projectName, privateToken = _ref2.privateToken, baseUrl = _ref2.baseUrl; return new Promise(function (resolve, reject) { var url = "/api/v3/projects/".concat(encodeURIComponent(projectName), "/merge_requests"); url = resolveBaseUrl(baseUrl, url); instance({ url: url, method: 'GET', headers: { 'PRIVATE-TOKEN': privateToken } }).then(function (res) { resolve(res.data); })["catch"](function (err) { reject(err); }); }); } /** * 获取MR的一条评论 * @param {object} options 输入配置 * @param {string} options.projectName 项目名称 * @param {string} options.privateToken 密钥 * @param {string} options.mrId 某次MR的Id * @returns {Promise<object>} 请求Promise * @example * * getOneMrComments({ * projectName: 't-comm', * privateToken: 'xxxxx', * mrId: '1' * }).then((resp) => { * * }) */ function getOneMrComments(_ref3) { var mrId = _ref3.mrId, projectName = _ref3.projectName, privateToken = _ref3.privateToken, baseUrl = _ref3.baseUrl; return new Promise(function (resolve, reject) { var url = "/api/v3/projects/".concat(encodeURIComponent(projectName), "/merge_request/").concat(mrId, "/comments"); url = resolveBaseUrl(baseUrl, url); instance({ url: url, method: 'GET', headers: { 'PRIVATE-TOKEN': privateToken } }).then(function (res) { resolve(res.data); })["catch"](function (err) { reject(err); }); }); } /** * 发表评审意见(同意/拒绝/评论/要求修改) * @param {object} options 输入配置 * @param {string} [options.projectName] 项目名称(与 url 二选一) * @param {string} options.privateToken 密钥 * @param {string} [options.reviewableId] 合并请求的 reviewable_id(与 url 二选一)。注意 reviewer/summary API 需要的既不是全局 id 也不是 iid,而是 reviewable_id,可通过 queryMRByIId 获取 * @param {string} [options.url] TGit MR 的 URL,传入后自动解析 projectName 和 mrIid,并自动调用 queryMRByIId 获取 reviewable_id * @param {string} options.reviewerEvent 评审人事件,可选:comment | approve | require_change | deny * @param {string} [options.summary] 评审信息摘要 * @returns {Promise<object>} 请求Promise * @example * * // 方式一:直接传入 projectName 和 reviewableId * reviewMR({ * projectName: 't-comm', * privateToken: 'xxxxx', * reviewableId: '1', * reviewerEvent: 'approve', * summary: 'LGTM', * }).then((resp) => { * * }) * * // 方式二:传入 URL 自动解析 * reviewMR({ * url: 'https://git.woa.com/coecology/xd/-/merge_requests/169', * privateToken: 'xxxxx', * reviewerEvent: 'approve', * }).then((resp) => { * * }) */ function reviewMR(_ref4) { var baseUrl = _ref4.baseUrl, reviewableId = _ref4.reviewableId, projectName = _ref4.projectName, privateToken = _ref4.privateToken, reviewerEvent = _ref4.reviewerEvent, summary = _ref4.summary, url = _ref4.url; return __awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() { var resolvedProjectName, resolvedMrId, parsed, mrDetail, fullUrl; return _regeneratorRuntime.wrap(function (_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: resolvedProjectName = projectName; resolvedMrId = reviewableId; // 如果传入了 url,自动解析 projectName 和 mrIid,再通过 queryMRByIId 获取 reviewable_id if (!url) { _context2.next = 2; break; } parsed = parseMRUrl(url); if (!parsed) { _context2.next = 2; break; } resolvedProjectName = resolvedProjectName || parsed.projectName; if (!(!resolvedMrId && resolvedProjectName)) { _context2.next = 2; break; } _context2.next = 1; return queryMRByIId({ projectName: resolvedProjectName, privateToken: privateToken, mrIid: parsed.mrIid, baseUrl: baseUrl }); case 1: mrDetail = _context2.sent; resolvedMrId = (mrDetail === null || mrDetail === void 0 ? void 0 : mrDetail.reviewable_id) != null ? String(mrDetail.reviewable_id) : undefined; case 2: if (!(!resolvedProjectName || !resolvedMrId || !reviewerEvent)) { _context2.next = 3; break; } return _context2.abrupt("return", Promise.reject()); case 3: fullUrl = "/api/v3/projects/".concat(encodeURIComponent(resolvedProjectName), "/merge_request/").concat(resolvedMrId, "/reviewer/summary"); fullUrl = resolveBaseUrl(baseUrl, fullUrl); return _context2.abrupt("return", instance({ url: fullUrl, method: 'PUT', data: { reviewer_event: reviewerEvent, summary: summary }, headers: { 'PRIVATE-TOKEN': privateToken } }).then(function (res) { return res.data; })); case 4: case "end": return _context2.stop(); } }, _callee2); })); } /** * 根据 iid(项目内序号)查询 MR 评审信息 * * iid 与 id 的区别: * - iid:项目内的序号,从 1 开始递增,即 URL 和 Web 界面上看到的数字(如 /merge_requests/112 中的 112) * - id:MR 在整个 TGit 系统中的全局唯一 ID,不同项目间不重复 * * 对应 API:GET /api/v3/projects/:id/merge_request/iid/:merge_request_iid/review * * @param {object} options 输入配置 * @param {string} options.projectName 项目名称或项目全路径 * @param {string} options.privateToken 密钥 * @param {string} options.mrIid 合并请求在项目中的编号 iid(即 URL 中看到的数字) * @param {string} [options.baseUrl] 自定义 API 基础路径 * @returns {Promise<object>} MR 评审信息,包含 id(评审记录全局 ID)、reviewable_id(reviewer/summary 接口需要的 ID)、iid、title、description、author 等字段 * @example * * queryMRByIId({ * projectName: 'coecology/xd', * privateToken: 'xxxxx', * mrIid: '169', * }).then((resp) => { * console.log(resp.id); // 全局 id * console.log(resp.title); // MR 标题 * }) */ function queryMRByIId(_ref5) { var projectName = _ref5.projectName, privateToken = _ref5.privateToken, mrIid = _ref5.mrIid, baseUrl = _ref5.baseUrl; return new Promise(function (resolve, reject) { if (!projectName || !mrIid) { reject(); return; } var fullUrl = "/api/v3/projects/".concat(encodeURIComponent(projectName), "/merge_request/iid/").concat(mrIid, "/review"); fullUrl = resolveBaseUrl(baseUrl, fullUrl); instance({ url: fullUrl, method: 'GET', headers: { 'PRIVATE-TOKEN': privateToken } }).then(function (res) { resolve(res.data); })["catch"](function (err) { reject(err); }); }); } /** * 通过 iid(项目内 MR 序号)获取 MR 的全局 id * * iid 与 id 的区别: * - iid:项目内的序号,从 1 开始递增,即 URL 和 Web 界面上看到的数字 * - id:MR 在整个 TGit 系统中的全局唯一 ID * * 工蜂 v3 API 的 merge_request 详情接口使用全局 id,而非 iid。 * 新建的 MR 在工蜂 API 侧可能有短暂同步延迟,本函数默认进行 3 次重试(3s 间隔)。 * * 对应工蜂 API:GET /api/v3/projects/:id/merge_requests?iid=:iid * * @param {object} options 输入配置 * @param {string | number} options.projectName 项目名称或项目 ID * @param {number | string} options.mrIid MR 的项目内序号 * @param {string} options.privateToken 密钥 * @param {string} [options.baseUrl] baseUrl * @param {number} [options.maxRetries=3] 最大重试次数 * @param {number} [options.retryDelay=3000] 重试间隔(毫秒) * @returns {Promise<number>} MR 的全局 id * @example * * getMRIdByIid({ * projectName: 'group/sub/repo', * mrIid: 112, * privateToken: 'xxxxx', * }).then((id) => { * console.log(id); // 全局 id * }) */ function getMRIdByIid(_ref6) { var projectName = _ref6.projectName, mrIid = _ref6.mrIid, privateToken = _ref6.privateToken, baseUrl = _ref6.baseUrl, _ref6$maxRetries = _ref6.maxRetries, maxRetries = _ref6$maxRetries === void 0 ? 3 : _ref6$maxRetries, _ref6$retryDelay = _ref6.retryDelay, retryDelay = _ref6$retryDelay === void 0 ? 3000 : _ref6$retryDelay; return __awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee3() { var url, attempt, _yield$instance2, data, list, _t; return _regeneratorRuntime.wrap(function (_context3) { while (1) switch (_context3.prev = _context3.next) { case 0: url = resolveBaseUrl(baseUrl, "/api/v3/projects/".concat(encodeProject(projectName), "/merge_requests")); attempt = 1; case 1: if (!(attempt <= maxRetries)) { _context3.next = 8; break; } _context3.prev = 2; _context3.next = 3; return instance({ url: url, method: 'GET', params: { iid: mrIid }, headers: { 'PRIVATE-TOKEN': privateToken } }); case 3: _yield$instance2 = _context3.sent; data = _yield$instance2.data; list = data || []; if (!(list.length > 0)) { _context3.next = 4; break; } return _context3.abrupt("return", list[0].id); case 4: _context3.next = 6; break; case 5: _context3.prev = 5; _t = _context3["catch"](2); if (!(attempt === maxRetries)) { _context3.next = 6; break; } throw _t; case 6: if (!(attempt < maxRetries)) { _context3.next = 7; break; } _context3.next = 7; return new Promise(function (resolve) { return setTimeout(resolve, retryDelay); }); case 7: attempt++; _context3.next = 1; break; case 8: throw new Error("\u9879\u76EE ".concat(projectName, " \u4E2D\u672A\u627E\u5230 iid=").concat(mrIid, " \u7684 MR\uFF08\u5DF2\u91CD\u8BD5 ").concat(maxRetries, " \u6B21\uFF09")); case 9: case "end": return _context3.stop(); } }, _callee3, null, [[2, 5]]); })); } /** * 获取 MR 详情(参数为 iid,内部自动转全局 id) * * 对应工蜂 API:GET /api/v3/projects/:id/merge_request/:id * * @param {object} options 输入配置 * @param {string | number} options.projectName 项目名称或项目 ID * @param {number | string} options.mrIid MR 的项目内序号 * @param {string} options.privateToken 密钥 * @param {string} [options.baseUrl] baseUrl * @returns {Promise<MRDetail>} MR 详情 * @example * * getMRDetail({ * projectName: 'coecology/xd', * mrIid: 169, * privateToken: 'xxxxx', * }).then((mr) => { * console.log(mr.title, mr.state); * }) */ function getMRDetail(_ref7) { var projectName = _ref7.projectName, mrIid = _ref7.mrIid, privateToken = _ref7.privateToken, baseUrl = _ref7.baseUrl; return __awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee4() { var mrId, url, _yield$instance3, data; return _regeneratorRuntime.wrap(function (_context4) { while (1) switch (_context4.prev = _context4.next) { case 0: _context4.next = 1; return getMRIdByIid({ projectName: projectName, mrIid: mrIid, privateToken: privateToken, baseUrl: baseUrl }); case 1: mrId = _context4.sent; url = resolveBaseUrl(baseUrl, "/api/v3/projects/".concat(encodeProject(projectName), "/merge_request/").concat(mrId)); _context4.next = 2; return instance({ url: url, method: 'GET', headers: { 'PRIVATE-TOKEN': privateToken } }); case 2: _yield$instance3 = _context4.sent; data = _yield$instance3.data; return _context4.abrupt("return", data); case 3: case "end": return _context4.stop(); } }, _callee4); })); } /** * 获取 MR 变更内容(含 diff) * * 对应工蜂 API:GET /api/v3/projects/:id/merge_request/:id/changes * * 工蜂 v3 API 返回的变更文件字段名为 `files`,本函数会额外映射一个 `changes` 字段 * 方便调用方以统一的字段名访问(同时保留 files)。 * * @param {object} options 输入配置 * @param {string | number} options.projectName 项目名称或项目 ID * @param {number | string} options.mrIid MR 的项目内序号 * @param {string} options.privateToken 密钥 * @param {string} [options.baseUrl] baseUrl * @returns {Promise<MRChangesDetail>} MR 变更详情 * @example * * getMRChanges({ * projectName: 'coecology/xd', * mrIid: 169, * privateToken: 'xxxxx', * }).then((res) => { * res.changes.forEach(c => console.log(c.new_path)); * }) */ function getMRChanges(_ref8) { var projectName = _ref8.projectName, mrIid = _ref8.mrIid, privateToken = _ref8.privateToken, baseUrl = _ref8.baseUrl; return __awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee5() { var mrId, url, _yield$instance4, data, empty, raw, result; return _regeneratorRuntime.wrap(function (_context5) { while (1) switch (_context5.prev = _context5.next) { case 0: _context5.next = 1; return getMRIdByIid({ projectName: projectName, mrIid: mrIid, privateToken: privateToken, baseUrl: baseUrl }); case 1: mrId = _context5.sent; url = resolveBaseUrl(baseUrl, "/api/v3/projects/".concat(encodeProject(projectName), "/merge_request/").concat(mrId, "/changes")); _context5.next = 2; return instance({ url: url, method: 'GET', headers: { 'PRIVATE-TOKEN': privateToken } }); case 2: _yield$instance4 = _context5.sent; data = _yield$instance4.data; empty = {}; raw = data || empty; result = Object.assign(Object.assign({}, raw), { changes: raw.files || [] }); return _context5.abrupt("return", result); case 3: case "end": return _context5.stop(); } }, _callee5); })); } /** * 在 MR 上提交评论(Note) * * - 不传 filePath/line:普通评论(贴在 MR 讨论区) * - 传 filePath 和 line:行内评论(贴在 diff 指定文件的指定行) * * 对应工蜂 API:POST /api/v3/projects/:id/merge_requests/:id/notes * * @param {object} options 输入配置 * @param {string | number} options.projectName 项目名称或项目 ID * @param {number | string} options.mrIid MR 的项目内序号 * @param {string} options.body 评论内容 * @param {string} options.privateToken 密钥 * @param {string} [options.filePath] 行内评论:diff 中的新文件路径 * @param {number} [options.line] 行内评论:新文件中的行号 * @param {'new' | 'old'} [options.lineType='new'] 行内评论:行类型,默认 new * @param {string} [options.baseUrl] baseUrl * @returns {Promise<{ id: number }>} 新建评论的 ID * @example * * // 1. 普通评论 * createMRNote({ * projectName: 'coecology/xd', * mrIid: 169, * body: 'LGTM', * privateToken: 'xxxxx', * }).then((res) => console.log(res.id)); * * // 2. 行内评论 * createMRNote({ * projectName: 'coecology/xd', * mrIid: 169, * body: '这里可以优化', * filePath: 'src/index.ts', * line: 12, * lineType: 'new', * privateToken: 'xxxxx', * }); */ function createMRNote(_ref9) { var projectName = _ref9.projectName, mrIid = _ref9.mrIid, body = _ref9.body, privateToken = _ref9.privateToken, filePath = _ref9.filePath, line = _ref9.line, _ref9$lineType = _ref9.lineType, lineType = _ref9$lineType === void 0 ? 'new' : _ref9$lineType, baseUrl = _ref9.baseUrl; return __awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee6() { var mrId, url, payload, _yield$instance5, data; return _regeneratorRuntime.wrap(function (_context6) { while (1) switch (_context6.prev = _context6.next) { case 0: _context6.next = 1; return getMRIdByIid({ projectName: projectName, mrIid: mrIid, privateToken: privateToken, baseUrl: baseUrl }); case 1: mrId = _context6.sent; url = resolveBaseUrl(baseUrl, "/api/v3/projects/".concat(encodeProject(projectName), "/merge_requests/").concat(mrId, "/notes")); payload = { body: body }; if (filePath && typeof line === 'number') { payload.path = filePath; payload.line = line; payload.line_type = lineType; } _context6.next = 2; return instance({ url: url, method: 'POST', data: payload, headers: { 'PRIVATE-TOKEN': privateToken } }); case 2: _yield$instance5 = _context6.sent; data = _yield$instance5.data; return _context6.abrupt("return", data); case 3: case "end": return _context6.stop(); } }, _callee6); })); } /** * 获取 MR 的所有评论(Notes)列表 * * 对应工蜂 API:GET /api/v3/projects/:id/merge_requests/:id/notes * * @param {object} options 输入配置 * @param {string | number} options.projectName 项目名称或项目 ID * @param {number | string} options.mrIid MR 的项目内序号 * @param {string} options.privateToken 密钥 * @param {number} [options.perPage=100] 每页数量 * @param {string} [options.baseUrl] baseUrl * @returns {Promise<Array<Record<string, unknown>>>} 评论列表 * @example * * getMRNotes({ * projectName: 'coecology/xd', * mrIid: 169, * privateToken: 'xxxxx', * }).then((notes) => { * notes.forEach(n => console.log(n.id, n.body)); * }) */ function getMRNotes(_ref0) { var projectName = _ref0.projectName, mrIid = _ref0.mrIid, privateToken = _ref0.privateToken, _ref0$perPage = _ref0.perPage, perPage = _ref0$perPage === void 0 ? 100 : _ref0$perPage, baseUrl = _ref0.baseUrl; return __awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee7() { var mrId, url, _yield$instance6, data; return _regeneratorRuntime.wrap(function (_context7) { while (1) switch (_context7.prev = _context7.next) { case 0: _context7.next = 1; return getMRIdByIid({ projectName: projectName, mrIid: mrIid, privateToken: privateToken, baseUrl: baseUrl }); case 1: mrId = _context7.sent; url = resolveBaseUrl(baseUrl, "/api/v3/projects/".concat(encodeProject(projectName), "/merge_requests/").concat(mrId, "/notes")); _context7.next = 2; return instance({ url: url, method: 'GET', params: { per_page: perPage }, headers: { 'PRIVATE-TOKEN': privateToken } }); case 2: _yield$instance6 = _context7.sent; data = _yield$instance6.data; return _context7.abrupt("return", data || []); case 3: case "end": return _context7.stop(); } }, _callee7); })); } /** * 获取 MR 的单条评论(Note)详情 * * 对应工蜂 API:GET /api/v3/projects/:id/merge_requests/:id/notes/:noteId * * @param {object} options 输入配置 * @param {string | number} options.projectName 项目名称或项目 ID * @param {number | string} options.mrIid MR 的项目内序号 * @param {number | string} options.noteId 评论 ID * @param {string} options.privateToken 密钥 * @param {string} [options.baseUrl] baseUrl * @returns {Promise<Record<string, unknown>>} 评论详情 * @example * * getMRNoteById({ * projectName: 'coecology/xd', * mrIid: 169, * noteId: 123456, * privateToken: 'xxxxx', * }).then((note) => { * console.log(note.body); * }) */ function getMRNoteById(_ref1) { var projectName = _ref1.projectName, mrIid = _ref1.mrIid, noteId = _ref1.noteId, privateToken = _ref1.privateToken, baseUrl = _ref1.baseUrl; return __awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee8() { var mrId, url, _yield$instance7, data; return _regeneratorRuntime.wrap(function (_context8) { while (1) switch (_context8.prev = _context8.next) { case 0: _context8.next = 1; return getMRIdByIid({ projectName: projectName, mrIid: mrIid, privateToken: privateToken, baseUrl: baseUrl }); case 1: mrId = _context8.sent; url = resolveBaseUrl(baseUrl, "/api/v3/projects/".concat(encodeProject(projectName), "/merge_requests/").concat(mrId, "/notes/").concat(noteId)); _context8.next = 2; return instance({ url: url, method: 'GET', headers: { 'PRIVATE-TOKEN': privateToken } }); case 2: _yield$instance7 = _context8.sent; data = _yield$instance7.data; return _context8.abrupt("return", data); case 3: case "end": return _context8.stop(); } }, _callee8); })); } /** * 更新 MR 上的已有评论 * * 对应工蜂 API:PUT /api/v3/projects/:id/merge_requests/:id/notes/:noteId * * @param {object} options 输入配置 * @param {string | number} options.projectName 项目名称或项目 ID * @param {number | string} options.mrIid MR 的项目内序号 * @param {number | string} options.noteId 要更新的评论 ID * @param {string} options.body 新的评论内容 * @param {string} options.privateToken 密钥 * @param {string} [options.baseUrl] baseUrl * @returns {Promise<{ id: number }>} 更新后的评论 * @example * * updateMRNote({ * projectName: 'coecology/xd', * mrIid: 169, * noteId: 123456, * body: '更新后的评论内容', * privateToken: 'xxxxx', * }).then((res) => console.log(res.id)); */ function updateMRNote(_ref10) { var projectName = _ref10.projectName, mrIid = _ref10.mrIid, noteId = _ref10.noteId, body = _ref10.body, privateToken = _ref10.privateToken, baseUrl = _ref10.baseUrl; return __awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee9() { var mrId, url, _yield$instance8, data; return _regeneratorRuntime.wrap(function (_context9) { while (1) switch (_context9.prev = _context9.next) { case 0: _context9.next = 1; return getMRIdByIid({ projectName: projectName, mrIid: mrIid, privateToken: privateToken, baseUrl: baseUrl }); case 1: mrId = _context9.sent; url = resolveBaseUrl(baseUrl, "/api/v3/projects/".concat(encodeProject(projectName), "/merge_requests/").concat(mrId, "/notes/").concat(noteId)); _context9.next = 2; return instance({ url: url, method: 'PUT', data: { body: body }, headers: { 'PRIVATE-TOKEN': privateToken } }); case 2: _yield$instance8 = _context9.sent; data = _yield$instance8.data; return _context9.abrupt("return", data); case 3: case "end": return _context9.stop(); } }, _callee9); })); } /** * 回复 MR 上的已有评论(在讨论串中回复) * * 对应工蜂 API:POST /api/v3/projects/:id/merge_requests/:id/notes/:noteId/replies * * @param {object} options 输入配置 * @param {string | number} options.projectName 项目名称或项目 ID * @param {number | string} options.mrIid MR 的项目内序号 * @param {number | string} options.parentNoteId 要回复的父评论 ID * @param {string} options.body 回复内容 * @param {string} options.privateToken 密钥 * @param {string} [options.baseUrl] baseUrl * @returns {Promise<{ id: number }>} 新建回复的 ID * @example * * createNoteReply({ * projectName: 'coecology/xd', * mrIid: 169, * parentNoteId: 123456, * body: '同意上面的意见', * privateToken: 'xxxxx', * }).then((res) => console.log(res.id)); */ function createNoteReply(_ref11) { var projectName = _ref11.projectName, mrIid = _ref11.mrIid, parentNoteId = _ref11.parentNoteId, body = _ref11.body, privateToken = _ref11.privateToken, baseUrl = _ref11.baseUrl; return __awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee0() { var mrId, url, _yield$instance9, data; return _regeneratorRuntime.wrap(function (_context0) { while (1) switch (_context0.prev = _context0.next) { case 0: _context0.next = 1; return getMRIdByIid({ projectName: projectName, mrIid: mrIid, privateToken: privateToken, baseUrl: baseUrl }); case 1: mrId = _context0.sent; url = resolveBaseUrl(baseUrl, "/api/v3/projects/".concat(encodeProject(projectName), "/merge_requests/").concat(mrId, "/notes/").concat(parentNoteId, "/replies")); _context0.next = 2; return instance({ url: url, method: 'POST', data: { body: body }, headers: { 'PRIVATE-TOKEN': privateToken } }); case 2: _yield$instance9 = _context0.sent; data = _yield$instance9.data; return _context0.abrupt("return", data); case 3: case "end": return _context0.stop(); } }, _callee0); })); } /** * 回复代码评审中的评论(行内评论 reply) * * 对应工蜂 API:POST /api/v3/projects/:id/reviews/:reviewId/notes/:noteId/replies * * 只能回复代码行上的第一条评论,不支持回复「已是回复」的评论。 * * @param {object} options 输入配置 * @param {string | number} options.projectName 项目名称或项目 ID * @param {number | string} options.reviewId 评审记录 ID * @param {number | string} options.noteId 要回复的评论 ID * @param {string} options.body 回复内容 * @param {string} options.privateToken 密钥 * @param {string} [options.baseUrl] baseUrl * @returns {Promise<{ id: number }>} 新建回复的 ID * @example * * createReviewNoteReply({ * projectName: 'coecology/xd', * reviewId: 9999, * noteId: 123456, * body: '这里应该补一下边界检查', * privateToken: 'xxxxx', * }).then((res) => console.log(res.id)); */ function createReviewNoteReply(_ref12) { var projectName = _ref12.projectName, reviewId = _ref12.reviewId, noteId = _ref12.noteId, body = _ref12.body, privateToken = _ref12.privateToken, baseUrl = _ref12.baseUrl; return __awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee1() { var url, _yield$instance0, data; return _regeneratorRuntime.wrap(function (_context1) { while (1) switch (_context1.prev = _context1.next) { case 0: url = resolveBaseUrl(baseUrl, "/api/v3/projects/".concat(encodeProject(projectName), "/reviews/").concat(reviewId, "/notes/").concat(noteId, "/replies")); _context1.next = 1; return instance({ url: url, method: 'POST', data: { body: body }, headers: { 'PRIVATE-TOKEN': privateToken } }); case 1: _yield$instance0 = _context1.sent; data = _yield$instance0.data; return _context1.abrupt("return", data); case 2: case "end": return _context1.stop(); } }, _callee1); })); } /** * 关闭 MR(参数为 iid,内部自动转全局 id) * * 对应工蜂 API:PUT /api/v3/projects/:id/merge_request/:id body: { state_event: 'close' } * * @param {object} options 输入配置 * @param {string | number} options.projectName 项目名称或项目 ID * @param {number | string} options.mrIid MR 的项目内序号 * @param {string} options.privateToken 密钥 * @param {string} [options.baseUrl] baseUrl * @returns {Promise<MRDetail>} 关闭后的 MR 详情 * @example * * closeMR({ * projectName: 'coecology/xd', * mrIid: 169, * privateToken: 'xxxxx', * }).then((mr) => { * console.log(mr.state); * }) */ function closeMR(_ref13) { var projectName = _ref13.projectName, mrIid = _ref13.mrIid, privateToken = _ref13.privateToken, baseUrl = _ref13.baseUrl; return __awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee10() { var mrId, url, _yield$instance1, data; return _regeneratorRuntime.wrap(function (_context10) { while (1) switch (_context10.prev = _context10.next) { case 0: _context10.next = 1; return getMRIdByIid({ projectName: projectName, mrIid: mrIid, privateToken: privateToken, baseUrl: baseUrl }); case 1: mrId = _context10.sent; if (mrId) { _context10.next = 2; break; } throw new Error("\u901A\u8FC7 getMRIdByIid \u672A\u83B7\u53D6\u5230 MR \u5168\u5C40 id\uFF0C\u8BF7\u68C0\u67E5 MR \u72B6\u6001: projectName=".concat(projectName, ", mrIid=").concat(mrIid)); case 2: url = resolveBaseUrl(baseUrl, "/api/v3/projects/".concat(encodeProject(projectName), "/merge_request/").concat(mrId)); _context10.next = 3; return instance({ url: url, method: 'PUT', data: { state_event: 'close' }, headers: { 'PRIVATE-TOKEN': privateToken } }); case 3: _yield$instance1 = _context10.sent; data = _yield$instance1.data; return _context10.abrupt("return", data); case 4: case "end": return _context10.stop(); } }, _callee10); })); } /** * 通过 MR URL 关闭一个 MR * * 内部流程: * 1. 调用 {@link parseMRUrl} 从 URL 中解析出 projectName + mrIid * 2. 调用 {@link closeMR} 完成关闭操作 * * @param {object} options 输入配置 * @param {string} options.mrUrl MR 的 URL * @param {string} options.privateToken 密钥 * @param {string} [options.baseUrl] baseUrl * @returns {Promise<{ projectName: string; mrIid: string; raw: MRDetail }>} 解析与关闭结果 * @example * * closeMRByUrl({ * mrUrl: 'https://git.woa.com/coecology/xd/-/merge_requests/169', * privateToken: 'xxxxx', * }).then((res) => { * console.log(res.projectName, res.mrIid, res.raw.state); * }) */ function closeMRByUrl(_ref14) { var mrUrl = _ref14.mrUrl, privateToken = _ref14.privateToken, baseUrl = _ref14.baseUrl; return __awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee11() { var parsed, projectName, mrIid, raw; return _regeneratorRuntime.wrap(function (_context11) { while (1) switch (_context11.prev = _context11.next) { case 0: if (mrUrl) { _context11.next = 1; break; } throw new Error('缺少必要参数:mrUrl'); case 1: parsed = parseMRUrl(mrUrl); if (parsed) { _context11.next = 2; break; } throw new Error("\u65E0\u6CD5\u4ECE URL \u89E3\u6790\u51FA projectName \u548C mrIid: ".concat(mrUrl)); case 2: projectName = parsed.projectName, mrIid = parsed.mrIid; _context11.next = 3; return closeMR({ projectName: projectName, mrIid: mrIid, privateToken: privateToken, baseUrl: baseUrl }); case 3: raw = _context11.sent; return _context11.abrupt("return", { projectName: projectName, mrIid: mrIid, raw: raw }); case 4: case "end": return _context11.stop(); } }, _callee11); })); } export { closeMR, closeMRByUrl, createMR, createMRNote, createNoteReply, createReviewNoteReply, getMRChanges, getMRDetail, getMRIdByIid, getMRNoteById, getMRNotes, getMrList, getOneMrComments, parseMRUrl, queryMRByIId, reviewMR, updateMRNote };