Skip to content

Commit b0c0003

Browse files
committed
fix(gitlab): allow issue-only permission exports and verify pacing
1 parent fb9d890 commit b0c0003

2 files changed

Lines changed: 15 additions & 6 deletions

File tree

‎apps/docs/public/static/examples/gitlab/export-permissions.py‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,6 @@ def export(client, project):
9797
or not all(char.isalnum() or char in "_.-" for char in part)
9898
for part in project_path.split("/"))):
9999
raise ExportError("GitLab returned an invalid project path.")
100-
if record.get("repository_access_level") == "disabled":
101-
raise ExportError("The project's repository is disabled.")
102100
users, emails, seen_members = {}, {}, set()
103101
today = datetime.now(timezone.utc).date()
104102
page = 1

‎apps/docs/scripts/test-gitlab-export-e2e.py‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import sys
1212
import tempfile
1313
import threading
14+
import time
1415
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
1516
from urllib.parse import parse_qs, urlsplit
1617

@@ -34,7 +35,7 @@ def log_message(self, *_args):
3435
def do_GET(self):
3536
parsed = urlsplit(self.path)
3637
scenario, endpoint = parsed.path.lstrip("/").split("/api/v4/", 1)
37-
self.calls.append((scenario, endpoint, parse_qs(parsed.query)))
38+
self.calls.append((scenario, endpoint, parse_qs(parsed.query), time.monotonic()))
3839
if self.headers.get("PRIVATE-TOKEN") != TOKEN:
3940
self.reply(401, {"message": "Missing token"})
4041
return
@@ -46,7 +47,8 @@ def do_GET(self):
4647
self.reply(302, {}, {"Location": "/redirect/api/v4/trap"})
4748
else:
4849
self.reply(200, {"id": 91, "path_with_namespace": "engineering/platform",
49-
"repository_access_level": "enabled"})
50+
"repository_access_level": "disabled" if scenario == "issues-only" else "enabled",
51+
"issues_access_level": "enabled", "wiki_access_level": "enabled"})
5052
elif endpoint == "projects/91/members/all":
5153
page = parse_qs(parsed.query).get("page", ["1"])[0]
5254
if scenario == "page-failure" and page == "2":
@@ -114,6 +116,7 @@ def run():
114116
try:
115117
for scenario, expected_error in [
116118
("success", None), ("numeric-project", None), ("missing-pagination", None),
119+
("issues-only", None), ("paced", None),
117120
("hidden-email", "email"), ("duplicate-email", "email"),
118121
("wrong-identity", "identity"), ("denied", "403"),
119122
("page-failure", "500"), ("pagination-loop", "pagination"),
@@ -128,7 +131,7 @@ def run():
128131
f"https://127.0.0.1:{server.server_port}/{scenario}",
129132
"--project", "91" if scenario == "numeric-project" else "engineering/platform",
130133
"--output-dir", str(output),
131-
"--requests-per-minute", "600000"]
134+
"--requests-per-minute", "120" if scenario == "paced" else "600000"]
132135
result = subprocess.run(command, capture_output=True, text=True, timeout=20,
133136
env={**os.environ, "GITLAB_EXPORT_TOKEN": TOKEN,
134137
"SSL_CERT_FILE": str(cert)})
@@ -169,8 +172,16 @@ def run():
169172
"subjects": sorted("u:" + row[1] for row in expected),
170173
"otherProject": [],
171174
}, parsed.stdout
175+
if scenario == "paced":
176+
arrivals = [arrived for case, _, _, arrived in GitLabFixture.calls if case == scenario]
177+
# The first request's transit time can consume one pacing interval.
178+
minimum_span = (len(arrivals) - 2) * 0.5
179+
assert arrivals[-1] - arrivals[0] >= minimum_span, "Requests exceeded the configured rate"
180+
report["pacing"] = {"requests": len(arrivals), "requestsPerMinute": 120,
181+
"observedSeconds": arrivals[-1] - arrivals[0],
182+
"minimumSeconds": minimum_span}
172183
report["checks"].append({"name": scenario, "passed": True})
173-
assert not any(endpoint == "trap" for _, endpoint, _ in GitLabFixture.calls)
184+
assert not any(endpoint == "trap" for _, endpoint, _, _ in GitLabFixture.calls)
174185
report["passed"] = True
175186
finally:
176187
server.shutdown()

0 commit comments

Comments
 (0)