t-comm
Version:
专业、稳定、纯粹的工具库
1,152 lines (1,149 loc) • 40.1 kB
JavaScript
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 };