aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/app.test.ts32
-rw-r--r--src/app.ts19
2 files changed, 51 insertions, 0 deletions
diff --git a/src/app.test.ts b/src/app.test.ts
index 274b2d5..8720a23 100644
--- a/src/app.test.ts
+++ b/src/app.test.ts
@@ -358,6 +358,38 @@ describe("wiki", () => {
});
});
+describe("global search", () => {
+ it("should keep the rest of the query around the qualifier", async () => {
+ const res = await app.request(
+ "http://cgithub.example/search?q=" +
+ encodeURIComponent("is:issue repo:actions/deploy-pages label:bug") +
+ "&type=issues",
+ );
+
+ assert.strictEqual(
+ res.headers.get("location"),
+ "/actions/deploy-pages/search?q=is%3Aissue+label%3Abug&type=issues",
+ );
+ });
+
+ it("should redirect searches it cannot scope to a repository", async () => {
+ for (const url of [
+ "http://cgithub.example/search?q=dms&type=code",
+ // An owner-wide search is not a single repository.
+ "http://cgithub.example/search?q=" +
+ encodeURIComponent("org:nix-community dms") +
+ "&type=code",
+ // Without a type there is no search to hand over.
+ "http://cgithub.example/search?q=" + encodeURIComponent("repo:actions/deploy-pages dms"),
+ ]) {
+ const res = await app.request(url);
+
+ assert.strictEqual(res.status, 200, url);
+ assert.match(await res.text(), /url=https:\/\/github.com\/search\?/);
+ }
+ });
+});
+
describe("redirects to GitHub", () => {
// An HTTP redirect would be caught by the extension's declarativeNetRequest
// rules and bounced straight back here.
diff --git a/src/app.ts b/src/app.ts
index a040aaa..7e5e9e8 100644
--- a/src/app.ts
+++ b/src/app.ts
@@ -95,6 +95,25 @@ export function createApp(eta: Eta) {
return c.render("home.eta");
});
+ // GitHub's global search, which we only serve when it is scoped to a single
+ // repository: strip the qualifier and let that repository's search route
+ // decide what to do with the rest.
+ app.get("/search", async (c) => {
+ const q = c.req.query("q") ?? "";
+ const type = c.req.query("type");
+
+ const repoQualifier = /(?:^|\s)repo:([^/\s]+)\/([^/\s]+)(?=\s|$)/.exec(q);
+ if (type && repoQualifier) {
+ const [qualifier, owner, repo] = repoQualifier;
+ const rest =
+ q.slice(0, repoQualifier.index) + q.slice(repoQualifier.index + qualifier.length);
+ const params = new URLSearchParams({ q: rest.trim(), type });
+ return c.redirect(`/${owner}/${repo}/search?${params}`);
+ }
+
+ return redirectToGitHub(c);
+ });
+
app.get("/:owner", async (c) => {
const { owner } = c.req.param();
return c.render("owner.eta", await getGitHubOwner(owner));