@zereight/mcp-gitlab 2.1.63 → 2.1.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +20 -5
- package/README.md +19 -6
- package/README.zh-CN.md +19 -6
- package/build/downloads/proxy.js +14 -7
- package/build/index.js +413 -455
- package/build/schemas.js +2 -2
- package/build/scripts/generate-tool-coverage.js +58 -0
- package/build/scripts/generate-tool-docs.js +6 -1
- package/build/scripts/tool-coverage/coverage.js +339 -0
- package/build/scripts/tool-coverage/coverage.test.js +142 -0
- package/build/test/path-segment-encoding.test.js +73 -17
- package/build/test/sse-session-limits.test.js +273 -0
- package/build/test/test-blank-filters.js +156 -0
- package/build/test/test-download-attachment.js +11 -0
- package/build/test/test-empty-toolsets.js +535 -0
- package/build/test/test-geteffectiveprojectid.js +1 -1
- package/build/test/test-job-artifacts.js +22 -0
- package/build/test/test-list-issues.js +47 -0
- package/build/test/test-list-merge-requests.js +76 -0
- package/build/test/test-list-milestones.js +145 -0
- package/build/test/test-permission-mode.js +103 -12
- package/build/test/test-remote-downloads.js +14 -1
- package/build/test/utils/graphql-query.test.js +72 -0
- package/build/test/utils/safe-redirect-fetch.test.js +463 -0
- package/build/test/utils/tool-args.test.js +89 -1
- package/build/test/utils/url.test.js +128 -0
- package/build/test-note.js +2 -1
- package/build/test-resolve-issue-note.js +4 -3
- package/build/tools/registry.js +16 -2
- package/build/utils/graphql-query.js +31 -9
- package/build/utils/safe-redirect-fetch.js +321 -0
- package/build/utils/tool-args.js +47 -3
- package/build/utils/url.js +102 -0
- package/package.json +2 -1
package/build/index.js
CHANGED
|
@@ -89,7 +89,7 @@ import { formatPrometheusMetrics } from "./server/metrics.js";
|
|
|
89
89
|
import { SERVER_VERSION } from "./server/version.js";
|
|
90
90
|
import { isInitializationRequestBody, isUnauthenticatedDiscoveryRequestBody, readAcceptHeader, readMcpSessionIdHeader, redactSessionIdForLog, } from "./server/request-helpers.js";
|
|
91
91
|
export { readMcpSessionIdHeader } from "./server/request-helpers.js";
|
|
92
|
-
import { normalizeGitLabApiUrl } from "./utils/url.js";
|
|
92
|
+
import { encodeGitLabPath, encodeGitLabPathSegment, normalizeGitLabApiUrl, } from "./utils/url.js";
|
|
93
93
|
import { estimateMergeCommitCount, filterDiffsByPatterns, openSafeOutputWriteStream, readSafeExistingFile, summarizeWebhookEvents, } from "./utils/helpers.js";
|
|
94
94
|
import { graphqlQueryContainsWriteOperation, graphqlQueryContainsDeleteOperation, } from "./utils/graphql-query.js";
|
|
95
95
|
import { resolveNestedWikiUpdateTitle } from "./utils/wiki-title.js";
|
|
@@ -98,9 +98,10 @@ import { redactSensitiveGitLabFields } from "./utils/redact-sensitive.js";
|
|
|
98
98
|
import { createMaskingPolicyResolver, getManagedMaskingProjectIds, } from "./masking/index.js";
|
|
99
99
|
import { checkForNewVersion } from "./utils/version-check.js";
|
|
100
100
|
import { assertGitLabVersionAtLeast } from "./utils/gitlab-version-gate.js";
|
|
101
|
+
import { fetchWithValidatedRedirects } from "./utils/safe-redirect-fetch.js";
|
|
101
102
|
import { parseGitLabVersionApiResponse, } from "./utils/gitlab-instance-version.js";
|
|
102
103
|
import { buildBulkPublishDraftNotesBody, needsGitLab19_2BulkPublish, } from "./utils/bulk-publish-options.js";
|
|
103
|
-
import { cleanMutuallyExclusiveIdUsernameOptions, LIST_MERGE_REQUESTS_ID_USERNAME_PAIRS, sanitizeToolArguments, } from "./utils/tool-args.js";
|
|
104
|
+
import { appendFilterParam, appendFilterParams, cleanMutuallyExclusiveIdUsernameOptions, dropBlankArrayEntries, isBlankFilterValue, LIST_MERGE_REQUESTS_ID_USERNAME_PAIRS, sanitizeToolArguments, } from "./utils/tool-args.js";
|
|
104
105
|
import { parseSearchReplaceBlocks, applySearchReplace, applyUnifiedDiff, } from "./utils/patch-helper.js";
|
|
105
106
|
import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js";
|
|
106
107
|
import { GitLabClientPool } from "./gitlab-client-pool.js";
|
|
@@ -829,7 +830,7 @@ async function ensureValidOAuthToken() {
|
|
|
829
830
|
throw error;
|
|
830
831
|
}
|
|
831
832
|
}
|
|
832
|
-
// Permission mode gate: readonly exposes only read tools; modify blocks delete tools; full allows all
|
|
833
|
+
// Permission mode gate: readonly exposes only read tools; modify blocks delete/teardown tools; full allows all
|
|
833
834
|
function isToolAllowedByPermissionMode(toolName) {
|
|
834
835
|
switch (GITLAB_PERMISSION_MODE) {
|
|
835
836
|
case "readonly":
|
|
@@ -1165,6 +1166,21 @@ const getFetchConfig = () => {
|
|
|
1165
1166
|
dispatcher,
|
|
1166
1167
|
};
|
|
1167
1168
|
};
|
|
1169
|
+
/**
|
|
1170
|
+
* Options for the download sinks that stream a file through `fetchWithValidatedRedirects`.
|
|
1171
|
+
*
|
|
1172
|
+
* GitLab answers a release asset, a job artifact or an uploaded file with a redirect to
|
|
1173
|
+
* its object storage, so those hops leave the origin and must be validated. The client
|
|
1174
|
+
* split matters as much as the headers: the wrapped client adds the cookie jar and a
|
|
1175
|
+
* fresh `Authorization` on a `401`, so a hop whose credentials were withheld has to use
|
|
1176
|
+
* plain undici instead.
|
|
1177
|
+
*/
|
|
1178
|
+
const downloadRedirectOptions = () => ({
|
|
1179
|
+
...getFetchConfig(),
|
|
1180
|
+
fetchImpl: fetch,
|
|
1181
|
+
unauthenticatedFetchImpl: undiciFetch,
|
|
1182
|
+
isTrustedRedirectHost: isTrustedGitLabRedirectHost,
|
|
1183
|
+
});
|
|
1168
1184
|
// Compute at startup
|
|
1169
1185
|
const enabledToolsets = parseEnabledToolsets(GITLAB_TOOLSETS_RAW);
|
|
1170
1186
|
const individuallyEnabledTools = parseIndividualTools(GITLAB_TOOLS_RAW);
|
|
@@ -1195,23 +1211,6 @@ function parseAllowedGitLabApiUrls(value) {
|
|
|
1195
1211
|
.map(toAllowedGitLabApiUrl)
|
|
1196
1212
|
.filter((entry) => Boolean(entry));
|
|
1197
1213
|
}
|
|
1198
|
-
function encodeGitLabPathSegment(value) {
|
|
1199
|
-
const segment = String(value);
|
|
1200
|
-
let decodedSegment;
|
|
1201
|
-
try {
|
|
1202
|
-
decodedSegment = decodeURIComponent(segment);
|
|
1203
|
-
}
|
|
1204
|
-
catch {
|
|
1205
|
-
decodedSegment = segment;
|
|
1206
|
-
}
|
|
1207
|
-
if (decodedSegment === "." || decodedSegment === "..") {
|
|
1208
|
-
throw new Error("GitLab URL path segments cannot be '.' or '..'");
|
|
1209
|
-
}
|
|
1210
|
-
return encodeURIComponent(decodedSegment);
|
|
1211
|
-
}
|
|
1212
|
-
function encodeGitLabPath(value) {
|
|
1213
|
-
return value.split("/").map(encodeGitLabPathSegment).join("/");
|
|
1214
|
-
}
|
|
1215
1214
|
function resolveTrustedGitLabApiUrl(value) {
|
|
1216
1215
|
const parsed = new URL(normalizeGitLabApiUrl(value));
|
|
1217
1216
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
@@ -1238,6 +1237,17 @@ for (const { host, apiUrl } of [
|
|
|
1238
1237
|
GITLAB_ALLOWED_API_URLS_BY_HOST.set(host, apiUrl);
|
|
1239
1238
|
}
|
|
1240
1239
|
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Redirects from the GitLab API to operator-declared GitLab hosts are followed even
|
|
1242
|
+
* when the host resolves to a private address (self-hosted instances and their
|
|
1243
|
+
* storage may live on an internal network), and those hosts keep receiving the
|
|
1244
|
+
* request credentials. Every other redirect target must resolve to a public
|
|
1245
|
+
* address and is fetched without credentials.
|
|
1246
|
+
*
|
|
1247
|
+
* The lookup key is `URL.host`, which keeps a non-default port, matching how this
|
|
1248
|
+
* map is built from GITLAB_API_URL / GITLAB_ALLOWED_HOSTS.
|
|
1249
|
+
*/
|
|
1250
|
+
const isTrustedGitLabRedirectHost = (host) => GITLAB_ALLOWED_API_URLS_BY_HOST.has(host);
|
|
1241
1251
|
const GITLAB_PROJECT_ID = process.env.GITLAB_PROJECT_ID;
|
|
1242
1252
|
const GITLAB_ALLOWED_PROJECT_IDS = process.env.GITLAB_ALLOWED_PROJECT_IDS?.split(",")
|
|
1243
1253
|
.map(id => id.trim())
|
|
@@ -1364,7 +1374,7 @@ function rejectIfProjectScopedDeployment(toolName) {
|
|
|
1364
1374
|
async function forkProject(projectId, namespace) {
|
|
1365
1375
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1366
1376
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
1367
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
1377
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/fork`);
|
|
1368
1378
|
if (namespace) {
|
|
1369
1379
|
url.searchParams.append("namespace", namespace);
|
|
1370
1380
|
}
|
|
@@ -1391,7 +1401,7 @@ async function forkProject(projectId, namespace) {
|
|
|
1391
1401
|
async function createBranch(projectId, options) {
|
|
1392
1402
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1393
1403
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
1394
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
1404
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/repository/branches`);
|
|
1395
1405
|
const response = await fetch(url.toString(), {
|
|
1396
1406
|
...getFetchConfig(),
|
|
1397
1407
|
method: "POST",
|
|
@@ -1413,7 +1423,7 @@ async function createBranch(projectId, options) {
|
|
|
1413
1423
|
async function getDefaultBranchRef(projectId) {
|
|
1414
1424
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1415
1425
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
1416
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
1426
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}`);
|
|
1417
1427
|
const response = await fetch(url.toString(), {
|
|
1418
1428
|
...getFetchConfig(),
|
|
1419
1429
|
});
|
|
@@ -1433,12 +1443,12 @@ async function getDefaultBranchRef(projectId) {
|
|
|
1433
1443
|
async function getFileContents(projectId, filePath, ref) {
|
|
1434
1444
|
const decodedProjectId = projectId ? decodeURIComponent(projectId) : "";
|
|
1435
1445
|
const effectiveProjectId = getEffectiveProjectId(decodedProjectId);
|
|
1436
|
-
const encodedPath =
|
|
1446
|
+
const encodedPath = encodeGitLabPathSegment(filePath);
|
|
1437
1447
|
// Fall back to default branch if ref is not provided
|
|
1438
1448
|
if (!ref) {
|
|
1439
1449
|
ref = await getDefaultBranchRef(decodedProjectId);
|
|
1440
1450
|
}
|
|
1441
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
1451
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/repository/files/${encodedPath}`);
|
|
1442
1452
|
url.searchParams.append("ref", ref);
|
|
1443
1453
|
const response = await fetch(url.toString(), {
|
|
1444
1454
|
...getFetchConfig(),
|
|
@@ -1464,7 +1474,7 @@ async function getFileContents(projectId, filePath, ref) {
|
|
|
1464
1474
|
async function createIssue(projectId, options) {
|
|
1465
1475
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1466
1476
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
1467
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
1477
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/issues`);
|
|
1468
1478
|
// Build request body, converting labels array to comma-separated string
|
|
1469
1479
|
const body = { ...options };
|
|
1470
1480
|
if (body.labels && Array.isArray(body.labels)) {
|
|
@@ -1499,30 +1509,33 @@ async function listIssues(projectId, options = {}) {
|
|
|
1499
1509
|
// resolve the default project (or fail for a multi-project allowlist)
|
|
1500
1510
|
const decodedProjectId = projectId ? decodeURIComponent(projectId) : "";
|
|
1501
1511
|
const effectiveProjectId = getEffectiveProjectId(decodedProjectId);
|
|
1502
|
-
url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
1512
|
+
url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/issues`);
|
|
1503
1513
|
}
|
|
1504
1514
|
else {
|
|
1505
1515
|
url = new URL(`${getEffectiveApiUrl()}/issues`);
|
|
1506
1516
|
}
|
|
1507
1517
|
// Add all query parameters
|
|
1508
1518
|
Object.entries(options).forEach(([key, value]) => {
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
url.searchParams.append(`${key}[]`,
|
|
1520
|
-
}
|
|
1519
|
+
// Drop blank entries inside arrays so ["", "bug"] behaves like the scalar guard below
|
|
1520
|
+
const normalized = dropBlankArrayEntries(value);
|
|
1521
|
+
if (isBlankFilterValue(normalized)) {
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1524
|
+
const keys = ["labels", "assignee_username"];
|
|
1525
|
+
if (keys.includes(key)) {
|
|
1526
|
+
if (Array.isArray(normalized)) {
|
|
1527
|
+
// Handle array of labels
|
|
1528
|
+
normalized.forEach(label => {
|
|
1529
|
+
url.searchParams.append(`${key}[]`, String(label));
|
|
1530
|
+
});
|
|
1521
1531
|
}
|
|
1522
1532
|
else {
|
|
1523
|
-
url.searchParams.append(key
|
|
1533
|
+
url.searchParams.append(`${key}[]`, String(normalized));
|
|
1524
1534
|
}
|
|
1525
1535
|
}
|
|
1536
|
+
else {
|
|
1537
|
+
url.searchParams.append(key, String(normalized));
|
|
1538
|
+
}
|
|
1526
1539
|
});
|
|
1527
1540
|
const response = await fetch(url.toString(), {
|
|
1528
1541
|
...getFetchConfig(),
|
|
@@ -1533,11 +1546,7 @@ async function listIssues(projectId, options = {}) {
|
|
|
1533
1546
|
}
|
|
1534
1547
|
async function listTodos(options = {}) {
|
|
1535
1548
|
const url = new URL(`${getEffectiveApiUrl()}/todos`);
|
|
1536
|
-
|
|
1537
|
-
if (value !== undefined) {
|
|
1538
|
-
url.searchParams.append(key, String(value));
|
|
1539
|
-
}
|
|
1540
|
-
});
|
|
1549
|
+
appendFilterParams(url.searchParams, options);
|
|
1541
1550
|
const response = await fetch(url.toString(), {
|
|
1542
1551
|
...getFetchConfig(),
|
|
1543
1552
|
});
|
|
@@ -1566,20 +1575,22 @@ async function markAllTodosDone() {
|
|
|
1566
1575
|
*/
|
|
1567
1576
|
function appendMergeRequestFilters(url, options) {
|
|
1568
1577
|
Object.entries(options).forEach(([key, value]) => {
|
|
1569
|
-
|
|
1578
|
+
// Drop blank entries inside arrays so ["", "bug"] behaves like the scalar guard below
|
|
1579
|
+
const normalized = dropBlankArrayEntries(value);
|
|
1580
|
+
if (isBlankFilterValue(normalized)) {
|
|
1570
1581
|
return;
|
|
1571
1582
|
}
|
|
1572
|
-
if (key === "labels" && Array.isArray(
|
|
1573
|
-
url.searchParams.append(key,
|
|
1583
|
+
if (key === "labels" && Array.isArray(normalized)) {
|
|
1584
|
+
url.searchParams.append(key, normalized.join(","));
|
|
1574
1585
|
}
|
|
1575
|
-
else if (key === "approved_by_usernames" && Array.isArray(
|
|
1586
|
+
else if (key === "approved_by_usernames" && Array.isArray(normalized)) {
|
|
1576
1587
|
// GitLab expects array-bracket form: approved_by_usernames[]=alice&approved_by_usernames[]=bob
|
|
1577
|
-
for (const v of
|
|
1588
|
+
for (const v of normalized) {
|
|
1578
1589
|
url.searchParams.append(`${key}[]`, String(v));
|
|
1579
1590
|
}
|
|
1580
1591
|
}
|
|
1581
1592
|
else {
|
|
1582
|
-
url.searchParams.append(key, String(
|
|
1593
|
+
url.searchParams.append(key, String(normalized));
|
|
1583
1594
|
}
|
|
1584
1595
|
});
|
|
1585
1596
|
}
|
|
@@ -1599,7 +1610,7 @@ async function listMergeRequests(projectId, options = {}) {
|
|
|
1599
1610
|
// In strict scope, never fall back to the instance-wide endpoint;
|
|
1600
1611
|
// resolve the default project (or fail for a multi-project allowlist)
|
|
1601
1612
|
const effectiveProjectId = getEffectiveProjectId(decodedProjectId);
|
|
1602
|
-
url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
1613
|
+
url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/merge_requests`);
|
|
1603
1614
|
}
|
|
1604
1615
|
else {
|
|
1605
1616
|
url = new URL(`${getEffectiveApiUrl()}/merge_requests`);
|
|
@@ -1621,7 +1632,7 @@ async function listMergeRequests(projectId, options = {}) {
|
|
|
1621
1632
|
*/
|
|
1622
1633
|
async function listGroupMergeRequests(groupId, options = {}) {
|
|
1623
1634
|
const decodedGroupId = decodeURIComponent(groupId);
|
|
1624
|
-
const url = new URL(`${getEffectiveApiUrl()}/groups/${
|
|
1635
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(decodedGroupId)}/merge_requests`);
|
|
1625
1636
|
appendMergeRequestFilters(url, options);
|
|
1626
1637
|
const response = await fetch(url.toString(), {
|
|
1627
1638
|
...getFetchConfig(),
|
|
@@ -1640,7 +1651,7 @@ async function listGroupMergeRequests(groupId, options = {}) {
|
|
|
1640
1651
|
*/
|
|
1641
1652
|
async function getIssue(projectId, issueIid) {
|
|
1642
1653
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1643
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
1654
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/issues/${encodeGitLabPathSegment(issueIid)}`);
|
|
1644
1655
|
const response = await fetch(url.toString(), {
|
|
1645
1656
|
...getFetchConfig(),
|
|
1646
1657
|
});
|
|
@@ -1659,7 +1670,7 @@ async function getIssue(projectId, issueIid) {
|
|
|
1659
1670
|
*/
|
|
1660
1671
|
async function updateIssue(projectId, issueIid, options) {
|
|
1661
1672
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1662
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
1673
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/issues/${encodeGitLabPathSegment(issueIid)}`);
|
|
1663
1674
|
// Convert labels array to comma-separated string if present
|
|
1664
1675
|
const body = { ...options };
|
|
1665
1676
|
if (body.labels && Array.isArray(body.labels)) {
|
|
@@ -1684,7 +1695,7 @@ async function updateIssue(projectId, issueIid, options) {
|
|
|
1684
1695
|
*/
|
|
1685
1696
|
async function deleteIssue(projectId, issueIid) {
|
|
1686
1697
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1687
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
1698
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/issues/${encodeGitLabPathSegment(issueIid)}`);
|
|
1688
1699
|
const response = await fetch(url.toString(), {
|
|
1689
1700
|
...getFetchConfig(),
|
|
1690
1701
|
method: "DELETE",
|
|
@@ -2297,7 +2308,7 @@ async function resolveProjectOrGroupPath(projectId) {
|
|
|
2297
2308
|
}
|
|
2298
2309
|
const effectiveProjectId = getEffectiveProjectId(requestedProjectId);
|
|
2299
2310
|
if (explicitKind === "group") {
|
|
2300
|
-
const groupUrl = new URL(`${getEffectiveApiUrl()}/groups/${
|
|
2311
|
+
const groupUrl = new URL(`${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(effectiveProjectId)}`);
|
|
2301
2312
|
const groupResponse = await fetch(groupUrl.toString(), {
|
|
2302
2313
|
...getFetchConfig(),
|
|
2303
2314
|
});
|
|
@@ -2305,7 +2316,7 @@ async function resolveProjectOrGroupPath(projectId) {
|
|
|
2305
2316
|
const group = await groupResponse.json();
|
|
2306
2317
|
return { path: group.full_path, kind: "group" };
|
|
2307
2318
|
}
|
|
2308
|
-
const projectUrl = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
2319
|
+
const projectUrl = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}`);
|
|
2309
2320
|
const projectResponse = await fetch(projectUrl.toString(), {
|
|
2310
2321
|
...getFetchConfig(),
|
|
2311
2322
|
});
|
|
@@ -2317,7 +2328,7 @@ async function resolveProjectOrGroupPath(projectId) {
|
|
|
2317
2328
|
!explicitKind &&
|
|
2318
2329
|
getEffectiveAllowedProjectIds().length === 0 &&
|
|
2319
2330
|
!/^\d+$/.test(effectiveProjectId)) {
|
|
2320
|
-
const groupUrl = new URL(`${getEffectiveApiUrl()}/groups/${
|
|
2331
|
+
const groupUrl = new URL(`${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(effectiveProjectId)}`);
|
|
2321
2332
|
const groupResponse = await fetch(groupUrl.toString(), {
|
|
2322
2333
|
...getFetchConfig(),
|
|
2323
2334
|
});
|
|
@@ -3058,7 +3069,7 @@ async function updateWorkItem(projectId, iid, options) {
|
|
|
3058
3069
|
*/
|
|
3059
3070
|
async function listIssueLinks(projectId, issueIid) {
|
|
3060
3071
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3061
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3072
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/issues/${encodeGitLabPathSegment(issueIid)}/links`);
|
|
3062
3073
|
const response = await fetch(url.toString(), {
|
|
3063
3074
|
...getFetchConfig(),
|
|
3064
3075
|
});
|
|
@@ -3077,7 +3088,7 @@ async function listIssueLinks(projectId, issueIid) {
|
|
|
3077
3088
|
*/
|
|
3078
3089
|
async function getIssueLink(projectId, issueIid, issueLinkId) {
|
|
3079
3090
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3080
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3091
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/issues/${encodeGitLabPathSegment(issueIid)}/links/${encodeGitLabPathSegment(issueLinkId)}`);
|
|
3081
3092
|
const response = await fetch(url.toString(), {
|
|
3082
3093
|
...getFetchConfig(),
|
|
3083
3094
|
});
|
|
@@ -3099,7 +3110,7 @@ async function getIssueLink(projectId, issueIid, issueLinkId) {
|
|
|
3099
3110
|
async function createIssueLink(projectId, issueIid, targetProjectId, targetIssueIid, linkType = "relates_to") {
|
|
3100
3111
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3101
3112
|
targetProjectId = decodeURIComponent(targetProjectId); // Decode target project ID as well
|
|
3102
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3113
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/issues/${encodeGitLabPathSegment(issueIid)}/links`);
|
|
3103
3114
|
const response = await fetch(url.toString(), {
|
|
3104
3115
|
...getFetchConfig(),
|
|
3105
3116
|
method: "POST",
|
|
@@ -3124,7 +3135,7 @@ async function createIssueLink(projectId, issueIid, targetProjectId, targetIssue
|
|
|
3124
3135
|
*/
|
|
3125
3136
|
async function deleteIssueLink(projectId, issueIid, issueLinkId) {
|
|
3126
3137
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3127
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3138
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/issues/${encodeGitLabPathSegment(issueIid)}/links/${encodeGitLabPathSegment(issueLinkId)}`);
|
|
3128
3139
|
const response = await fetch(url.toString(), {
|
|
3129
3140
|
...getFetchConfig(),
|
|
3130
3141
|
method: "DELETE",
|
|
@@ -3141,7 +3152,7 @@ async function deleteIssueLink(projectId, issueIid, issueLinkId) {
|
|
|
3141
3152
|
*/
|
|
3142
3153
|
async function createMergeRequest(projectId, options) {
|
|
3143
3154
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3144
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3155
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests`);
|
|
3145
3156
|
const response = await fetch(url.toString(), {
|
|
3146
3157
|
...getFetchConfig(),
|
|
3147
3158
|
method: "POST",
|
|
@@ -3183,7 +3194,7 @@ async function createMergeRequest(projectId, options) {
|
|
|
3183
3194
|
*/
|
|
3184
3195
|
async function listDiscussions(projectId, resourceType, resourceIid, options = {}) {
|
|
3185
3196
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3186
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3197
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/${resourceType}/${encodeGitLabPathSegment(resourceIid)}/discussions`);
|
|
3187
3198
|
// Add query parameters for pagination and sorting
|
|
3188
3199
|
if (options.page) {
|
|
3189
3200
|
url.searchParams.append("page", options.page.toString());
|
|
@@ -3247,7 +3258,7 @@ async function listIssueDiscussions(projectId, issueIid, options = {}) {
|
|
|
3247
3258
|
}
|
|
3248
3259
|
async function deleteMergeRequestDiscussionNote(projectId, mergeRequestIid, discussionId, noteId) {
|
|
3249
3260
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3250
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3261
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/discussions/${encodeGitLabPathSegment(discussionId)}/notes/${encodeGitLabPathSegment(noteId)}`);
|
|
3251
3262
|
const response = await fetch(url.toString(), {
|
|
3252
3263
|
...getFetchConfig(),
|
|
3253
3264
|
method: "DELETE",
|
|
@@ -3271,7 +3282,7 @@ async function deleteMergeRequestDiscussionNote(projectId, mergeRequestIid, disc
|
|
|
3271
3282
|
*/
|
|
3272
3283
|
async function updateMergeRequestDiscussionNote(projectId, mergeRequestIid, discussionId, noteId, body, resolved) {
|
|
3273
3284
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3274
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3285
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/discussions/${encodeGitLabPathSegment(discussionId)}/notes/${encodeGitLabPathSegment(noteId)}`);
|
|
3275
3286
|
// Only one of body or resolved can be sent according to GitLab API
|
|
3276
3287
|
const payload = {};
|
|
3277
3288
|
if (body !== undefined) {
|
|
@@ -3317,7 +3328,7 @@ async function updateMergeRequestDiscussionNote(projectId, mergeRequestIid, disc
|
|
|
3317
3328
|
*/
|
|
3318
3329
|
async function updateIssueNote(projectId, issueIid, discussionId, noteId, body, resolved) {
|
|
3319
3330
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3320
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3331
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/issues/${encodeGitLabPathSegment(issueIid)}/discussions/${encodeGitLabPathSegment(discussionId)}/notes/${encodeGitLabPathSegment(noteId)}`);
|
|
3321
3332
|
// Only one of body or resolved can be sent according to GitLab API
|
|
3322
3333
|
const payload = {};
|
|
3323
3334
|
if (body !== undefined) {
|
|
@@ -3346,7 +3357,7 @@ async function updateIssueNote(projectId, issueIid, discussionId, noteId, body,
|
|
|
3346
3357
|
*/
|
|
3347
3358
|
async function createIssueNote(projectId, issueIid, discussionId, body, createdAt) {
|
|
3348
3359
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3349
|
-
const basePath = `${getEffectiveApiUrl()}/projects/${
|
|
3360
|
+
const basePath = `${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/issues/${encodeGitLabPathSegment(issueIid)}`;
|
|
3350
3361
|
const url = new URL(discussionId ? `${basePath}/discussions/${encodeGitLabPathSegment(discussionId)}/notes` : `${basePath}/notes`);
|
|
3351
3362
|
const payload = { body };
|
|
3352
3363
|
if (createdAt) {
|
|
@@ -3374,7 +3385,7 @@ async function createIssueNote(projectId, issueIid, discussionId, body, createdA
|
|
|
3374
3385
|
*/
|
|
3375
3386
|
async function createMergeRequestDiscussionNote(projectId, mergeRequestIid, discussionId, body, createdAt) {
|
|
3376
3387
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3377
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3388
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/discussions/${encodeGitLabPathSegment(discussionId)}/notes`);
|
|
3378
3389
|
const payload = { body };
|
|
3379
3390
|
if (createdAt) {
|
|
3380
3391
|
payload.created_at = createdAt;
|
|
@@ -3390,7 +3401,7 @@ async function createMergeRequestDiscussionNote(projectId, mergeRequestIid, disc
|
|
|
3390
3401
|
}
|
|
3391
3402
|
async function createMergeRequestNote(projectId, mergeRequestIid, body) {
|
|
3392
3403
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3393
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3404
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/notes`);
|
|
3394
3405
|
const payload = {
|
|
3395
3406
|
id: projectId,
|
|
3396
3407
|
merge_request_iid: mergeRequestIid,
|
|
@@ -3407,7 +3418,7 @@ async function createMergeRequestNote(projectId, mergeRequestIid, body) {
|
|
|
3407
3418
|
}
|
|
3408
3419
|
async function deleteMergeRequestNote(projectId, mergeRequestIid, noteId) {
|
|
3409
3420
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3410
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3421
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/notes/${encodeGitLabPathSegment(noteId)}`);
|
|
3411
3422
|
const response = await fetch(url.toString(), {
|
|
3412
3423
|
...getFetchConfig(),
|
|
3413
3424
|
method: "DELETE",
|
|
@@ -3420,7 +3431,7 @@ async function deleteMergeRequestNote(projectId, mergeRequestIid, noteId) {
|
|
|
3420
3431
|
// --- Emoji Reactions (REST) ---
|
|
3421
3432
|
function buildAwardEmojiPath(entity, projectId, entityIid, opts) {
|
|
3422
3433
|
projectId = decodeURIComponent(projectId);
|
|
3423
|
-
const pp =
|
|
3434
|
+
const pp = encodeGitLabPathSegment(getEffectiveProjectId(projectId));
|
|
3424
3435
|
let path = `${getEffectiveApiUrl()}/projects/${pp}/${entity}/${encodeGitLabPathSegment(entityIid)}`;
|
|
3425
3436
|
if (opts?.noteId) {
|
|
3426
3437
|
path = opts.discussionId
|
|
@@ -3455,7 +3466,7 @@ async function deleteRestAwardEmoji(path) {
|
|
|
3455
3466
|
}
|
|
3456
3467
|
async function getMergeRequestNote(projectId, mergeRequestIid, noteId) {
|
|
3457
3468
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3458
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3469
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/notes/${encodeGitLabPathSegment(noteId)}`);
|
|
3459
3470
|
const response = await fetch(url.toString(), {
|
|
3460
3471
|
...getFetchConfig(),
|
|
3461
3472
|
method: "GET",
|
|
@@ -3466,7 +3477,7 @@ async function getMergeRequestNote(projectId, mergeRequestIid, noteId) {
|
|
|
3466
3477
|
}
|
|
3467
3478
|
async function getMergeRequestDiscussion(projectId, mergeRequestIid, discussionId) {
|
|
3468
3479
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3469
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3480
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/discussions/${encodeGitLabPathSegment(discussionId)}`);
|
|
3470
3481
|
const response = await fetch(url.toString(), {
|
|
3471
3482
|
...getFetchConfig(),
|
|
3472
3483
|
method: "GET",
|
|
@@ -3477,7 +3488,7 @@ async function getMergeRequestDiscussion(projectId, mergeRequestIid, discussionI
|
|
|
3477
3488
|
}
|
|
3478
3489
|
async function getMergeRequestNotes(projectId, mergeRequestIid, sort, order_by, per_page, page) {
|
|
3479
3490
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3480
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3491
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/notes`);
|
|
3481
3492
|
if (sort) {
|
|
3482
3493
|
url.searchParams.append("sort", sort);
|
|
3483
3494
|
}
|
|
@@ -3500,7 +3511,7 @@ async function getMergeRequestNotes(projectId, mergeRequestIid, sort, order_by,
|
|
|
3500
3511
|
}
|
|
3501
3512
|
async function updateMergeRequestNote(projectId, mergeRequestIid, noteId, body) {
|
|
3502
3513
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3503
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3514
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/notes/${encodeGitLabPathSegment(noteId)}`);
|
|
3504
3515
|
const payload = {
|
|
3505
3516
|
id: projectId,
|
|
3506
3517
|
merge_request_iid: mergeRequestIid,
|
|
@@ -3530,8 +3541,8 @@ async function updateMergeRequestNote(projectId, mergeRequestIid, noteId, body)
|
|
|
3530
3541
|
*/
|
|
3531
3542
|
async function createOrUpdateFile(projectId, filePath, content, commitMessage, branch, previousPath, last_commit_id, commit_id, encoding) {
|
|
3532
3543
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3533
|
-
const encodedPath =
|
|
3534
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3544
|
+
const encodedPath = encodeGitLabPathSegment(filePath);
|
|
3545
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/repository/files/${encodedPath}`);
|
|
3535
3546
|
const resolvedEncoding = encoding ?? GITLAB_REPO_FILE_ENCODING;
|
|
3536
3547
|
const body = {
|
|
3537
3548
|
branch,
|
|
@@ -3602,7 +3613,7 @@ async function createOrUpdateFile(projectId, filePath, content, commitMessage, b
|
|
|
3602
3613
|
*/
|
|
3603
3614
|
async function createCommit(projectId, message, branch, actions) {
|
|
3604
3615
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3605
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3616
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/repository/commits`);
|
|
3606
3617
|
const response = await fetch(url.toString(), {
|
|
3607
3618
|
...getFetchConfig(),
|
|
3608
3619
|
method: "POST",
|
|
@@ -3671,7 +3682,7 @@ async function searchBlobs(params) {
|
|
|
3671
3682
|
let basePath;
|
|
3672
3683
|
if (params.project_id) {
|
|
3673
3684
|
const decodedProjectId = decodeURIComponent(params.project_id);
|
|
3674
|
-
const projectId =
|
|
3685
|
+
const projectId = encodeGitLabPathSegment(getEffectiveProjectId(decodedProjectId));
|
|
3675
3686
|
basePath = `${getEffectiveApiUrl()}/projects/${projectId}/search`;
|
|
3676
3687
|
}
|
|
3677
3688
|
else if (params.group_id) {
|
|
@@ -3749,11 +3760,11 @@ async function getMergeRequest(projectId, mergeRequestIid, branchName) {
|
|
|
3749
3760
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3750
3761
|
let url;
|
|
3751
3762
|
if (mergeRequestIid) {
|
|
3752
|
-
url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3763
|
+
url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}`);
|
|
3753
3764
|
url.searchParams.append("include_diverged_commits_count", "true");
|
|
3754
3765
|
}
|
|
3755
3766
|
else if (branchName) {
|
|
3756
|
-
url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3767
|
+
url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests?source_branch=${encodeURIComponent(branchName)}`);
|
|
3757
3768
|
}
|
|
3758
3769
|
else {
|
|
3759
3770
|
throw new Error("Either mergeRequestIid or branchName must be provided");
|
|
@@ -3771,7 +3782,7 @@ async function getMergeRequest(projectId, mergeRequestIid, branchName) {
|
|
|
3771
3782
|
return GitLabMergeRequestSchema.parse(data);
|
|
3772
3783
|
}
|
|
3773
3784
|
async function getMergeRequestSourceCommitCount(projectId, mergeRequestIid) {
|
|
3774
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3785
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/commits`);
|
|
3775
3786
|
url.searchParams.append("per_page", "100");
|
|
3776
3787
|
let totalCount = 0;
|
|
3777
3788
|
let page = 1;
|
|
@@ -3799,12 +3810,8 @@ async function getMergeRequestSourceCommitCount(projectId, mergeRequestIid) {
|
|
|
3799
3810
|
}
|
|
3800
3811
|
async function listMergeRequestPipelines(projectId, mergeRequestIid, options = {}) {
|
|
3801
3812
|
projectId = decodeURIComponent(projectId);
|
|
3802
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3803
|
-
|
|
3804
|
-
if (value !== undefined) {
|
|
3805
|
-
url.searchParams.append(key, value.toString());
|
|
3806
|
-
}
|
|
3807
|
-
});
|
|
3813
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/pipelines`);
|
|
3814
|
+
appendFilterParams(url.searchParams, options);
|
|
3808
3815
|
const response = await fetch(url.toString(), {
|
|
3809
3816
|
...getFetchConfig(),
|
|
3810
3817
|
});
|
|
@@ -3813,7 +3820,7 @@ async function listMergeRequestPipelines(projectId, mergeRequestIid, options = {
|
|
|
3813
3820
|
return z.array(GitLabMergeRequestPipelineSchema).parse(data);
|
|
3814
3821
|
}
|
|
3815
3822
|
async function getProjectMergeMethod(projectId) {
|
|
3816
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
3823
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}`);
|
|
3817
3824
|
const response = await fetch(url.toString(), {
|
|
3818
3825
|
...getFetchConfig(),
|
|
3819
3826
|
});
|
|
@@ -4000,7 +4007,7 @@ async function getMergeRequestDiffs(projectId, mergeRequestIid, branchName, view
|
|
|
4000
4007
|
const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
|
|
4001
4008
|
mergeRequestIid = mergeRequest.iid;
|
|
4002
4009
|
}
|
|
4003
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4010
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/changes`);
|
|
4004
4011
|
if (view) {
|
|
4005
4012
|
url.searchParams.append("view", view);
|
|
4006
4013
|
}
|
|
@@ -4030,7 +4037,7 @@ async function listMergeRequestDiffs(projectId, mergeRequestIid, branchName, pag
|
|
|
4030
4037
|
const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
|
|
4031
4038
|
mergeRequestIid = mergeRequest.iid;
|
|
4032
4039
|
}
|
|
4033
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4040
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/diffs`);
|
|
4034
4041
|
if (page) {
|
|
4035
4042
|
url.searchParams.append("page", page.toString());
|
|
4036
4043
|
}
|
|
@@ -4066,7 +4073,7 @@ async function listMergeRequestChangedFiles(projectId, mergeRequestIid, branchNa
|
|
|
4066
4073
|
const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
|
|
4067
4074
|
mergeRequestIid = mergeRequest.iid;
|
|
4068
4075
|
}
|
|
4069
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4076
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/changes`);
|
|
4070
4077
|
const response = await fetch(url.toString(), { ...getFetchConfig() });
|
|
4071
4078
|
await handleGitLabError(response);
|
|
4072
4079
|
const data = (await response.json());
|
|
@@ -4107,7 +4114,7 @@ async function getMergeRequestFileDiff(projectId, filePaths, mergeRequestIid, br
|
|
|
4107
4114
|
let page = 1;
|
|
4108
4115
|
const perPage = 20;
|
|
4109
4116
|
while (remaining.size > 0) {
|
|
4110
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4117
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/diffs`);
|
|
4111
4118
|
url.searchParams.append("page", page.toString());
|
|
4112
4119
|
url.searchParams.append("per_page", perPage.toString());
|
|
4113
4120
|
if (unidiff) {
|
|
@@ -4150,7 +4157,7 @@ async function getMergeRequestFileDiff(projectId, filePaths, mergeRequestIid, br
|
|
|
4150
4157
|
*/
|
|
4151
4158
|
async function getBranchDiffs(projectId, from, to, straight) {
|
|
4152
4159
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
4153
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4160
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/repository/compare`);
|
|
4154
4161
|
url.searchParams.append("from", from);
|
|
4155
4162
|
url.searchParams.append("to", to);
|
|
4156
4163
|
if (straight !== undefined) {
|
|
@@ -4185,7 +4192,7 @@ async function updateMergeRequest(projectId, options, mergeRequestIid, branchNam
|
|
|
4185
4192
|
const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
|
|
4186
4193
|
mergeRequestIid = mergeRequest.iid;
|
|
4187
4194
|
}
|
|
4188
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4195
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}`);
|
|
4189
4196
|
const response = await fetch(url.toString(), {
|
|
4190
4197
|
...getFetchConfig(),
|
|
4191
4198
|
method: "PUT",
|
|
@@ -4205,7 +4212,7 @@ async function updateMergeRequest(projectId, options, mergeRequestIid, branchNam
|
|
|
4205
4212
|
*/
|
|
4206
4213
|
async function mergeMergeRequest(projectId, options, mergeRequestIid) {
|
|
4207
4214
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
4208
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4215
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/merge`);
|
|
4209
4216
|
const response = await fetch(url.toString(), {
|
|
4210
4217
|
...getFetchConfig(),
|
|
4211
4218
|
method: "PUT",
|
|
@@ -4225,7 +4232,7 @@ async function mergeMergeRequest(projectId, options, mergeRequestIid) {
|
|
|
4225
4232
|
*/
|
|
4226
4233
|
async function approveMergeRequest(projectId, mergeRequestIid, sha, approvalPassword) {
|
|
4227
4234
|
projectId = decodeURIComponent(projectId);
|
|
4228
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4235
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/approve`);
|
|
4229
4236
|
const body = {};
|
|
4230
4237
|
if (sha) {
|
|
4231
4238
|
body.sha = sha;
|
|
@@ -4250,7 +4257,7 @@ async function approveMergeRequest(projectId, mergeRequestIid, sha, approvalPass
|
|
|
4250
4257
|
*/
|
|
4251
4258
|
async function unapproveMergeRequest(projectId, mergeRequestIid) {
|
|
4252
4259
|
projectId = decodeURIComponent(projectId);
|
|
4253
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4260
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/unapprove`);
|
|
4254
4261
|
const response = await fetch(url.toString(), {
|
|
4255
4262
|
...getFetchConfig(),
|
|
4256
4263
|
method: "POST",
|
|
@@ -4268,7 +4275,7 @@ async function unapproveMergeRequest(projectId, mergeRequestIid) {
|
|
|
4268
4275
|
*/
|
|
4269
4276
|
async function getMergeRequestApprovalState(projectId, mergeRequestIid) {
|
|
4270
4277
|
projectId = decodeURIComponent(projectId);
|
|
4271
|
-
const approvalStateUrl = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4278
|
+
const approvalStateUrl = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/approval_state`);
|
|
4272
4279
|
const approvalStateResponse = await fetch(approvalStateUrl.toString(), {
|
|
4273
4280
|
...getFetchConfig(),
|
|
4274
4281
|
method: "GET",
|
|
@@ -4316,7 +4323,7 @@ async function getMergeRequestApprovalState(projectId, mergeRequestIid) {
|
|
|
4316
4323
|
*/
|
|
4317
4324
|
async function getMergeRequestConflicts(projectId, mergeRequestIid) {
|
|
4318
4325
|
projectId = decodeURIComponent(projectId);
|
|
4319
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4326
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/conflicts`);
|
|
4320
4327
|
const response = await fetch(url.toString(), {
|
|
4321
4328
|
...getFetchConfig(),
|
|
4322
4329
|
method: "GET",
|
|
@@ -4325,7 +4332,7 @@ async function getMergeRequestConflicts(projectId, mergeRequestIid) {
|
|
|
4325
4332
|
return (await response.json());
|
|
4326
4333
|
}
|
|
4327
4334
|
async function getMergeRequestApprovalsFallback(projectId, mergeRequestIid) {
|
|
4328
|
-
const approvalsUrl = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4335
|
+
const approvalsUrl = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/approvals`);
|
|
4329
4336
|
const approvalsResponse = await fetch(approvalsUrl.toString(), {
|
|
4330
4337
|
...getFetchConfig(),
|
|
4331
4338
|
method: "GET",
|
|
@@ -4385,7 +4392,7 @@ async function createNote(projectId, noteableType, // specifies 'issue' or 'merg
|
|
|
4385
4392
|
noteableIid, body) {
|
|
4386
4393
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
4387
4394
|
// ⚙️ Response type can be adjusted according to the GitLab API documentation
|
|
4388
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4395
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/${noteableType}s/${encodeGitLabPathSegment(noteableIid)}/notes` // Using plural form (issues/merge_requests) as per GitLab API documentation
|
|
4389
4396
|
);
|
|
4390
4397
|
const response = await fetch(url.toString(), {
|
|
4391
4398
|
...getFetchConfig(),
|
|
@@ -4406,7 +4413,7 @@ noteableIid, body) {
|
|
|
4406
4413
|
*/
|
|
4407
4414
|
async function getDraftNote(project_id, merge_request_iid, draft_note_id) {
|
|
4408
4415
|
const effectiveProjectId = getEffectiveProjectId(decodeURIComponent(project_id));
|
|
4409
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
4416
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/merge_requests/${encodeGitLabPathSegment(merge_request_iid)}/draft_notes/${encodeGitLabPathSegment(draft_note_id)}`, { ...getFetchConfig() });
|
|
4410
4417
|
if (!response.ok) {
|
|
4411
4418
|
const errorText = await response.text();
|
|
4412
4419
|
throw new Error(`GitLab API error: ${response.status} ${response.statusText}\n${errorText}`);
|
|
@@ -4416,7 +4423,7 @@ async function getDraftNote(project_id, merge_request_iid, draft_note_id) {
|
|
|
4416
4423
|
}
|
|
4417
4424
|
async function listDraftNotes(projectId, mergeRequestIid) {
|
|
4418
4425
|
projectId = decodeURIComponent(projectId);
|
|
4419
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4426
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/draft_notes`);
|
|
4420
4427
|
const response = await fetch(url.toString(), {
|
|
4421
4428
|
...getFetchConfig(),
|
|
4422
4429
|
method: "GET",
|
|
@@ -4440,7 +4447,7 @@ async function listDraftNotes(projectId, mergeRequestIid) {
|
|
|
4440
4447
|
*/
|
|
4441
4448
|
async function createDraftNote(projectId, mergeRequestIid, body, inReplyToDiscussionId, position, resolveDiscussion) {
|
|
4442
4449
|
projectId = decodeURIComponent(projectId);
|
|
4443
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4450
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/draft_notes`);
|
|
4444
4451
|
const requestBody = { note: body };
|
|
4445
4452
|
if (inReplyToDiscussionId) {
|
|
4446
4453
|
requestBody.in_reply_to_discussion_id = inReplyToDiscussionId;
|
|
@@ -4475,7 +4482,7 @@ async function createDraftNote(projectId, mergeRequestIid, body, inReplyToDiscus
|
|
|
4475
4482
|
*/
|
|
4476
4483
|
async function updateDraftNote(projectId, mergeRequestIid, draftNoteId, body, position, resolveDiscussion) {
|
|
4477
4484
|
projectId = decodeURIComponent(projectId);
|
|
4478
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4485
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/draft_notes/${encodeGitLabPathSegment(draftNoteId)}`);
|
|
4479
4486
|
const requestBody = {};
|
|
4480
4487
|
if (body !== undefined) {
|
|
4481
4488
|
requestBody.note = body;
|
|
@@ -4507,7 +4514,7 @@ async function updateDraftNote(projectId, mergeRequestIid, draftNoteId, body, po
|
|
|
4507
4514
|
*/
|
|
4508
4515
|
async function deleteDraftNote(projectId, mergeRequestIid, draftNoteId) {
|
|
4509
4516
|
projectId = decodeURIComponent(projectId);
|
|
4510
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4517
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/draft_notes/${encodeGitLabPathSegment(draftNoteId)}`);
|
|
4511
4518
|
const response = await fetch(url.toString(), {
|
|
4512
4519
|
...getFetchConfig(),
|
|
4513
4520
|
method: "DELETE",
|
|
@@ -4526,7 +4533,7 @@ async function deleteDraftNote(projectId, mergeRequestIid, draftNoteId) {
|
|
|
4526
4533
|
*/
|
|
4527
4534
|
async function publishDraftNote(projectId, mergeRequestIid, draftNoteId) {
|
|
4528
4535
|
projectId = decodeURIComponent(projectId);
|
|
4529
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4536
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/draft_notes/${encodeGitLabPathSegment(draftNoteId)}/publish`);
|
|
4530
4537
|
const response = await fetch(url.toString(), {
|
|
4531
4538
|
...getFetchConfig(),
|
|
4532
4539
|
method: "PUT",
|
|
@@ -4597,7 +4604,7 @@ async function fetchGitLabInstanceVersion() {
|
|
|
4597
4604
|
*/
|
|
4598
4605
|
async function bulkPublishDraftNotes(projectId, mergeRequestIid, options = {}) {
|
|
4599
4606
|
projectId = decodeURIComponent(projectId);
|
|
4600
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4607
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/draft_notes/bulk_publish`);
|
|
4601
4608
|
const body = buildBulkPublishDraftNotesBody(options);
|
|
4602
4609
|
if (needsGitLab19_2BulkPublish(body)) {
|
|
4603
4610
|
await assertGitLabVersionAtLeast({
|
|
@@ -4635,7 +4642,7 @@ async function bulkPublishDraftNotes(projectId, mergeRequestIid, options = {}) {
|
|
|
4635
4642
|
}
|
|
4636
4643
|
async function resolveMergeRequestThread(projectId, mergeRequestIid, discussionId, resolved) {
|
|
4637
4644
|
projectId = decodeURIComponent(projectId);
|
|
4638
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4645
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/discussions/${encodeGitLabPathSegment(discussionId)}`);
|
|
4639
4646
|
if (resolved !== undefined) {
|
|
4640
4647
|
url.searchParams.append("resolved", resolved ? "true" : "false");
|
|
4641
4648
|
}
|
|
@@ -4667,7 +4674,7 @@ async function resolveMergeRequestThread(projectId, mergeRequestIid, discussionI
|
|
|
4667
4674
|
*/
|
|
4668
4675
|
async function createMergeRequestThread(projectId, mergeRequestIid, body, position, createdAt) {
|
|
4669
4676
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
4670
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4677
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/discussions`);
|
|
4671
4678
|
const payload = { body };
|
|
4672
4679
|
if (position) {
|
|
4673
4680
|
payload.position = position;
|
|
@@ -4694,7 +4701,7 @@ async function createMergeRequestThread(projectId, mergeRequestIid, body, positi
|
|
|
4694
4701
|
*/
|
|
4695
4702
|
async function listMergeRequestVersions(projectId, mergeRequestIid) {
|
|
4696
4703
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
4697
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4704
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/versions`);
|
|
4698
4705
|
const response = await fetch(url.toString(), {
|
|
4699
4706
|
...getFetchConfig(),
|
|
4700
4707
|
});
|
|
@@ -4713,7 +4720,7 @@ async function listMergeRequestVersions(projectId, mergeRequestIid) {
|
|
|
4713
4720
|
*/
|
|
4714
4721
|
async function getMergeRequestVersion(projectId, mergeRequestIid, versionId, unidiff) {
|
|
4715
4722
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
4716
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4723
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/merge_requests/${encodeGitLabPathSegment(mergeRequestIid)}/versions/${encodeGitLabPathSegment(versionId)}`);
|
|
4717
4724
|
if (unidiff !== undefined) {
|
|
4718
4725
|
url.searchParams.append("unidiff", String(unidiff));
|
|
4719
4726
|
}
|
|
@@ -4734,16 +4741,7 @@ async function getMergeRequestVersion(projectId, mergeRequestIid, versionId, uni
|
|
|
4734
4741
|
async function listProjects(options = {}) {
|
|
4735
4742
|
// Construct the query parameters
|
|
4736
4743
|
const params = new URLSearchParams();
|
|
4737
|
-
|
|
4738
|
-
if (value !== undefined && value !== null) {
|
|
4739
|
-
if (typeof value === "boolean") {
|
|
4740
|
-
params.append(key, value ? "true" : "false");
|
|
4741
|
-
}
|
|
4742
|
-
else {
|
|
4743
|
-
params.append(key, String(value));
|
|
4744
|
-
}
|
|
4745
|
-
}
|
|
4746
|
-
}
|
|
4744
|
+
appendFilterParams(params, options);
|
|
4747
4745
|
// Make the API request
|
|
4748
4746
|
const response = await fetch(`${getEffectiveApiUrl()}/projects?${params.toString()}`, {
|
|
4749
4747
|
...getFetchConfig(),
|
|
@@ -4764,18 +4762,9 @@ async function listProjects(options = {}) {
|
|
|
4764
4762
|
async function listLabels(projectId, options = {}) {
|
|
4765
4763
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
4766
4764
|
// Construct the URL with project path
|
|
4767
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4765
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/labels`);
|
|
4768
4766
|
// Add query parameters
|
|
4769
|
-
|
|
4770
|
-
if (value !== undefined) {
|
|
4771
|
-
if (typeof value === "boolean") {
|
|
4772
|
-
url.searchParams.append(key, value ? "true" : "false");
|
|
4773
|
-
}
|
|
4774
|
-
else {
|
|
4775
|
-
url.searchParams.append(key, String(value));
|
|
4776
|
-
}
|
|
4777
|
-
}
|
|
4778
|
-
});
|
|
4767
|
+
appendFilterParams(url.searchParams, options);
|
|
4779
4768
|
// Make the API request
|
|
4780
4769
|
const response = await fetch(url.toString(), {
|
|
4781
4770
|
...getFetchConfig(),
|
|
@@ -4796,7 +4785,7 @@ async function listLabels(projectId, options = {}) {
|
|
|
4796
4785
|
*/
|
|
4797
4786
|
async function getLabel(projectId, labelId, includeAncestorGroups) {
|
|
4798
4787
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
4799
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
4788
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/labels/${encodeGitLabPathSegment(String(labelId))}`);
|
|
4800
4789
|
// Add query parameters
|
|
4801
4790
|
if (includeAncestorGroups !== undefined) {
|
|
4802
4791
|
url.searchParams.append("include_ancestor_groups", includeAncestorGroups ? "true" : "false");
|
|
@@ -4821,7 +4810,7 @@ async function getLabel(projectId, labelId, includeAncestorGroups) {
|
|
|
4821
4810
|
async function createLabel(projectId, options) {
|
|
4822
4811
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
4823
4812
|
// Make the API request
|
|
4824
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
4813
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/labels`, {
|
|
4825
4814
|
...getFetchConfig(),
|
|
4826
4815
|
method: "POST",
|
|
4827
4816
|
body: JSON.stringify(options),
|
|
@@ -4843,7 +4832,7 @@ async function createLabel(projectId, options) {
|
|
|
4843
4832
|
async function updateLabel(projectId, labelId, options) {
|
|
4844
4833
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
4845
4834
|
// Make the API request
|
|
4846
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
4835
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/labels/${encodeGitLabPathSegment(String(labelId))}`, {
|
|
4847
4836
|
...getFetchConfig(),
|
|
4848
4837
|
method: "PUT",
|
|
4849
4838
|
body: JSON.stringify(options),
|
|
@@ -4863,7 +4852,7 @@ async function updateLabel(projectId, labelId, options) {
|
|
|
4863
4852
|
async function deleteLabel(projectId, labelId) {
|
|
4864
4853
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
4865
4854
|
// Make the API request
|
|
4866
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
4855
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/labels/${encodeGitLabPathSegment(String(labelId))}`, {
|
|
4867
4856
|
...getFetchConfig(),
|
|
4868
4857
|
method: "DELETE",
|
|
4869
4858
|
});
|
|
@@ -4877,7 +4866,7 @@ async function deleteLabel(projectId, labelId) {
|
|
|
4877
4866
|
* @returns {Promise<GitLabProject[]>} Array of projects in the group
|
|
4878
4867
|
*/
|
|
4879
4868
|
async function listGroupProjects(options) {
|
|
4880
|
-
const url = new URL(`${getEffectiveApiUrl()}/groups/${
|
|
4869
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(options.group_id)}/projects`);
|
|
4881
4870
|
// Add optional parameters to URL
|
|
4882
4871
|
if (options.include_subgroups)
|
|
4883
4872
|
url.searchParams.append("include_subgroups", "true");
|
|
@@ -4927,11 +4916,11 @@ async function listGroupProjects(options) {
|
|
|
4927
4916
|
function buildWebhookBaseUrl(projectId, groupId) {
|
|
4928
4917
|
if (projectId) {
|
|
4929
4918
|
projectId = decodeURIComponent(projectId);
|
|
4930
|
-
return `${getEffectiveApiUrl()}/projects/${
|
|
4919
|
+
return `${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/hooks`;
|
|
4931
4920
|
}
|
|
4932
4921
|
rejectIfStrictProjectScope("group webhooks");
|
|
4933
4922
|
const decodedGroupId = decodeURIComponent(groupId);
|
|
4934
|
-
return `${getEffectiveApiUrl()}/groups/${
|
|
4923
|
+
return `${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(decodedGroupId)}/hooks`;
|
|
4935
4924
|
}
|
|
4936
4925
|
/**
|
|
4937
4926
|
* List webhooks for a project or group
|
|
@@ -5046,7 +5035,7 @@ async function getWebhookEvent(options) {
|
|
|
5046
5035
|
*/
|
|
5047
5036
|
async function listWikiPages(projectId, options = {}) {
|
|
5048
5037
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5049
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5038
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/wikis`);
|
|
5050
5039
|
if (options.page)
|
|
5051
5040
|
url.searchParams.append("page", options.page.toString());
|
|
5052
5041
|
if (options.per_page)
|
|
@@ -5067,7 +5056,7 @@ async function listWikiPages(projectId, options = {}) {
|
|
|
5067
5056
|
*/
|
|
5068
5057
|
async function getWikiPage(projectId, slug, renderHtml) {
|
|
5069
5058
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5070
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5059
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/wikis/${encodeGitLabPathSegment(slug)}`);
|
|
5071
5060
|
if (renderHtml)
|
|
5072
5061
|
url.searchParams.append("render_html", renderHtml.toString());
|
|
5073
5062
|
const response = await fetch(url.toString(), { ...getFetchConfig() });
|
|
@@ -5083,7 +5072,7 @@ async function createWikiPage(projectId, title, content, format) {
|
|
|
5083
5072
|
const body = { title, content };
|
|
5084
5073
|
if (format)
|
|
5085
5074
|
body.format = format;
|
|
5086
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
5075
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/wikis`, {
|
|
5087
5076
|
...getFetchConfig(),
|
|
5088
5077
|
method: "POST",
|
|
5089
5078
|
body: JSON.stringify(body),
|
|
@@ -5111,7 +5100,7 @@ async function updateWikiPage(projectId, slug, title, content, format) {
|
|
|
5111
5100
|
body.content = content;
|
|
5112
5101
|
if (format)
|
|
5113
5102
|
body.format = format;
|
|
5114
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
5103
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/wikis/${encodeGitLabPathSegment(slug)}`, {
|
|
5115
5104
|
...getFetchConfig(),
|
|
5116
5105
|
method: "PUT",
|
|
5117
5106
|
body: JSON.stringify(body),
|
|
@@ -5125,7 +5114,7 @@ async function updateWikiPage(projectId, slug, title, content, format) {
|
|
|
5125
5114
|
*/
|
|
5126
5115
|
async function deleteWikiPage(projectId, slug) {
|
|
5127
5116
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5128
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
5117
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/wikis/${encodeGitLabPathSegment(slug)}`, {
|
|
5129
5118
|
...getFetchConfig(),
|
|
5130
5119
|
method: "DELETE",
|
|
5131
5120
|
});
|
|
@@ -5136,7 +5125,7 @@ async function deleteWikiPage(projectId, slug) {
|
|
|
5136
5125
|
*/
|
|
5137
5126
|
async function listGroupWikiPages(groupId, options = {}) {
|
|
5138
5127
|
groupId = decodeURIComponent(groupId); // Decode group ID
|
|
5139
|
-
const url = new URL(`${getEffectiveApiUrl()}/groups/${
|
|
5128
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(groupId)}/wikis`);
|
|
5140
5129
|
if (options.page)
|
|
5141
5130
|
url.searchParams.append("page", options.page.toString());
|
|
5142
5131
|
if (options.per_page)
|
|
@@ -5157,7 +5146,7 @@ async function listGroupWikiPages(groupId, options = {}) {
|
|
|
5157
5146
|
*/
|
|
5158
5147
|
async function getGroupWikiPage(groupId, slug, renderHtml) {
|
|
5159
5148
|
groupId = decodeURIComponent(groupId); // Decode group ID
|
|
5160
|
-
const url = new URL(`${getEffectiveApiUrl()}/groups/${
|
|
5149
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(groupId)}/wikis/${encodeGitLabPathSegment(slug)}`);
|
|
5161
5150
|
if (renderHtml)
|
|
5162
5151
|
url.searchParams.append("render_html", renderHtml.toString());
|
|
5163
5152
|
const response = await fetch(url.toString(), { ...getFetchConfig() });
|
|
@@ -5173,7 +5162,7 @@ async function createGroupWikiPage(groupId, title, content, format) {
|
|
|
5173
5162
|
const body = { title, content };
|
|
5174
5163
|
if (format)
|
|
5175
5164
|
body.format = format;
|
|
5176
|
-
const response = await fetch(`${getEffectiveApiUrl()}/groups/${
|
|
5165
|
+
const response = await fetch(`${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(groupId)}/wikis`, {
|
|
5177
5166
|
...getFetchConfig(),
|
|
5178
5167
|
method: "POST",
|
|
5179
5168
|
body: JSON.stringify(body),
|
|
@@ -5201,7 +5190,7 @@ async function updateGroupWikiPage(groupId, slug, title, content, format) {
|
|
|
5201
5190
|
body.content = content;
|
|
5202
5191
|
if (format)
|
|
5203
5192
|
body.format = format;
|
|
5204
|
-
const response = await fetch(`${getEffectiveApiUrl()}/groups/${
|
|
5193
|
+
const response = await fetch(`${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(groupId)}/wikis/${encodeGitLabPathSegment(slug)}`, {
|
|
5205
5194
|
...getFetchConfig(),
|
|
5206
5195
|
method: "PUT",
|
|
5207
5196
|
body: JSON.stringify(body),
|
|
@@ -5215,7 +5204,7 @@ async function updateGroupWikiPage(groupId, slug, title, content, format) {
|
|
|
5215
5204
|
*/
|
|
5216
5205
|
async function deleteGroupWikiPage(groupId, slug) {
|
|
5217
5206
|
groupId = decodeURIComponent(groupId); // Decode group ID
|
|
5218
|
-
const response = await fetch(`${getEffectiveApiUrl()}/groups/${
|
|
5207
|
+
const response = await fetch(`${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(groupId)}/wikis/${encodeGitLabPathSegment(slug)}`, {
|
|
5219
5208
|
...getFetchConfig(),
|
|
5220
5209
|
method: "DELETE",
|
|
5221
5210
|
});
|
|
@@ -5230,13 +5219,9 @@ async function deleteGroupWikiPage(groupId, slug) {
|
|
|
5230
5219
|
*/
|
|
5231
5220
|
async function listPipelines(projectId, options = {}) {
|
|
5232
5221
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5233
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5222
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipelines`);
|
|
5234
5223
|
// Add all query parameters
|
|
5235
|
-
|
|
5236
|
-
if (value !== undefined) {
|
|
5237
|
-
url.searchParams.append(key, value.toString());
|
|
5238
|
-
}
|
|
5239
|
-
});
|
|
5224
|
+
appendFilterParams(url.searchParams, options);
|
|
5240
5225
|
const response = await fetch(url.toString(), {
|
|
5241
5226
|
...getFetchConfig(),
|
|
5242
5227
|
});
|
|
@@ -5253,7 +5238,7 @@ async function listPipelines(projectId, options = {}) {
|
|
|
5253
5238
|
*/
|
|
5254
5239
|
async function getPipeline(projectId, pipelineId, signal) {
|
|
5255
5240
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5256
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5241
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipelines/${encodeGitLabPathSegment(pipelineId)}`);
|
|
5257
5242
|
const response = await fetch(url.toString(), {
|
|
5258
5243
|
...getFetchConfig(),
|
|
5259
5244
|
signal,
|
|
@@ -5267,7 +5252,7 @@ async function getPipeline(projectId, pipelineId, signal) {
|
|
|
5267
5252
|
}
|
|
5268
5253
|
async function getPipelineVariables(projectId, pipelineId, page, perPage) {
|
|
5269
5254
|
projectId = decodeURIComponent(projectId);
|
|
5270
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5255
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipelines/${encodeGitLabPathSegment(pipelineId)}/variables`);
|
|
5271
5256
|
if (page !== undefined)
|
|
5272
5257
|
url.searchParams.set("page", String(page));
|
|
5273
5258
|
if (perPage !== undefined)
|
|
@@ -5279,7 +5264,7 @@ async function getPipelineVariables(projectId, pipelineId, page, perPage) {
|
|
|
5279
5264
|
async function getPipelineReport(projectId, pipelineId, summary = false, page, perPage) {
|
|
5280
5265
|
projectId = decodeURIComponent(projectId);
|
|
5281
5266
|
const endpoint = summary ? "test_report_summary" : "test_report";
|
|
5282
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5267
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipelines/${encodeGitLabPathSegment(pipelineId)}/${endpoint}`);
|
|
5283
5268
|
if (page !== undefined)
|
|
5284
5269
|
url.searchParams.set("page", String(page));
|
|
5285
5270
|
if (perPage !== undefined)
|
|
@@ -5290,13 +5275,13 @@ async function getPipelineReport(projectId, pipelineId, summary = false, page, p
|
|
|
5290
5275
|
}
|
|
5291
5276
|
async function deletePipeline(projectId, pipelineId) {
|
|
5292
5277
|
projectId = decodeURIComponent(projectId);
|
|
5293
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
5278
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipelines/${encodeGitLabPathSegment(pipelineId)}`, { ...getFetchConfig(), method: "DELETE" });
|
|
5294
5279
|
await handleGitLabError(response);
|
|
5295
5280
|
return response.status === 204 ? { deleted: true } : response.json();
|
|
5296
5281
|
}
|
|
5297
5282
|
async function updatePipelineMetadata(projectId, pipelineId, name) {
|
|
5298
5283
|
projectId = decodeURIComponent(projectId);
|
|
5299
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
5284
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipelines/${encodeGitLabPathSegment(pipelineId)}/metadata`, { ...getFetchConfig(), method: "PUT", body: JSON.stringify({ name }) });
|
|
5300
5285
|
await handleGitLabError(response);
|
|
5301
5286
|
return response.json();
|
|
5302
5287
|
}
|
|
@@ -5309,12 +5294,8 @@ async function updatePipelineMetadata(projectId, pipelineId, name) {
|
|
|
5309
5294
|
*/
|
|
5310
5295
|
async function listDeployments(projectId, options = {}) {
|
|
5311
5296
|
projectId = decodeURIComponent(projectId);
|
|
5312
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5313
|
-
|
|
5314
|
-
if (value !== undefined) {
|
|
5315
|
-
url.searchParams.append(key, value.toString());
|
|
5316
|
-
}
|
|
5317
|
-
});
|
|
5297
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/deployments`);
|
|
5298
|
+
appendFilterParams(url.searchParams, options);
|
|
5318
5299
|
const response = await fetch(url.toString(), {
|
|
5319
5300
|
...getFetchConfig(),
|
|
5320
5301
|
});
|
|
@@ -5331,7 +5312,7 @@ async function listDeployments(projectId, options = {}) {
|
|
|
5331
5312
|
*/
|
|
5332
5313
|
async function getDeployment(projectId, deploymentId) {
|
|
5333
5314
|
projectId = decodeURIComponent(projectId);
|
|
5334
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5315
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/deployments/${encodeGitLabPathSegment(deploymentId)}`);
|
|
5335
5316
|
const response = await fetch(url.toString(), {
|
|
5336
5317
|
...getFetchConfig(),
|
|
5337
5318
|
});
|
|
@@ -5344,7 +5325,7 @@ async function getDeployment(projectId, deploymentId) {
|
|
|
5344
5325
|
}
|
|
5345
5326
|
async function deploymentRequest(projectId, path = "", method = "GET", body, query) {
|
|
5346
5327
|
projectId = decodeURIComponent(projectId);
|
|
5347
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5328
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/deployments${path}`);
|
|
5348
5329
|
for (const [key, value] of Object.entries(query ?? {})) {
|
|
5349
5330
|
if (value !== undefined)
|
|
5350
5331
|
url.searchParams.set(key, String(value));
|
|
@@ -5365,12 +5346,8 @@ async function deploymentRequest(projectId, path = "", method = "GET", body, que
|
|
|
5365
5346
|
*/
|
|
5366
5347
|
async function listEnvironments(projectId, options = {}) {
|
|
5367
5348
|
projectId = decodeURIComponent(projectId);
|
|
5368
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5369
|
-
|
|
5370
|
-
if (value !== undefined) {
|
|
5371
|
-
url.searchParams.append(key, value.toString());
|
|
5372
|
-
}
|
|
5373
|
-
});
|
|
5349
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/environments`);
|
|
5350
|
+
appendFilterParams(url.searchParams, options);
|
|
5374
5351
|
const response = await fetch(url.toString(), {
|
|
5375
5352
|
...getFetchConfig(),
|
|
5376
5353
|
});
|
|
@@ -5387,7 +5364,7 @@ async function listEnvironments(projectId, options = {}) {
|
|
|
5387
5364
|
*/
|
|
5388
5365
|
async function getEnvironment(projectId, environmentId) {
|
|
5389
5366
|
projectId = decodeURIComponent(projectId);
|
|
5390
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5367
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/environments/${encodeGitLabPathSegment(environmentId)}`);
|
|
5391
5368
|
const response = await fetch(url.toString(), {
|
|
5392
5369
|
...getFetchConfig(),
|
|
5393
5370
|
});
|
|
@@ -5400,7 +5377,7 @@ async function getEnvironment(projectId, environmentId) {
|
|
|
5400
5377
|
}
|
|
5401
5378
|
async function environmentRequest(projectId, path, method, body, query) {
|
|
5402
5379
|
projectId = decodeURIComponent(projectId);
|
|
5403
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5380
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/environments${path}`);
|
|
5404
5381
|
for (const [key, value] of Object.entries(query ?? {}))
|
|
5405
5382
|
if (value !== undefined)
|
|
5406
5383
|
url.searchParams.set(key, String(value));
|
|
@@ -5410,13 +5387,13 @@ async function environmentRequest(projectId, path, method, body, query) {
|
|
|
5410
5387
|
}
|
|
5411
5388
|
async function pipelineTriggerRequest(projectId, path = "", method = "GET", body) {
|
|
5412
5389
|
projectId = decodeURIComponent(projectId);
|
|
5413
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
5390
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/triggers${path}`, { ...getFetchConfig(), method, body: body === undefined ? undefined : JSON.stringify(body) });
|
|
5414
5391
|
await handleGitLabError(response);
|
|
5415
5392
|
return response.status === 204 ? { success: true } : response.json();
|
|
5416
5393
|
}
|
|
5417
5394
|
async function triggerPipeline(projectId, token, ref, variables, inputs) {
|
|
5418
5395
|
projectId = decodeURIComponent(projectId);
|
|
5419
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5396
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/trigger/pipeline`);
|
|
5420
5397
|
url.searchParams.set("token", token);
|
|
5421
5398
|
url.searchParams.set("ref", ref);
|
|
5422
5399
|
const body = {};
|
|
@@ -5438,18 +5415,9 @@ async function triggerPipeline(projectId, token, ref, variables, inputs) {
|
|
|
5438
5415
|
*/
|
|
5439
5416
|
async function listPipelineJobs(projectId, pipelineId, options = {}) {
|
|
5440
5417
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5441
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5418
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipelines/${encodeGitLabPathSegment(pipelineId)}/jobs`);
|
|
5442
5419
|
// Add all query parameters
|
|
5443
|
-
|
|
5444
|
-
if (value !== undefined) {
|
|
5445
|
-
if (typeof value === "boolean") {
|
|
5446
|
-
url.searchParams.append(key, value ? "true" : "false");
|
|
5447
|
-
}
|
|
5448
|
-
else {
|
|
5449
|
-
url.searchParams.append(key, value.toString());
|
|
5450
|
-
}
|
|
5451
|
-
}
|
|
5452
|
-
});
|
|
5420
|
+
appendFilterParams(url.searchParams, options);
|
|
5453
5421
|
const response = await fetch(url.toString(), {
|
|
5454
5422
|
...getFetchConfig(),
|
|
5455
5423
|
});
|
|
@@ -5470,18 +5438,9 @@ async function listPipelineJobs(projectId, pipelineId, options = {}) {
|
|
|
5470
5438
|
*/
|
|
5471
5439
|
async function listPipelineTriggerJobs(projectId, pipelineId, options = {}) {
|
|
5472
5440
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5473
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5441
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipelines/${encodeGitLabPathSegment(pipelineId)}/bridges`);
|
|
5474
5442
|
// Add all query parameters
|
|
5475
|
-
|
|
5476
|
-
if (value !== undefined) {
|
|
5477
|
-
if (typeof value === "boolean") {
|
|
5478
|
-
url.searchParams.append(key, value ? "true" : "false");
|
|
5479
|
-
}
|
|
5480
|
-
else {
|
|
5481
|
-
url.searchParams.append(key, value.toString());
|
|
5482
|
-
}
|
|
5483
|
-
}
|
|
5484
|
-
});
|
|
5443
|
+
appendFilterParams(url.searchParams, options);
|
|
5485
5444
|
const response = await fetch(url.toString(), {
|
|
5486
5445
|
...getFetchConfig(),
|
|
5487
5446
|
});
|
|
@@ -5494,7 +5453,7 @@ async function listPipelineTriggerJobs(projectId, pipelineId, options = {}) {
|
|
|
5494
5453
|
}
|
|
5495
5454
|
async function getPipelineJob(projectId, jobId, signal) {
|
|
5496
5455
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5497
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5456
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/jobs/${encodeGitLabPathSegment(jobId)}`);
|
|
5498
5457
|
const response = await fetch(url.toString(), {
|
|
5499
5458
|
...getFetchConfig(),
|
|
5500
5459
|
signal,
|
|
@@ -5518,7 +5477,7 @@ async function getPipelineJob(projectId, jobId, signal) {
|
|
|
5518
5477
|
const MAX_JOB_TRACE_LINES = 1000;
|
|
5519
5478
|
async function getPipelineJobOutput(projectId, jobId, limit, offset) {
|
|
5520
5479
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5521
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5480
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/jobs/${encodeGitLabPathSegment(jobId)}/trace`);
|
|
5522
5481
|
const response = await fetch(url.toString(), {
|
|
5523
5482
|
...getFetchConfig(),
|
|
5524
5483
|
headers: {
|
|
@@ -5553,7 +5512,7 @@ async function getPipelineJobOutput(projectId, jobId, limit, offset) {
|
|
|
5553
5512
|
}
|
|
5554
5513
|
async function validateCiLint(projectId, options) {
|
|
5555
5514
|
projectId = decodeURIComponent(projectId);
|
|
5556
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5515
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/ci/lint`);
|
|
5557
5516
|
const response = await fetch(url.toString(), {
|
|
5558
5517
|
...getFetchConfig(),
|
|
5559
5518
|
method: "POST",
|
|
@@ -5565,17 +5524,8 @@ async function validateCiLint(projectId, options) {
|
|
|
5565
5524
|
}
|
|
5566
5525
|
async function validateProjectCiLint(projectId, options) {
|
|
5567
5526
|
projectId = decodeURIComponent(projectId);
|
|
5568
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5569
|
-
|
|
5570
|
-
if (value !== undefined) {
|
|
5571
|
-
if (typeof value === "boolean") {
|
|
5572
|
-
url.searchParams.append(key, value ? "true" : "false");
|
|
5573
|
-
}
|
|
5574
|
-
else {
|
|
5575
|
-
url.searchParams.append(key, value.toString());
|
|
5576
|
-
}
|
|
5577
|
-
}
|
|
5578
|
-
});
|
|
5527
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/ci/lint`);
|
|
5528
|
+
appendFilterParams(url.searchParams, options);
|
|
5579
5529
|
const response = await fetch(url.toString(), {
|
|
5580
5530
|
...getFetchConfig(),
|
|
5581
5531
|
});
|
|
@@ -5593,17 +5543,8 @@ async function validateProjectCiLint(projectId, options) {
|
|
|
5593
5543
|
*/
|
|
5594
5544
|
async function listJobArtifacts(projectId, jobId, options = {}) {
|
|
5595
5545
|
projectId = decodeURIComponent(projectId);
|
|
5596
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5597
|
-
|
|
5598
|
-
if (value !== undefined) {
|
|
5599
|
-
if (typeof value === "boolean") {
|
|
5600
|
-
url.searchParams.append(key, value ? "true" : "false");
|
|
5601
|
-
}
|
|
5602
|
-
else {
|
|
5603
|
-
url.searchParams.append(key, value.toString());
|
|
5604
|
-
}
|
|
5605
|
-
}
|
|
5606
|
-
});
|
|
5546
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/jobs/${encodeGitLabPathSegment(jobId)}/artifacts/tree`);
|
|
5547
|
+
appendFilterParams(url.searchParams, options);
|
|
5607
5548
|
const response = await fetch(url.toString(), {
|
|
5608
5549
|
...getFetchConfig(),
|
|
5609
5550
|
});
|
|
@@ -5625,10 +5566,8 @@ async function listJobArtifacts(projectId, jobId, options = {}) {
|
|
|
5625
5566
|
async function downloadJobArtifacts(projectId, jobId, localPath) {
|
|
5626
5567
|
projectId = decodeURIComponent(projectId);
|
|
5627
5568
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
5628
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5629
|
-
const response = await
|
|
5630
|
-
...getFetchConfig(),
|
|
5631
|
-
});
|
|
5569
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/jobs/${encodeGitLabPathSegment(jobId)}/artifacts`);
|
|
5570
|
+
const response = await fetchWithValidatedRedirects(url.toString(), downloadRedirectOptions());
|
|
5632
5571
|
if (response.status === 404) {
|
|
5633
5572
|
throw new Error(`Job artifacts not found. The job may not have produced artifacts or the job ID is invalid.`);
|
|
5634
5573
|
}
|
|
@@ -5652,14 +5591,9 @@ async function downloadJobArtifacts(projectId, jobId, localPath) {
|
|
|
5652
5591
|
async function getJobArtifactFile(projectId, jobId, artifactPath) {
|
|
5653
5592
|
projectId = decodeURIComponent(projectId);
|
|
5654
5593
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
5655
|
-
const encodedArtifactPath = artifactPath
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
.join("/");
|
|
5659
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/jobs/${encodeGitLabPathSegment(jobId)}/artifacts/${encodedArtifactPath}`);
|
|
5660
|
-
const response = await fetch(url.toString(), {
|
|
5661
|
-
...getFetchConfig(),
|
|
5662
|
-
});
|
|
5594
|
+
const encodedArtifactPath = encodeGitLabPath(artifactPath);
|
|
5595
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/jobs/${encodeGitLabPathSegment(jobId)}/artifacts/${encodedArtifactPath}`);
|
|
5596
|
+
const response = await fetchWithValidatedRedirects(url.toString(), downloadRedirectOptions());
|
|
5663
5597
|
if (response.status === 404) {
|
|
5664
5598
|
throw new Error(`Artifact file not found: ${artifactPath}`);
|
|
5665
5599
|
}
|
|
@@ -5677,7 +5611,7 @@ async function getJobArtifactFile(projectId, jobId, artifactPath) {
|
|
|
5677
5611
|
*/
|
|
5678
5612
|
async function createPipeline(projectId, ref, variables, inputs) {
|
|
5679
5613
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5680
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5614
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipeline`);
|
|
5681
5615
|
const body = { ref };
|
|
5682
5616
|
if (variables && variables.length > 0) {
|
|
5683
5617
|
body.variables = variables;
|
|
@@ -5703,7 +5637,7 @@ async function createPipeline(projectId, ref, variables, inputs) {
|
|
|
5703
5637
|
*/
|
|
5704
5638
|
async function retryPipeline(projectId, pipelineId) {
|
|
5705
5639
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5706
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5640
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipelines/${encodeGitLabPathSegment(pipelineId)}/retry`);
|
|
5707
5641
|
const response = await fetch(url.toString(), {
|
|
5708
5642
|
...getFetchConfig(),
|
|
5709
5643
|
method: "POST",
|
|
@@ -5721,7 +5655,7 @@ async function retryPipeline(projectId, pipelineId) {
|
|
|
5721
5655
|
*/
|
|
5722
5656
|
async function cancelPipeline(projectId, pipelineId) {
|
|
5723
5657
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5724
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5658
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipelines/${encodeGitLabPathSegment(pipelineId)}/cancel`);
|
|
5725
5659
|
const response = await fetch(url.toString(), {
|
|
5726
5660
|
...getFetchConfig(),
|
|
5727
5661
|
method: "POST",
|
|
@@ -5739,12 +5673,8 @@ async function cancelPipeline(projectId, pipelineId) {
|
|
|
5739
5673
|
*/
|
|
5740
5674
|
async function listPipelineSchedules(projectId, options = {}) {
|
|
5741
5675
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5742
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5743
|
-
|
|
5744
|
-
if (value !== undefined) {
|
|
5745
|
-
url.searchParams.append(key, value.toString());
|
|
5746
|
-
}
|
|
5747
|
-
});
|
|
5676
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipeline_schedules`);
|
|
5677
|
+
appendFilterParams(url.searchParams, options);
|
|
5748
5678
|
const response = await fetch(url.toString(), {
|
|
5749
5679
|
...getFetchConfig(),
|
|
5750
5680
|
});
|
|
@@ -5761,7 +5691,7 @@ async function listPipelineSchedules(projectId, options = {}) {
|
|
|
5761
5691
|
*/
|
|
5762
5692
|
async function getPipelineSchedule(projectId, pipelineScheduleId) {
|
|
5763
5693
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5764
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5694
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}`);
|
|
5765
5695
|
const response = await fetch(url.toString(), {
|
|
5766
5696
|
...getFetchConfig(),
|
|
5767
5697
|
});
|
|
@@ -5779,12 +5709,8 @@ async function getPipelineSchedule(projectId, pipelineScheduleId) {
|
|
|
5779
5709
|
*/
|
|
5780
5710
|
async function listPipelineSchedulePipelines(projectId, pipelineScheduleId, options = {}) {
|
|
5781
5711
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5782
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5783
|
-
|
|
5784
|
-
if (value !== undefined) {
|
|
5785
|
-
url.searchParams.append(key, value.toString());
|
|
5786
|
-
}
|
|
5787
|
-
});
|
|
5712
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}/pipelines`);
|
|
5713
|
+
appendFilterParams(url.searchParams, options);
|
|
5788
5714
|
const response = await fetch(url.toString(), {
|
|
5789
5715
|
...getFetchConfig(),
|
|
5790
5716
|
});
|
|
@@ -5801,7 +5727,7 @@ async function listPipelineSchedulePipelines(projectId, pipelineScheduleId, opti
|
|
|
5801
5727
|
*/
|
|
5802
5728
|
async function createPipelineSchedule(projectId, options) {
|
|
5803
5729
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5804
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5730
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipeline_schedules`);
|
|
5805
5731
|
const response = await fetch(url.toString(), {
|
|
5806
5732
|
...getFetchConfig(),
|
|
5807
5733
|
method: "POST",
|
|
@@ -5821,7 +5747,7 @@ async function createPipelineSchedule(projectId, options) {
|
|
|
5821
5747
|
*/
|
|
5822
5748
|
async function updatePipelineSchedule(projectId, pipelineScheduleId, options) {
|
|
5823
5749
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5824
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5750
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}`);
|
|
5825
5751
|
const response = await fetch(url.toString(), {
|
|
5826
5752
|
...getFetchConfig(),
|
|
5827
5753
|
method: "PUT",
|
|
@@ -5840,7 +5766,7 @@ async function updatePipelineSchedule(projectId, pipelineScheduleId, options) {
|
|
|
5840
5766
|
*/
|
|
5841
5767
|
async function deletePipelineSchedule(projectId, pipelineScheduleId) {
|
|
5842
5768
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5843
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5769
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}`);
|
|
5844
5770
|
const response = await fetch(url.toString(), {
|
|
5845
5771
|
...getFetchConfig(),
|
|
5846
5772
|
method: "DELETE",
|
|
@@ -5856,7 +5782,7 @@ async function deletePipelineSchedule(projectId, pipelineScheduleId) {
|
|
|
5856
5782
|
*/
|
|
5857
5783
|
async function playPipelineSchedule(projectId, pipelineScheduleId) {
|
|
5858
5784
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5859
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5785
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}/play`);
|
|
5860
5786
|
const response = await fetch(url.toString(), {
|
|
5861
5787
|
...getFetchConfig(),
|
|
5862
5788
|
method: "POST",
|
|
@@ -5874,7 +5800,7 @@ async function playPipelineSchedule(projectId, pipelineScheduleId) {
|
|
|
5874
5800
|
*/
|
|
5875
5801
|
async function takeOwnershipPipelineSchedule(projectId, pipelineScheduleId) {
|
|
5876
5802
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5877
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5803
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}/take_ownership`);
|
|
5878
5804
|
const response = await fetch(url.toString(), {
|
|
5879
5805
|
...getFetchConfig(),
|
|
5880
5806
|
method: "POST",
|
|
@@ -5893,7 +5819,7 @@ async function takeOwnershipPipelineSchedule(projectId, pipelineScheduleId) {
|
|
|
5893
5819
|
*/
|
|
5894
5820
|
async function getPipelineScheduleVariable(projectId, pipelineScheduleId, key) {
|
|
5895
5821
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5896
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5822
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}/variables/${encodeGitLabPathSegment(key)}`);
|
|
5897
5823
|
const response = await fetch(url.toString(), {
|
|
5898
5824
|
...getFetchConfig(),
|
|
5899
5825
|
});
|
|
@@ -5911,7 +5837,7 @@ async function getPipelineScheduleVariable(projectId, pipelineScheduleId, key) {
|
|
|
5911
5837
|
*/
|
|
5912
5838
|
async function createPipelineScheduleVariable(projectId, pipelineScheduleId, options) {
|
|
5913
5839
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5914
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5840
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}/variables`);
|
|
5915
5841
|
const response = await fetch(url.toString(), {
|
|
5916
5842
|
...getFetchConfig(),
|
|
5917
5843
|
method: "POST",
|
|
@@ -5932,7 +5858,7 @@ async function createPipelineScheduleVariable(projectId, pipelineScheduleId, opt
|
|
|
5932
5858
|
*/
|
|
5933
5859
|
async function updatePipelineScheduleVariable(projectId, pipelineScheduleId, key, options) {
|
|
5934
5860
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5935
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5861
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}/variables/${encodeGitLabPathSegment(key)}`);
|
|
5936
5862
|
const response = await fetch(url.toString(), {
|
|
5937
5863
|
...getFetchConfig(),
|
|
5938
5864
|
method: "PUT",
|
|
@@ -5952,7 +5878,7 @@ async function updatePipelineScheduleVariable(projectId, pipelineScheduleId, key
|
|
|
5952
5878
|
*/
|
|
5953
5879
|
async function deletePipelineScheduleVariable(projectId, pipelineScheduleId, key) {
|
|
5954
5880
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
5955
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5881
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/pipeline_schedules/${encodeGitLabPathSegment(pipelineScheduleId)}/variables/${encodeGitLabPathSegment(key)}`);
|
|
5956
5882
|
const response = await fetch(url.toString(), {
|
|
5957
5883
|
...getFetchConfig(),
|
|
5958
5884
|
method: "DELETE",
|
|
@@ -5969,7 +5895,7 @@ async function deletePipelineScheduleVariable(projectId, pipelineScheduleId, key
|
|
|
5969
5895
|
*/
|
|
5970
5896
|
async function playPipelineJob(projectId, jobId, variables, jobInputs) {
|
|
5971
5897
|
projectId = decodeURIComponent(projectId);
|
|
5972
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5898
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/jobs/${encodeGitLabPathSegment(jobId)}/play`);
|
|
5973
5899
|
const body = {};
|
|
5974
5900
|
if (variables && variables.length > 0) {
|
|
5975
5901
|
body.job_variables_attributes = variables;
|
|
@@ -6003,7 +5929,7 @@ async function playPipelineJobs(projectId, jobIds, variables, timeoutSeconds = 3
|
|
|
6003
5929
|
*/
|
|
6004
5930
|
async function retryPipelineJob(projectId, jobId, jobInputs) {
|
|
6005
5931
|
projectId = decodeURIComponent(projectId);
|
|
6006
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5932
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/jobs/${encodeGitLabPathSegment(jobId)}/retry`);
|
|
6007
5933
|
const response = await fetch(url.toString(), {
|
|
6008
5934
|
...getFetchConfig(),
|
|
6009
5935
|
method: "POST",
|
|
@@ -6015,7 +5941,7 @@ async function retryPipelineJob(projectId, jobId, jobInputs) {
|
|
|
6015
5941
|
}
|
|
6016
5942
|
async function erasePipelineJob(projectId, jobId) {
|
|
6017
5943
|
projectId = decodeURIComponent(projectId);
|
|
6018
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
5944
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/jobs/${encodeGitLabPathSegment(jobId)}/erase`, { ...getFetchConfig(), method: "POST" });
|
|
6019
5945
|
await handleGitLabError(response);
|
|
6020
5946
|
return response.status === 204 ? { erased: true } : response.json();
|
|
6021
5947
|
}
|
|
@@ -6051,7 +5977,7 @@ async function waitForStatus(fetchStatus, terminalStatuses, timeoutSeconds = 300
|
|
|
6051
5977
|
*/
|
|
6052
5978
|
async function cancelPipelineJob(projectId, jobId, force) {
|
|
6053
5979
|
projectId = decodeURIComponent(projectId);
|
|
6054
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
5980
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/jobs/${encodeGitLabPathSegment(jobId)}/cancel`);
|
|
6055
5981
|
if (force !== undefined) {
|
|
6056
5982
|
url.searchParams.append("force", force.toString());
|
|
6057
5983
|
}
|
|
@@ -6083,7 +6009,7 @@ async function getRepositoryTree(options) {
|
|
|
6083
6009
|
queryParams.append("page_token", options.page_token);
|
|
6084
6010
|
if (options.pagination)
|
|
6085
6011
|
queryParams.append("pagination", options.pagination);
|
|
6086
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
6012
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(options.project_id))}/repository/tree?${queryParams.toString()}`, { ...getFetchConfig() });
|
|
6087
6013
|
if (response.status === 404) {
|
|
6088
6014
|
throw new Error("Repository or path not found");
|
|
6089
6015
|
}
|
|
@@ -6105,17 +6031,20 @@ async function getRepositoryTree(options) {
|
|
|
6105
6031
|
*/
|
|
6106
6032
|
async function listProjectMilestones(projectId, options) {
|
|
6107
6033
|
projectId = decodeURIComponent(projectId);
|
|
6108
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6034
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/milestones`);
|
|
6109
6035
|
Object.entries(options).forEach(([key, value]) => {
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
url.searchParams.append(
|
|
6118
|
-
}
|
|
6036
|
+
// Drop blank entries inside arrays so they behave like the scalar blank guard below
|
|
6037
|
+
const normalized = dropBlankArrayEntries(value);
|
|
6038
|
+
if (isBlankFilterValue(normalized)) {
|
|
6039
|
+
return;
|
|
6040
|
+
}
|
|
6041
|
+
if (key === "iids" && Array.isArray(normalized)) {
|
|
6042
|
+
normalized.forEach(iid => {
|
|
6043
|
+
url.searchParams.append("iids[]", String(iid));
|
|
6044
|
+
});
|
|
6045
|
+
}
|
|
6046
|
+
else {
|
|
6047
|
+
url.searchParams.append(key, String(normalized));
|
|
6119
6048
|
}
|
|
6120
6049
|
});
|
|
6121
6050
|
const response = await fetch(url.toString(), {
|
|
@@ -6133,7 +6062,7 @@ async function listProjectMilestones(projectId, options) {
|
|
|
6133
6062
|
*/
|
|
6134
6063
|
async function getProjectMilestone(projectId, milestoneId) {
|
|
6135
6064
|
projectId = decodeURIComponent(projectId);
|
|
6136
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6065
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/milestones/${encodeGitLabPathSegment(milestoneId)}`);
|
|
6137
6066
|
const response = await fetch(url.toString(), {
|
|
6138
6067
|
...getFetchConfig(),
|
|
6139
6068
|
});
|
|
@@ -6149,7 +6078,7 @@ async function getProjectMilestone(projectId, milestoneId) {
|
|
|
6149
6078
|
*/
|
|
6150
6079
|
async function createProjectMilestone(projectId, options) {
|
|
6151
6080
|
projectId = decodeURIComponent(projectId);
|
|
6152
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6081
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/milestones`);
|
|
6153
6082
|
const response = await fetch(url.toString(), {
|
|
6154
6083
|
...getFetchConfig(),
|
|
6155
6084
|
method: "POST",
|
|
@@ -6168,7 +6097,7 @@ async function createProjectMilestone(projectId, options) {
|
|
|
6168
6097
|
*/
|
|
6169
6098
|
async function editProjectMilestone(projectId, milestoneId, options) {
|
|
6170
6099
|
projectId = decodeURIComponent(projectId);
|
|
6171
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6100
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/milestones/${encodeGitLabPathSegment(milestoneId)}`);
|
|
6172
6101
|
const response = await fetch(url.toString(), {
|
|
6173
6102
|
...getFetchConfig(),
|
|
6174
6103
|
method: "PUT",
|
|
@@ -6186,7 +6115,7 @@ async function editProjectMilestone(projectId, milestoneId, options) {
|
|
|
6186
6115
|
*/
|
|
6187
6116
|
async function deleteProjectMilestone(projectId, milestoneId) {
|
|
6188
6117
|
projectId = decodeURIComponent(projectId);
|
|
6189
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6118
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/milestones/${encodeGitLabPathSegment(milestoneId)}`);
|
|
6190
6119
|
const response = await fetch(url.toString(), {
|
|
6191
6120
|
...getFetchConfig(),
|
|
6192
6121
|
method: "DELETE",
|
|
@@ -6201,12 +6130,8 @@ async function deleteProjectMilestone(projectId, milestoneId) {
|
|
|
6201
6130
|
*/
|
|
6202
6131
|
async function getMilestoneIssues(projectId, milestoneId, options = {}) {
|
|
6203
6132
|
projectId = decodeURIComponent(projectId);
|
|
6204
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6205
|
-
|
|
6206
|
-
if (value !== undefined) {
|
|
6207
|
-
url.searchParams.append(key, String(value));
|
|
6208
|
-
}
|
|
6209
|
-
});
|
|
6133
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/milestones/${encodeGitLabPathSegment(milestoneId)}/issues`);
|
|
6134
|
+
appendFilterParams(url.searchParams, options);
|
|
6210
6135
|
const response = await fetch(url.toString(), {
|
|
6211
6136
|
...getFetchConfig(),
|
|
6212
6137
|
});
|
|
@@ -6222,12 +6147,8 @@ async function getMilestoneIssues(projectId, milestoneId, options = {}) {
|
|
|
6222
6147
|
*/
|
|
6223
6148
|
async function getMilestoneMergeRequests(projectId, milestoneId, options = {}) {
|
|
6224
6149
|
projectId = decodeURIComponent(projectId);
|
|
6225
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6226
|
-
|
|
6227
|
-
if (value !== undefined) {
|
|
6228
|
-
url.searchParams.append(key, String(value));
|
|
6229
|
-
}
|
|
6230
|
-
});
|
|
6150
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/milestones/${encodeGitLabPathSegment(milestoneId)}/merge_requests`);
|
|
6151
|
+
appendFilterParams(url.searchParams, options);
|
|
6231
6152
|
const response = await fetch(url.toString(), {
|
|
6232
6153
|
...getFetchConfig(),
|
|
6233
6154
|
});
|
|
@@ -6243,7 +6164,7 @@ async function getMilestoneMergeRequests(projectId, milestoneId, options = {}) {
|
|
|
6243
6164
|
*/
|
|
6244
6165
|
async function promoteProjectMilestone(projectId, milestoneId) {
|
|
6245
6166
|
projectId = decodeURIComponent(projectId);
|
|
6246
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6167
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/milestones/${encodeGitLabPathSegment(milestoneId)}/promote`);
|
|
6247
6168
|
const response = await fetch(url.toString(), {
|
|
6248
6169
|
...getFetchConfig(),
|
|
6249
6170
|
method: "POST",
|
|
@@ -6260,12 +6181,8 @@ async function promoteProjectMilestone(projectId, milestoneId) {
|
|
|
6260
6181
|
*/
|
|
6261
6182
|
async function getMilestoneBurndownEvents(projectId, milestoneId, options = {}) {
|
|
6262
6183
|
projectId = decodeURIComponent(projectId);
|
|
6263
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6264
|
-
|
|
6265
|
-
if (value !== undefined) {
|
|
6266
|
-
url.searchParams.append(key, String(value));
|
|
6267
|
-
}
|
|
6268
|
-
});
|
|
6184
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/milestones/${encodeGitLabPathSegment(milestoneId)}/burndown_events`);
|
|
6185
|
+
appendFilterParams(url.searchParams, options);
|
|
6269
6186
|
const response = await fetch(url.toString(), {
|
|
6270
6187
|
...getFetchConfig(),
|
|
6271
6188
|
});
|
|
@@ -6278,17 +6195,20 @@ async function getMilestoneBurndownEvents(projectId, milestoneId, options = {})
|
|
|
6278
6195
|
*/
|
|
6279
6196
|
async function listGroupMilestones(groupId, options) {
|
|
6280
6197
|
groupId = decodeURIComponent(groupId);
|
|
6281
|
-
const url = new URL(`${getEffectiveApiUrl()}/groups/${
|
|
6198
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(groupId)}/milestones`);
|
|
6282
6199
|
Object.entries(options).forEach(([key, value]) => {
|
|
6283
|
-
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
url.searchParams.append(
|
|
6291
|
-
}
|
|
6200
|
+
// Drop blank entries inside arrays so they behave like the scalar blank guard below
|
|
6201
|
+
const normalized = dropBlankArrayEntries(value);
|
|
6202
|
+
if (isBlankFilterValue(normalized)) {
|
|
6203
|
+
return;
|
|
6204
|
+
}
|
|
6205
|
+
if (key === "iids" && Array.isArray(normalized)) {
|
|
6206
|
+
normalized.forEach(iid => {
|
|
6207
|
+
url.searchParams.append("iids[]", String(iid));
|
|
6208
|
+
});
|
|
6209
|
+
}
|
|
6210
|
+
else {
|
|
6211
|
+
url.searchParams.append(key, String(normalized));
|
|
6292
6212
|
}
|
|
6293
6213
|
});
|
|
6294
6214
|
const response = await fetch(url.toString(), {
|
|
@@ -6303,7 +6223,7 @@ async function listGroupMilestones(groupId, options) {
|
|
|
6303
6223
|
*/
|
|
6304
6224
|
async function getGroupMilestone(groupId, milestoneId) {
|
|
6305
6225
|
groupId = decodeURIComponent(groupId);
|
|
6306
|
-
const url = new URL(`${getEffectiveApiUrl()}/groups/${
|
|
6226
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(groupId)}/milestones/${encodeGitLabPathSegment(milestoneId)}`);
|
|
6307
6227
|
const response = await fetch(url.toString(), {
|
|
6308
6228
|
...getFetchConfig(),
|
|
6309
6229
|
});
|
|
@@ -6316,7 +6236,7 @@ async function getGroupMilestone(groupId, milestoneId) {
|
|
|
6316
6236
|
*/
|
|
6317
6237
|
async function createGroupMilestone(groupId, options) {
|
|
6318
6238
|
groupId = decodeURIComponent(groupId);
|
|
6319
|
-
const url = new URL(`${getEffectiveApiUrl()}/groups/${
|
|
6239
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(groupId)}/milestones`);
|
|
6320
6240
|
const response = await fetch(url.toString(), {
|
|
6321
6241
|
...getFetchConfig(),
|
|
6322
6242
|
method: "POST",
|
|
@@ -6331,7 +6251,7 @@ async function createGroupMilestone(groupId, options) {
|
|
|
6331
6251
|
*/
|
|
6332
6252
|
async function editGroupMilestone(groupId, milestoneId, options) {
|
|
6333
6253
|
groupId = decodeURIComponent(groupId);
|
|
6334
|
-
const url = new URL(`${getEffectiveApiUrl()}/groups/${
|
|
6254
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(groupId)}/milestones/${encodeGitLabPathSegment(milestoneId)}`);
|
|
6335
6255
|
const response = await fetch(url.toString(), {
|
|
6336
6256
|
...getFetchConfig(),
|
|
6337
6257
|
method: "PUT",
|
|
@@ -6346,7 +6266,7 @@ async function editGroupMilestone(groupId, milestoneId, options) {
|
|
|
6346
6266
|
*/
|
|
6347
6267
|
async function deleteGroupMilestone(groupId, milestoneId) {
|
|
6348
6268
|
groupId = decodeURIComponent(groupId);
|
|
6349
|
-
const url = new URL(`${getEffectiveApiUrl()}/groups/${
|
|
6269
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(groupId)}/milestones/${encodeGitLabPathSegment(milestoneId)}`);
|
|
6350
6270
|
const response = await fetch(url.toString(), {
|
|
6351
6271
|
...getFetchConfig(),
|
|
6352
6272
|
method: "DELETE",
|
|
@@ -6358,12 +6278,8 @@ async function deleteGroupMilestone(groupId, milestoneId) {
|
|
|
6358
6278
|
*/
|
|
6359
6279
|
async function getGroupMilestoneIssues(groupId, milestoneId, options = {}) {
|
|
6360
6280
|
groupId = decodeURIComponent(groupId);
|
|
6361
|
-
const url = new URL(`${getEffectiveApiUrl()}/groups/${
|
|
6362
|
-
|
|
6363
|
-
if (value !== undefined) {
|
|
6364
|
-
url.searchParams.append(key, String(value));
|
|
6365
|
-
}
|
|
6366
|
-
});
|
|
6281
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(groupId)}/milestones/${encodeGitLabPathSegment(milestoneId)}/issues`);
|
|
6282
|
+
appendFilterParams(url.searchParams, options);
|
|
6367
6283
|
const response = await fetch(url.toString(), {
|
|
6368
6284
|
...getFetchConfig(),
|
|
6369
6285
|
});
|
|
@@ -6376,12 +6292,8 @@ async function getGroupMilestoneIssues(groupId, milestoneId, options = {}) {
|
|
|
6376
6292
|
*/
|
|
6377
6293
|
async function getGroupMilestoneMergeRequests(groupId, milestoneId, options = {}) {
|
|
6378
6294
|
groupId = decodeURIComponent(groupId);
|
|
6379
|
-
const url = new URL(`${getEffectiveApiUrl()}/groups/${
|
|
6380
|
-
|
|
6381
|
-
if (value !== undefined) {
|
|
6382
|
-
url.searchParams.append(key, String(value));
|
|
6383
|
-
}
|
|
6384
|
-
});
|
|
6295
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(groupId)}/milestones/${encodeGitLabPathSegment(milestoneId)}/merge_requests`);
|
|
6296
|
+
appendFilterParams(url.searchParams, options);
|
|
6385
6297
|
const response = await fetch(url.toString(), {
|
|
6386
6298
|
...getFetchConfig(),
|
|
6387
6299
|
});
|
|
@@ -6394,12 +6306,8 @@ async function getGroupMilestoneMergeRequests(groupId, milestoneId, options = {}
|
|
|
6394
6306
|
*/
|
|
6395
6307
|
async function getGroupMilestoneBurndownEvents(groupId, milestoneId, options = {}) {
|
|
6396
6308
|
groupId = decodeURIComponent(groupId);
|
|
6397
|
-
const url = new URL(`${getEffectiveApiUrl()}/groups/${
|
|
6398
|
-
|
|
6399
|
-
if (value !== undefined) {
|
|
6400
|
-
url.searchParams.append(key, String(value));
|
|
6401
|
-
}
|
|
6402
|
-
});
|
|
6309
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(groupId)}/milestones/${encodeGitLabPathSegment(milestoneId)}/burndown_events`);
|
|
6310
|
+
appendFilterParams(url.searchParams, options);
|
|
6403
6311
|
const response = await fetch(url.toString(), {
|
|
6404
6312
|
...getFetchConfig(),
|
|
6405
6313
|
});
|
|
@@ -6469,7 +6377,7 @@ async function getUsers(usernames) {
|
|
|
6469
6377
|
*/
|
|
6470
6378
|
async function listCommits(projectId, options = {}) {
|
|
6471
6379
|
projectId = decodeURIComponent(projectId);
|
|
6472
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6380
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/repository/commits`);
|
|
6473
6381
|
// Add query parameters
|
|
6474
6382
|
if (options.ref_name)
|
|
6475
6383
|
url.searchParams.append("ref_name", options.ref_name);
|
|
@@ -6513,7 +6421,7 @@ async function listCommits(projectId, options = {}) {
|
|
|
6513
6421
|
*/
|
|
6514
6422
|
async function getCommit(projectId, sha, stats) {
|
|
6515
6423
|
projectId = decodeURIComponent(projectId);
|
|
6516
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6424
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/repository/commits/${encodeGitLabPathSegment(sha)}`);
|
|
6517
6425
|
if (stats) {
|
|
6518
6426
|
url.searchParams.append("stats", "true");
|
|
6519
6427
|
}
|
|
@@ -6535,7 +6443,7 @@ async function getCommit(projectId, sha, stats) {
|
|
|
6535
6443
|
*/
|
|
6536
6444
|
async function getCommitDiff(projectId, sha, full_diff) {
|
|
6537
6445
|
projectId = decodeURIComponent(projectId);
|
|
6538
|
-
const baseUrl = `${getEffectiveApiUrl()}/projects/${
|
|
6446
|
+
const baseUrl = `${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/repository/commits/${encodeGitLabPathSegment(sha)}/diff`;
|
|
6539
6447
|
let allDiffs = [];
|
|
6540
6448
|
let page = 1;
|
|
6541
6449
|
while (true) {
|
|
@@ -6574,7 +6482,7 @@ async function getCommitDiff(projectId, sha, full_diff) {
|
|
|
6574
6482
|
*/
|
|
6575
6483
|
async function getFileBlame(projectId, options) {
|
|
6576
6484
|
projectId = decodeURIComponent(projectId);
|
|
6577
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6485
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/repository/files/${encodeGitLabPathSegment(options.file_path)}/blame`);
|
|
6578
6486
|
url.searchParams.append("ref", options.ref);
|
|
6579
6487
|
if (options.range_start !== undefined && options.range_end !== undefined) {
|
|
6580
6488
|
url.searchParams.append("range[start]", options.range_start.toString());
|
|
@@ -6597,12 +6505,8 @@ async function getFileBlame(projectId, options) {
|
|
|
6597
6505
|
*/
|
|
6598
6506
|
async function listCommitStatuses(projectId, sha, options = {}) {
|
|
6599
6507
|
projectId = decodeURIComponent(projectId);
|
|
6600
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6601
|
-
|
|
6602
|
-
if (value !== undefined) {
|
|
6603
|
-
url.searchParams.append(key, value.toString());
|
|
6604
|
-
}
|
|
6605
|
-
});
|
|
6508
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/repository/commits/${encodeGitLabPathSegment(sha)}/statuses`);
|
|
6509
|
+
appendFilterParams(url.searchParams, options);
|
|
6606
6510
|
const response = await fetch(url.toString(), {
|
|
6607
6511
|
...getFetchConfig(),
|
|
6608
6512
|
});
|
|
@@ -6623,12 +6527,8 @@ async function createCommitStatus(projectId, sha, options) {
|
|
|
6623
6527
|
throw new Error("Use either name or context when creating a commit status, not both.");
|
|
6624
6528
|
}
|
|
6625
6529
|
projectId = decodeURIComponent(projectId);
|
|
6626
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6627
|
-
|
|
6628
|
-
if (value !== undefined) {
|
|
6629
|
-
url.searchParams.append(key, value.toString());
|
|
6630
|
-
}
|
|
6631
|
-
});
|
|
6530
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/statuses/${encodeGitLabPathSegment(sha)}`);
|
|
6531
|
+
appendFilterParams(url.searchParams, options);
|
|
6632
6532
|
const response = await fetch(url.toString(), {
|
|
6633
6533
|
...getFetchConfig(),
|
|
6634
6534
|
method: "POST",
|
|
@@ -6717,7 +6617,7 @@ async function listProjectMembers(projectId, options = {}) {
|
|
|
6717
6617
|
projectId = decodeURIComponent(projectId);
|
|
6718
6618
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
6719
6619
|
const membersPath = options.include_inheritance ? "members/all" : "members";
|
|
6720
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6620
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/${membersPath}`);
|
|
6721
6621
|
// Add query parameters
|
|
6722
6622
|
if (options.query)
|
|
6723
6623
|
url.searchParams.append("query", options.query);
|
|
@@ -6747,7 +6647,7 @@ async function listProjectMembers(projectId, options = {}) {
|
|
|
6747
6647
|
async function listGroupMembers(groupId, options = {}) {
|
|
6748
6648
|
groupId = decodeURIComponent(groupId);
|
|
6749
6649
|
const membersPath = options.include_inheritance ? "members/all" : "members";
|
|
6750
|
-
const url = new URL(`${getEffectiveApiUrl()}/groups/${
|
|
6650
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(groupId)}/${membersPath}`);
|
|
6751
6651
|
if (options.query)
|
|
6752
6652
|
url.searchParams.append("query", options.query);
|
|
6753
6653
|
if (options.user_ids) {
|
|
@@ -6774,7 +6674,7 @@ async function listGroupMembers(groupId, options = {}) {
|
|
|
6774
6674
|
*/
|
|
6775
6675
|
async function listGroupIterations(groupId, options = {}) {
|
|
6776
6676
|
groupId = decodeURIComponent(groupId);
|
|
6777
|
-
const url = new URL(`${getEffectiveApiUrl()}/groups/${
|
|
6677
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encodeGitLabPathSegment(groupId)}/iterations`);
|
|
6778
6678
|
// クエリパラメータの追加
|
|
6779
6679
|
if (options.state)
|
|
6780
6680
|
url.searchParams.append("state", options.state);
|
|
@@ -6804,21 +6704,15 @@ async function listGroupIterations(groupId, options = {}) {
|
|
|
6804
6704
|
// --- CI/CD Variables ---
|
|
6805
6705
|
async function listProjectVariables(projectId, options = {}) {
|
|
6806
6706
|
projectId = decodeURIComponent(projectId);
|
|
6807
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6707
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/variables`);
|
|
6808
6708
|
Object.entries(options).forEach(([key, value]) => {
|
|
6809
|
-
if (value === undefined)
|
|
6810
|
-
return;
|
|
6811
6709
|
if (key === "filter" && typeof value === "object" && value !== null) {
|
|
6812
|
-
Object.entries(value).forEach(([
|
|
6813
|
-
url.searchParams
|
|
6710
|
+
Object.entries(value).forEach(([filterKey, filterValue]) => {
|
|
6711
|
+
appendFilterParam(url.searchParams, `filter[${filterKey}]`, filterValue);
|
|
6814
6712
|
});
|
|
6713
|
+
return;
|
|
6815
6714
|
}
|
|
6816
|
-
|
|
6817
|
-
url.searchParams.append(key, value ? "true" : "false");
|
|
6818
|
-
}
|
|
6819
|
-
else {
|
|
6820
|
-
url.searchParams.append(key, String(value));
|
|
6821
|
-
}
|
|
6715
|
+
appendFilterParam(url.searchParams, key, value);
|
|
6822
6716
|
});
|
|
6823
6717
|
const response = await fetch(url.toString(), getFetchConfig());
|
|
6824
6718
|
await handleGitLabError(response);
|
|
@@ -6827,7 +6721,7 @@ async function listProjectVariables(projectId, options = {}) {
|
|
|
6827
6721
|
}
|
|
6828
6722
|
async function getProjectVariable(projectId, key, filter) {
|
|
6829
6723
|
projectId = decodeURIComponent(projectId);
|
|
6830
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6724
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/variables/${encodeGitLabPathSegment(key)}`);
|
|
6831
6725
|
if (filter?.environment_scope) {
|
|
6832
6726
|
url.searchParams.append("filter[environment_scope]", filter.environment_scope);
|
|
6833
6727
|
}
|
|
@@ -6838,7 +6732,7 @@ async function getProjectVariable(projectId, key, filter) {
|
|
|
6838
6732
|
}
|
|
6839
6733
|
async function createProjectVariable(projectId, options) {
|
|
6840
6734
|
projectId = decodeURIComponent(projectId);
|
|
6841
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
6735
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/variables`, { ...getFetchConfig(), method: "POST", body: JSON.stringify(options) });
|
|
6842
6736
|
await handleGitLabError(response);
|
|
6843
6737
|
const data = await response.json();
|
|
6844
6738
|
return GitLabCiVariableSchema.parse(data);
|
|
@@ -6846,7 +6740,7 @@ async function createProjectVariable(projectId, options) {
|
|
|
6846
6740
|
async function updateProjectVariable(projectId, key, options) {
|
|
6847
6741
|
projectId = decodeURIComponent(projectId);
|
|
6848
6742
|
const { filter, ...body } = options;
|
|
6849
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6743
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/variables/${encodeGitLabPathSegment(key)}`);
|
|
6850
6744
|
if (filter?.environment_scope) {
|
|
6851
6745
|
url.searchParams.append("filter[environment_scope]", filter.environment_scope);
|
|
6852
6746
|
}
|
|
@@ -6861,7 +6755,7 @@ async function updateProjectVariable(projectId, key, options) {
|
|
|
6861
6755
|
}
|
|
6862
6756
|
async function deleteProjectVariable(projectId, key, filter) {
|
|
6863
6757
|
projectId = decodeURIComponent(projectId);
|
|
6864
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6758
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/variables/${encodeGitLabPathSegment(key)}`);
|
|
6865
6759
|
if (filter?.environment_scope) {
|
|
6866
6760
|
url.searchParams.append("filter[environment_scope]", filter.environment_scope);
|
|
6867
6761
|
}
|
|
@@ -6869,22 +6763,16 @@ async function deleteProjectVariable(projectId, key, filter) {
|
|
|
6869
6763
|
await handleGitLabError(response);
|
|
6870
6764
|
}
|
|
6871
6765
|
async function listGroupVariables(groupId, options = {}) {
|
|
6872
|
-
const encoded =
|
|
6766
|
+
const encoded = encodeGitLabPathSegment(groupId);
|
|
6873
6767
|
const url = new URL(`${getEffectiveApiUrl()}/groups/${encoded}/variables`);
|
|
6874
6768
|
Object.entries(options).forEach(([key, value]) => {
|
|
6875
|
-
if (value === undefined)
|
|
6876
|
-
return;
|
|
6877
6769
|
if (key === "filter" && typeof value === "object" && value !== null) {
|
|
6878
|
-
Object.entries(value).forEach(([
|
|
6879
|
-
url.searchParams
|
|
6770
|
+
Object.entries(value).forEach(([filterKey, filterValue]) => {
|
|
6771
|
+
appendFilterParam(url.searchParams, `filter[${filterKey}]`, filterValue);
|
|
6880
6772
|
});
|
|
6773
|
+
return;
|
|
6881
6774
|
}
|
|
6882
|
-
|
|
6883
|
-
url.searchParams.append(key, value ? "true" : "false");
|
|
6884
|
-
}
|
|
6885
|
-
else {
|
|
6886
|
-
url.searchParams.append(key, String(value));
|
|
6887
|
-
}
|
|
6775
|
+
appendFilterParam(url.searchParams, key, value);
|
|
6888
6776
|
});
|
|
6889
6777
|
const response = await fetch(url.toString(), getFetchConfig());
|
|
6890
6778
|
await handleGitLabError(response);
|
|
@@ -6892,8 +6780,8 @@ async function listGroupVariables(groupId, options = {}) {
|
|
|
6892
6780
|
return z.array(GitLabCiVariableSchema).parse(data);
|
|
6893
6781
|
}
|
|
6894
6782
|
async function getGroupVariable(groupId, key, filter) {
|
|
6895
|
-
const encoded =
|
|
6896
|
-
const url = new URL(`${getEffectiveApiUrl()}/groups/${encoded}/variables/${
|
|
6783
|
+
const encoded = encodeGitLabPathSegment(groupId);
|
|
6784
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encoded}/variables/${encodeGitLabPathSegment(key)}`);
|
|
6897
6785
|
if (filter?.environment_scope) {
|
|
6898
6786
|
url.searchParams.append("filter[environment_scope]", filter.environment_scope);
|
|
6899
6787
|
}
|
|
@@ -6903,7 +6791,7 @@ async function getGroupVariable(groupId, key, filter) {
|
|
|
6903
6791
|
return GitLabCiVariableSchema.parse(data);
|
|
6904
6792
|
}
|
|
6905
6793
|
async function createGroupVariable(groupId, options) {
|
|
6906
|
-
const encoded =
|
|
6794
|
+
const encoded = encodeGitLabPathSegment(groupId);
|
|
6907
6795
|
const response = await fetch(`${getEffectiveApiUrl()}/groups/${encoded}/variables`, {
|
|
6908
6796
|
...getFetchConfig(),
|
|
6909
6797
|
method: "POST",
|
|
@@ -6914,9 +6802,9 @@ async function createGroupVariable(groupId, options) {
|
|
|
6914
6802
|
return GitLabCiVariableSchema.parse(data);
|
|
6915
6803
|
}
|
|
6916
6804
|
async function updateGroupVariable(groupId, key, options) {
|
|
6917
|
-
const encoded =
|
|
6805
|
+
const encoded = encodeGitLabPathSegment(groupId);
|
|
6918
6806
|
const { filter, ...body } = options;
|
|
6919
|
-
const url = new URL(`${getEffectiveApiUrl()}/groups/${encoded}/variables/${
|
|
6807
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encoded}/variables/${encodeGitLabPathSegment(key)}`);
|
|
6920
6808
|
if (filter?.environment_scope) {
|
|
6921
6809
|
url.searchParams.append("filter[environment_scope]", filter.environment_scope);
|
|
6922
6810
|
}
|
|
@@ -6930,8 +6818,8 @@ async function updateGroupVariable(groupId, key, options) {
|
|
|
6930
6818
|
return GitLabCiVariableSchema.parse(data);
|
|
6931
6819
|
}
|
|
6932
6820
|
async function deleteGroupVariable(groupId, key, filter) {
|
|
6933
|
-
const encoded =
|
|
6934
|
-
const url = new URL(`${getEffectiveApiUrl()}/groups/${encoded}/variables/${
|
|
6821
|
+
const encoded = encodeGitLabPathSegment(groupId);
|
|
6822
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encoded}/variables/${encodeGitLabPathSegment(key)}`);
|
|
6935
6823
|
if (filter?.environment_scope) {
|
|
6936
6824
|
url.searchParams.append("filter[environment_scope]", filter.environment_scope);
|
|
6937
6825
|
}
|
|
@@ -7017,7 +6905,7 @@ async function listDependencyProxyBlobs(groupPath, options = {}) {
|
|
|
7017
6905
|
};
|
|
7018
6906
|
}
|
|
7019
6907
|
async function purgeDependencyProxyCache(groupId) {
|
|
7020
|
-
const encoded =
|
|
6908
|
+
const encoded = encodeGitLabPathSegment(groupId);
|
|
7021
6909
|
const url = new URL(`${getEffectiveApiUrl()}/groups/${encoded}/dependency_proxy/cache`);
|
|
7022
6910
|
const response = await fetch(url.toString(), { ...getFetchConfig(), method: "DELETE" });
|
|
7023
6911
|
await handleGitLabError(response);
|
|
@@ -7047,7 +6935,7 @@ async function resolveProjectFullPath(projectId) {
|
|
|
7047
6935
|
if (!/^\d+$/.test(decoded)) {
|
|
7048
6936
|
return decoded;
|
|
7049
6937
|
}
|
|
7050
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
6938
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(decoded)}`);
|
|
7051
6939
|
const response = await fetch(url.toString(), { ...getFetchConfig() });
|
|
7052
6940
|
await handleGitLabError(response);
|
|
7053
6941
|
const project = (await response.json());
|
|
@@ -7358,7 +7246,7 @@ async function markdownUpload(projectId, filePath, content, filename) {
|
|
|
7358
7246
|
// Create form data
|
|
7359
7247
|
const form = new FormData();
|
|
7360
7248
|
form.append("file", new File([fileBuffer], fileName, { type: "application/octet-stream" }));
|
|
7361
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
7249
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/uploads`);
|
|
7362
7250
|
const defaultFetchConfig = getFetchConfig();
|
|
7363
7251
|
delete defaultFetchConfig.headers["Content-Type"];
|
|
7364
7252
|
const response = await fetch(url.toString(), {
|
|
@@ -7399,11 +7287,8 @@ function getSafeAttachmentFilename(filename) {
|
|
|
7399
7287
|
async function downloadAttachment(projectId, secret, filename, localPath) {
|
|
7400
7288
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
7401
7289
|
const safeFilename = getSafeAttachmentFilename(filename);
|
|
7402
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
7403
|
-
const response = await
|
|
7404
|
-
...getFetchConfig(),
|
|
7405
|
-
method: "GET",
|
|
7406
|
-
});
|
|
7290
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/uploads/${encodeGitLabPathSegment(secret)}/${encodeGitLabPathSegment(safeFilename)}`);
|
|
7291
|
+
const response = await fetchWithValidatedRedirects(url.toString(), downloadRedirectOptions());
|
|
7407
7292
|
if (!response.ok) {
|
|
7408
7293
|
await handleGitLabError(response);
|
|
7409
7294
|
}
|
|
@@ -7431,11 +7316,7 @@ async function downloadAttachment(projectId, secret, filename, localPath) {
|
|
|
7431
7316
|
async function listEvents(options = {}) {
|
|
7432
7317
|
const url = new URL(`${getEffectiveApiUrl()}/events`);
|
|
7433
7318
|
// Add all query parameters
|
|
7434
|
-
|
|
7435
|
-
if (value !== undefined) {
|
|
7436
|
-
url.searchParams.append(key, value.toString());
|
|
7437
|
-
}
|
|
7438
|
-
});
|
|
7319
|
+
appendFilterParams(url.searchParams, options);
|
|
7439
7320
|
const response = await fetch(url.toString(), { ...getFetchConfig() });
|
|
7440
7321
|
if (!response.ok) {
|
|
7441
7322
|
await handleGitLabError(response);
|
|
@@ -7451,13 +7332,9 @@ async function listEvents(options = {}) {
|
|
|
7451
7332
|
*/
|
|
7452
7333
|
async function getProjectEvents(projectId, options = {}) {
|
|
7453
7334
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
7454
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
7335
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/events`);
|
|
7455
7336
|
// Add all query parameters
|
|
7456
|
-
|
|
7457
|
-
if (value !== undefined) {
|
|
7458
|
-
url.searchParams.append(key, value.toString());
|
|
7459
|
-
}
|
|
7460
|
-
});
|
|
7337
|
+
appendFilterParams(url.searchParams, options);
|
|
7461
7338
|
const response = await fetch(url.toString(), { ...getFetchConfig() });
|
|
7462
7339
|
if (!response.ok) {
|
|
7463
7340
|
await handleGitLabError(response);
|
|
@@ -7474,13 +7351,9 @@ async function getProjectEvents(projectId, options = {}) {
|
|
|
7474
7351
|
*/
|
|
7475
7352
|
async function listReleases(projectId, options = {}) {
|
|
7476
7353
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
7477
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
7354
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/releases`);
|
|
7478
7355
|
// Add query parameters
|
|
7479
|
-
|
|
7480
|
-
if (value !== undefined) {
|
|
7481
|
-
url.searchParams.append(key, value.toString());
|
|
7482
|
-
}
|
|
7483
|
-
});
|
|
7356
|
+
appendFilterParams(url.searchParams, options);
|
|
7484
7357
|
const response = await fetch(url.toString(), {
|
|
7485
7358
|
...getFetchConfig(),
|
|
7486
7359
|
});
|
|
@@ -7498,7 +7371,7 @@ async function listReleases(projectId, options = {}) {
|
|
|
7498
7371
|
*/
|
|
7499
7372
|
async function getRelease(projectId, tagName, includeHtmlDescription) {
|
|
7500
7373
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
7501
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
7374
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/releases/${encodeGitLabPathSegment(tagName)}`);
|
|
7502
7375
|
if (includeHtmlDescription !== undefined) {
|
|
7503
7376
|
url.searchParams.append("include_html_description", includeHtmlDescription.toString());
|
|
7504
7377
|
}
|
|
@@ -7518,7 +7391,7 @@ async function getRelease(projectId, tagName, includeHtmlDescription) {
|
|
|
7518
7391
|
*/
|
|
7519
7392
|
async function createRelease(projectId, options) {
|
|
7520
7393
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
7521
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
7394
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/releases`, {
|
|
7522
7395
|
...getFetchConfig(),
|
|
7523
7396
|
method: "POST",
|
|
7524
7397
|
body: JSON.stringify(options),
|
|
@@ -7537,7 +7410,7 @@ async function createRelease(projectId, options) {
|
|
|
7537
7410
|
*/
|
|
7538
7411
|
async function updateRelease(projectId, tagName, options) {
|
|
7539
7412
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
7540
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
7413
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/releases/${encodeGitLabPathSegment(tagName)}`, {
|
|
7541
7414
|
...getFetchConfig(),
|
|
7542
7415
|
method: "PUT",
|
|
7543
7416
|
body: JSON.stringify(options),
|
|
@@ -7555,7 +7428,7 @@ async function updateRelease(projectId, tagName, options) {
|
|
|
7555
7428
|
*/
|
|
7556
7429
|
async function deleteRelease(projectId, tagName) {
|
|
7557
7430
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
7558
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
7431
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/releases/${encodeGitLabPathSegment(tagName)}`, {
|
|
7559
7432
|
...getFetchConfig(),
|
|
7560
7433
|
method: "DELETE",
|
|
7561
7434
|
});
|
|
@@ -7571,7 +7444,7 @@ async function deleteRelease(projectId, tagName) {
|
|
|
7571
7444
|
*/
|
|
7572
7445
|
async function createReleaseEvidence(projectId, tagName) {
|
|
7573
7446
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
7574
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
7447
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/releases/${encodeGitLabPathSegment(tagName)}/evidence`, {
|
|
7575
7448
|
...getFetchConfig(),
|
|
7576
7449
|
method: "POST",
|
|
7577
7450
|
});
|
|
@@ -7587,9 +7460,7 @@ async function createReleaseEvidence(projectId, tagName) {
|
|
|
7587
7460
|
*/
|
|
7588
7461
|
async function downloadReleaseAsset(projectId, tagName, directAssetPath) {
|
|
7589
7462
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
7590
|
-
const response = await
|
|
7591
|
-
...getFetchConfig(),
|
|
7592
|
-
});
|
|
7463
|
+
const response = await fetchWithValidatedRedirects(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/releases/${encodeGitLabPathSegment(tagName)}/downloads/${encodeGitLabPath(directAssetPath)}`, downloadRedirectOptions());
|
|
7593
7464
|
await handleGitLabError(response);
|
|
7594
7465
|
return await response.text();
|
|
7595
7466
|
}
|
|
@@ -7602,12 +7473,8 @@ async function downloadReleaseAsset(projectId, tagName, directAssetPath) {
|
|
|
7602
7473
|
*/
|
|
7603
7474
|
async function listTags(projectId, options = {}) {
|
|
7604
7475
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
7605
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
7606
|
-
|
|
7607
|
-
if (value !== undefined) {
|
|
7608
|
-
url.searchParams.append(key, value.toString());
|
|
7609
|
-
}
|
|
7610
|
-
});
|
|
7476
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/repository/tags`);
|
|
7477
|
+
appendFilterParams(url.searchParams, options);
|
|
7611
7478
|
const response = await fetch(url.toString(), {
|
|
7612
7479
|
...getFetchConfig(),
|
|
7613
7480
|
});
|
|
@@ -7624,7 +7491,7 @@ async function listTags(projectId, options = {}) {
|
|
|
7624
7491
|
*/
|
|
7625
7492
|
async function getTag(projectId, tagName) {
|
|
7626
7493
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
7627
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
7494
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/repository/tags/${encodeGitLabPathSegment(tagName)}`, {
|
|
7628
7495
|
...getFetchConfig(),
|
|
7629
7496
|
});
|
|
7630
7497
|
await handleGitLabError(response);
|
|
@@ -7640,7 +7507,7 @@ async function getTag(projectId, tagName) {
|
|
|
7640
7507
|
*/
|
|
7641
7508
|
async function createTag(projectId, options) {
|
|
7642
7509
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
7643
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
7510
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/repository/tags`, {
|
|
7644
7511
|
...getFetchConfig(),
|
|
7645
7512
|
method: "POST",
|
|
7646
7513
|
body: JSON.stringify(options),
|
|
@@ -7657,7 +7524,7 @@ async function createTag(projectId, options) {
|
|
|
7657
7524
|
*/
|
|
7658
7525
|
async function deleteTag(projectId, tagName) {
|
|
7659
7526
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
7660
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
7527
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/repository/tags/${encodeGitLabPathSegment(tagName)}`, {
|
|
7661
7528
|
...getFetchConfig(),
|
|
7662
7529
|
method: "DELETE",
|
|
7663
7530
|
});
|
|
@@ -7672,7 +7539,7 @@ async function deleteTag(projectId, tagName) {
|
|
|
7672
7539
|
*/
|
|
7673
7540
|
async function getTagSignature(projectId, tagName) {
|
|
7674
7541
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
7675
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
7542
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/repository/tags/${encodeGitLabPathSegment(tagName)}/signature`, {
|
|
7676
7543
|
...getFetchConfig(),
|
|
7677
7544
|
});
|
|
7678
7545
|
await handleGitLabError(response);
|
|
@@ -7736,7 +7603,7 @@ async function handleToolCall(params) {
|
|
|
7736
7603
|
if (!isToolAllowedByPermissionMode(params.name)) {
|
|
7737
7604
|
throw new Error(GITLAB_PERMISSION_MODE === "readonly"
|
|
7738
7605
|
? `${params.name} is not allowed in read-only mode`
|
|
7739
|
-
: `${params.name} is not allowed in modify mode (delete operations are disabled)`);
|
|
7606
|
+
: `${params.name} is not allowed in modify mode (delete and teardown operations are disabled)`);
|
|
7740
7607
|
}
|
|
7741
7608
|
logger.info({ tool: params.name, event: "tool_call_start" }, `tool_call_start: ${params.name}`);
|
|
7742
7609
|
switch (params.name) {
|
|
@@ -7749,7 +7616,7 @@ async function handleToolCall(params) {
|
|
|
7749
7616
|
}
|
|
7750
7617
|
if (GITLAB_PERMISSION_MODE === "modify" &&
|
|
7751
7618
|
graphqlQueryContainsDeleteOperation(args.query)) {
|
|
7752
|
-
throw new Error("execute_graphql does not allow
|
|
7619
|
+
throw new Error("execute_graphql does not allow destructive mutations in modify mode");
|
|
7753
7620
|
}
|
|
7754
7621
|
const apiUrl = new URL(getEffectiveApiUrl());
|
|
7755
7622
|
// Build GraphQL endpoint preserving any instance subpath (e.g. /gitlab)
|
|
@@ -8339,7 +8206,7 @@ async function handleToolCall(params) {
|
|
|
8339
8206
|
}
|
|
8340
8207
|
case "get_namespace": {
|
|
8341
8208
|
const args = GetNamespaceSchema.parse(params.arguments);
|
|
8342
|
-
const url = new URL(`${GITLAB_API_URL}/namespaces/${
|
|
8209
|
+
const url = new URL(`${GITLAB_API_URL}/namespaces/${encodeGitLabPathSegment(args.namespace_id)}`);
|
|
8343
8210
|
const response = await fetch(url.toString(), {
|
|
8344
8211
|
...getFetchConfig(),
|
|
8345
8212
|
});
|
|
@@ -8352,7 +8219,7 @@ async function handleToolCall(params) {
|
|
|
8352
8219
|
}
|
|
8353
8220
|
case "verify_namespace": {
|
|
8354
8221
|
const args = VerifyNamespaceSchema.parse(params.arguments);
|
|
8355
|
-
const url = new URL(`${GITLAB_API_URL}/namespaces/${
|
|
8222
|
+
const url = new URL(`${GITLAB_API_URL}/namespaces/${encodeGitLabPathSegment(args.path)}/exists`);
|
|
8356
8223
|
if (args.parent_id !== undefined)
|
|
8357
8224
|
url.searchParams.set("parent_id", String(args.parent_id));
|
|
8358
8225
|
const response = await fetch(url.toString(), {
|
|
@@ -8369,7 +8236,7 @@ async function handleToolCall(params) {
|
|
|
8369
8236
|
const args = GetProjectSchema.parse(params.arguments);
|
|
8370
8237
|
const options = params.arguments;
|
|
8371
8238
|
const effectiveProjectId = getEffectiveProjectId(args.project_id);
|
|
8372
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
8239
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}`);
|
|
8373
8240
|
if (options.license)
|
|
8374
8241
|
url.searchParams.append("license", "true");
|
|
8375
8242
|
if (options.statistics)
|
|
@@ -8398,7 +8265,7 @@ async function handleToolCall(params) {
|
|
|
8398
8265
|
const { project_id, ...updates } = UpdateProjectSchema.parse(params.arguments);
|
|
8399
8266
|
const effectiveProjectId = getEffectiveProjectId(project_id);
|
|
8400
8267
|
const body = Object.fromEntries(Object.entries(updates).filter(([, value]) => value !== undefined));
|
|
8401
|
-
const response = await fetch(`${getEffectiveApiUrl()}/projects/${
|
|
8268
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}`, {
|
|
8402
8269
|
...getFetchConfig(),
|
|
8403
8270
|
method: "PUT",
|
|
8404
8271
|
body: JSON.stringify(body),
|
|
@@ -8437,7 +8304,7 @@ async function handleToolCall(params) {
|
|
|
8437
8304
|
}
|
|
8438
8305
|
case "get_user": {
|
|
8439
8306
|
const args = GetUserSchema.parse(params.arguments);
|
|
8440
|
-
const url = new URL(`${getEffectiveApiUrl()}/users/${
|
|
8307
|
+
const url = new URL(`${getEffectiveApiUrl()}/users/${encodeGitLabPathSegment(args.user_id)}`);
|
|
8441
8308
|
const response = await fetch(url.toString(), {
|
|
8442
8309
|
...getFetchConfig(),
|
|
8443
8310
|
});
|
|
@@ -8847,8 +8714,8 @@ async function handleToolCall(params) {
|
|
|
8847
8714
|
};
|
|
8848
8715
|
}
|
|
8849
8716
|
case "list_labels": {
|
|
8850
|
-
const
|
|
8851
|
-
const labels = await listLabels(
|
|
8717
|
+
const { project_id, ...options } = ListLabelsSchema.parse(params.arguments);
|
|
8718
|
+
const labels = await listLabels(project_id, options);
|
|
8852
8719
|
return {
|
|
8853
8720
|
content: [{ type: "text", text: JSON.stringify(labels) }],
|
|
8854
8721
|
};
|
|
@@ -10317,7 +10184,7 @@ async function handleToolCall(params) {
|
|
|
10317
10184
|
case "get_branch": {
|
|
10318
10185
|
const args = GetBranchSchema.parse(params.arguments);
|
|
10319
10186
|
const projectId = decodeURIComponent(args.project_id);
|
|
10320
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
10187
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/repository/branches/${encodeGitLabPathSegment(args.branch_name)}`);
|
|
10321
10188
|
const response = await fetch(url.toString(), {
|
|
10322
10189
|
...getFetchConfig(),
|
|
10323
10190
|
});
|
|
@@ -10331,7 +10198,7 @@ async function handleToolCall(params) {
|
|
|
10331
10198
|
case "list_branches": {
|
|
10332
10199
|
const args = ListBranchesSchema.parse(params.arguments);
|
|
10333
10200
|
const projectId = decodeURIComponent(args.project_id);
|
|
10334
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
10201
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/repository/branches`);
|
|
10335
10202
|
if (args.search) {
|
|
10336
10203
|
url.searchParams.append("search", args.search);
|
|
10337
10204
|
}
|
|
@@ -10354,7 +10221,7 @@ async function handleToolCall(params) {
|
|
|
10354
10221
|
case "delete_branch": {
|
|
10355
10222
|
const args = DeleteBranchSchema.parse(params.arguments);
|
|
10356
10223
|
const projectId = decodeURIComponent(args.project_id);
|
|
10357
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
10224
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/repository/branches/${encodeGitLabPathSegment(args.branch_name)}`);
|
|
10358
10225
|
const response = await fetch(url.toString(), {
|
|
10359
10226
|
...getFetchConfig(),
|
|
10360
10227
|
method: "DELETE",
|
|
@@ -10368,7 +10235,7 @@ async function handleToolCall(params) {
|
|
|
10368
10235
|
const args = ListProtectedBranchesSchema.parse(params.arguments);
|
|
10369
10236
|
const projectId = decodeURIComponent(args.project_id);
|
|
10370
10237
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
10371
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
10238
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/protected_branches`);
|
|
10372
10239
|
if (args.search)
|
|
10373
10240
|
url.searchParams.append("search", args.search);
|
|
10374
10241
|
if (args.page)
|
|
@@ -10388,7 +10255,7 @@ async function handleToolCall(params) {
|
|
|
10388
10255
|
const args = GetProtectedBranchSchema.parse(params.arguments);
|
|
10389
10256
|
const projectId = decodeURIComponent(args.project_id);
|
|
10390
10257
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
10391
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
10258
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/protected_branches/${encodeGitLabPathSegment(args.branch_name)}`);
|
|
10392
10259
|
const response = await fetch(url.toString(), {
|
|
10393
10260
|
...getFetchConfig(),
|
|
10394
10261
|
});
|
|
@@ -10402,7 +10269,7 @@ async function handleToolCall(params) {
|
|
|
10402
10269
|
const args = ProtectBranchSchema.parse(params.arguments);
|
|
10403
10270
|
const projectId = decodeURIComponent(args.project_id);
|
|
10404
10271
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
10405
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
10272
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/protected_branches`);
|
|
10406
10273
|
const body = { name: args.branch_name };
|
|
10407
10274
|
if (args.push_access_level !== undefined)
|
|
10408
10275
|
body.push_access_level = args.push_access_level;
|
|
@@ -10429,7 +10296,7 @@ async function handleToolCall(params) {
|
|
|
10429
10296
|
const args = UnprotectBranchSchema.parse(params.arguments);
|
|
10430
10297
|
const projectId = decodeURIComponent(args.project_id);
|
|
10431
10298
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
10432
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
10299
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}/protected_branches/${encodeGitLabPathSegment(args.branch_name)}`);
|
|
10433
10300
|
const response = await fetch(url.toString(), {
|
|
10434
10301
|
...getFetchConfig(),
|
|
10435
10302
|
method: "DELETE",
|
|
@@ -10443,7 +10310,7 @@ async function handleToolCall(params) {
|
|
|
10443
10310
|
const args = UpdateDefaultBranchSchema.parse(params.arguments);
|
|
10444
10311
|
const projectId = decodeURIComponent(args.project_id);
|
|
10445
10312
|
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
10446
|
-
const url = new URL(`${getEffectiveApiUrl()}/projects/${
|
|
10313
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(effectiveProjectId)}`);
|
|
10447
10314
|
const response = await fetch(url.toString(), {
|
|
10448
10315
|
...getFetchConfig(),
|
|
10449
10316
|
method: "PUT",
|
|
@@ -10500,6 +10367,7 @@ function buildDownloadProxyDeps() {
|
|
|
10500
10367
|
getDispatcherForUrl: clientPool.getDispatcherForUrl.bind(clientPool),
|
|
10501
10368
|
fetch: undiciFetch,
|
|
10502
10369
|
logger,
|
|
10370
|
+
isTrustedRedirectHost: isTrustedGitLabRedirectHost,
|
|
10503
10371
|
};
|
|
10504
10372
|
}
|
|
10505
10373
|
/**
|
|
@@ -10561,9 +10429,68 @@ async function startSSEServer() {
|
|
|
10561
10429
|
}
|
|
10562
10430
|
next();
|
|
10563
10431
|
};
|
|
10564
|
-
|
|
10432
|
+
// Session IDs are untrusted map keys (POST /messages?sessionId=). A null
|
|
10433
|
+
// prototype prevents inherited names such as "constructor" from masquerading
|
|
10434
|
+
// as live transports, matching the Streamable HTTP transport.
|
|
10435
|
+
const transports = Object.create(null);
|
|
10436
|
+
const sessionLastActivity = new Map();
|
|
10565
10437
|
let shuttingDown = false;
|
|
10566
|
-
|
|
10438
|
+
// Mirror the Streamable HTTP controls so the SSE transport cannot be used to
|
|
10439
|
+
// exhaust the process: a session cap, a per-IP creation rate limit and an idle
|
|
10440
|
+
// timeout that closes connections which never send anything.
|
|
10441
|
+
const maxSessions = Number.parseInt(process.env.MAX_SESSIONS || "1000", 10);
|
|
10442
|
+
const maxSseConnectionsPerMinute = Number.parseInt(process.env.MAX_REQUESTS_PER_MINUTE || "60", 10);
|
|
10443
|
+
const idleTimeoutMs = Math.max(SESSION_TIMEOUT_SECONDS, 1) * 1000;
|
|
10444
|
+
const sseConnectionRateLimit = rateLimit({
|
|
10445
|
+
windowMs: 60_000,
|
|
10446
|
+
limit: maxSseConnectionsPerMinute,
|
|
10447
|
+
keyGenerator: (req) => ipKeyGenerator(normalizeProxyClientIpForRateLimit(req.ip ?? "")),
|
|
10448
|
+
standardHeaders: true,
|
|
10449
|
+
legacyHeaders: false,
|
|
10450
|
+
handler: (_req, res) => {
|
|
10451
|
+
res.status(429).json({
|
|
10452
|
+
error: "Rate limit exceeded",
|
|
10453
|
+
message: `Maximum ${maxSseConnectionsPerMinute} SSE connections per minute allowed`,
|
|
10454
|
+
});
|
|
10455
|
+
},
|
|
10456
|
+
});
|
|
10457
|
+
/**
|
|
10458
|
+
* Reclaim SSE sessions that have not received a POST /messages request within
|
|
10459
|
+
* the idle timeout.
|
|
10460
|
+
*
|
|
10461
|
+
* On /sse it is correct to idle-close an open GET: the stream alone is not
|
|
10462
|
+
* proof of a live client, so a client that connects and never sends anything
|
|
10463
|
+
* would otherwise hold a MAX_SESSIONS slot forever. This intentionally
|
|
10464
|
+
* differs from the Streamable HTTP GET /mcp handler, which pauses inactivity
|
|
10465
|
+
* expiry while its stream is open so list_changed can still be pushed - do
|
|
10466
|
+
* not copy that rule here.
|
|
10467
|
+
*/
|
|
10468
|
+
const closeIdleSseSessions = () => {
|
|
10469
|
+
const now = Date.now();
|
|
10470
|
+
for (const [sessionId, lastActivity] of sessionLastActivity) {
|
|
10471
|
+
if (now - lastActivity < idleTimeoutMs)
|
|
10472
|
+
continue;
|
|
10473
|
+
const transport = transports[sessionId];
|
|
10474
|
+
delete transports[sessionId];
|
|
10475
|
+
sessionLastActivity.delete(sessionId);
|
|
10476
|
+
logger.info({ sessionId, event: "sse_session_idle_closed" }, `SSE session ${sessionId} closed after ${SESSION_TIMEOUT_SECONDS}s of inactivity`);
|
|
10477
|
+
if (transport) {
|
|
10478
|
+
void transport.close().catch(error => {
|
|
10479
|
+
logger.error({ err: error }, "Error closing idle SSE transport");
|
|
10480
|
+
});
|
|
10481
|
+
}
|
|
10482
|
+
}
|
|
10483
|
+
};
|
|
10484
|
+
const idleSweep = setInterval(closeIdleSseSessions, Math.min(idleTimeoutMs, 60_000));
|
|
10485
|
+
idleSweep.unref();
|
|
10486
|
+
app.get("/sse", rejectDnsRebinding, requireSseAuth, sseConnectionRateLimit, async (req, res) => {
|
|
10487
|
+
if (Object.keys(transports).length >= maxSessions) {
|
|
10488
|
+
res.status(503).json({
|
|
10489
|
+
error: "Server capacity reached",
|
|
10490
|
+
message: `Maximum ${maxSessions} concurrent sessions allowed. Please try again later.`,
|
|
10491
|
+
});
|
|
10492
|
+
return;
|
|
10493
|
+
}
|
|
10567
10494
|
const serverInstance = createServer();
|
|
10568
10495
|
const effectiveHosts = getEffectiveAllowedHosts(req);
|
|
10569
10496
|
const transport = new SSEServerTransport("/messages", res, {
|
|
@@ -10571,16 +10498,38 @@ async function startSSEServer() {
|
|
|
10571
10498
|
allowedHosts: [...effectiveHosts],
|
|
10572
10499
|
allowedOrigins: [...effectiveHosts].flatMap(host => [`http://${host}`, `https://${host}`]),
|
|
10573
10500
|
});
|
|
10574
|
-
|
|
10501
|
+
const sessionId = transport.sessionId;
|
|
10502
|
+
transports[sessionId] = transport;
|
|
10503
|
+
sessionLastActivity.set(sessionId, Date.now());
|
|
10575
10504
|
res.on("close", () => {
|
|
10576
|
-
delete transports[
|
|
10505
|
+
delete transports[sessionId];
|
|
10506
|
+
sessionLastActivity.delete(sessionId);
|
|
10577
10507
|
});
|
|
10578
|
-
|
|
10508
|
+
try {
|
|
10509
|
+
await serverInstance.connect(transport);
|
|
10510
|
+
}
|
|
10511
|
+
catch (error) {
|
|
10512
|
+
logger.error({ err: error, sessionId }, "SSE connection error");
|
|
10513
|
+
// Release the capacity slot immediately instead of waiting for the
|
|
10514
|
+
// client to disconnect or the idle sweep to run.
|
|
10515
|
+
delete transports[sessionId];
|
|
10516
|
+
sessionLastActivity.delete(sessionId);
|
|
10517
|
+
if (!res.headersSent) {
|
|
10518
|
+
res.status(500).json({
|
|
10519
|
+
error: "Internal server error",
|
|
10520
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
10521
|
+
});
|
|
10522
|
+
}
|
|
10523
|
+
else {
|
|
10524
|
+
res.end();
|
|
10525
|
+
}
|
|
10526
|
+
}
|
|
10579
10527
|
});
|
|
10580
10528
|
app.post("/messages", rejectDnsRebinding, requireSseAuth, async (req, res) => {
|
|
10581
10529
|
const sessionId = req.query.sessionId;
|
|
10582
10530
|
const transport = transports[sessionId];
|
|
10583
10531
|
if (transport) {
|
|
10532
|
+
sessionLastActivity.set(sessionId, Date.now());
|
|
10584
10533
|
await transport.handlePostMessage(req, res);
|
|
10585
10534
|
}
|
|
10586
10535
|
else {
|
|
@@ -10589,10 +10538,18 @@ async function startSSEServer() {
|
|
|
10589
10538
|
});
|
|
10590
10539
|
registerDownloadProxy(app, buildDownloadProxyDeps());
|
|
10591
10540
|
app.get("/health", (_, res) => {
|
|
10592
|
-
|
|
10593
|
-
|
|
10541
|
+
const activeSessions = Object.keys(transports).length;
|
|
10542
|
+
const isHealthy = activeSessions < maxSessions;
|
|
10543
|
+
if (!isHealthy) {
|
|
10544
|
+
logger.warn({ activeSessions, maxSessions }, "Health check degraded: active SSE session capacity reached");
|
|
10545
|
+
}
|
|
10546
|
+
res.status(isHealthy ? 200 : 503).json({
|
|
10547
|
+
status: isHealthy ? "healthy" : "degraded",
|
|
10594
10548
|
version: SERVER_VERSION,
|
|
10595
10549
|
transport: TransportMode.SSE,
|
|
10550
|
+
activeSessions,
|
|
10551
|
+
maxSessions,
|
|
10552
|
+
uptime: process.uptime(),
|
|
10596
10553
|
});
|
|
10597
10554
|
});
|
|
10598
10555
|
const httpServer = app.listen(Number(PORT), HOST, () => {
|
|
@@ -10604,6 +10561,7 @@ async function startSSEServer() {
|
|
|
10604
10561
|
return;
|
|
10605
10562
|
shuttingDown = true;
|
|
10606
10563
|
logger.info(`${signal} received, shutting down SSE server...`);
|
|
10564
|
+
clearInterval(idleSweep);
|
|
10607
10565
|
httpServer.close(() => logger.info("SSE HTTP server closed"));
|
|
10608
10566
|
await Promise.allSettled(Object.values(transports).map(async (transport) => {
|
|
10609
10567
|
try {
|