mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-11-02 13:12:50 +00:00
fix(jobdanmark-search): filter search by the administrative regions locations returns (#650)
* fix(jobdanmark-search): filter search by the administrative regions locations returns `locations` lists two kinds of region: landsdele such as Østjylland (category `region`) and the administrative regions such as Region Midtjylland (category `administrativeRegion`), and the search API filters each by its own type. `--region` always sent type `region`, so an administrative region - and SKILL.md's own `--region Midtjylland` example - returned a successful search with zero jobs. Add `--administrative-region`, sent as the `administrativeRegion` filter, map each locations category to its flag in SKILL.md and the README, and give `--region` examples that exist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * changelog: place the #650 entry under [Unreleased] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Mads Lorentzen <madslorentzen_17@hotmail.com>
This commit is contained in:
5 files changed
+101
-5
No files matched your search
@@ -52,7 +52,8 @@ Key flags:
|
||||
- `--jobtitle-id <id>` — job title ID from `autocomplete` results
|
||||
- `--municipality <name>` — e.g. `Odense`, `København`, `Aarhus`
|
||||
- `--zip <code>` — zip code, e.g. `5000`, `8000`, `2100`
|
||||
- `--region <name>` — region name, e.g. `Midtjylland`, `Sjælland`
|
||||
- `--region <name>` — landsdel (`locations` category `region`), e.g. `Sjælland`, `Østjylland`, `Fyn`
|
||||
- `--administrative-region <name>` — one of the five regions (`locations` category `administrativeRegion`), e.g. `Region Midtjylland`
|
||||
- `--job-type <types>` — comma-separated: `fuldtid`, `deltid`, `fleksjob`, `elev`, `studiejob`, `praktik`
|
||||
- `--page <n>` — page number (30 items per page, server-enforced)
|
||||
- `--limit <n>` — cap total results returned by CLI
|
||||
@@ -91,7 +92,7 @@ Use this to resolve search terms into precise job title IDs (`--jobtitle-id`) or
|
||||
bun run .agents/skills/jobdanmark-search/cli/src/cli.ts locations --query "<text>" [--limit <n>]
|
||||
```
|
||||
|
||||
Returns matching municipalities, zip codes, and regions. Use `value` from results as `--municipality` or `--zip` in `search`.
|
||||
Returns matching municipalities, zip codes, and regions. Pass an item's `value` to the `search` flag named by its `category`: `municipality` → `--municipality`, `zip` → `--zip`, `region` → `--region`, `administrativeRegion` → `--administrative-region`. A value under the wrong flag matches no jobs.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -79,7 +79,8 @@ bun run src/cli.ts search [flags]
|
||||
| `--jobtitle-id` | number | — | Job title ID from `autocomplete` results |
|
||||
| `--municipality` | string | — | Municipality name, e.g. `Odense`, `København` |
|
||||
| `--zip` | string | — | Zip code, e.g. `5000` |
|
||||
| `--region` | string | — | Region name |
|
||||
| `--region` | string | — | Landsdel: a `locations` item with category `region`, e.g. `Sjælland`, `Østjylland` |
|
||||
| `--administrative-region` | string | — | Administrative region: a `locations` item with category `administrativeRegion`, e.g. `Region Midtjylland` |
|
||||
| `--job-type` | string | — | Comma-separated job types: `fuldtid,deltid,fleksjob,elev,studiejob,praktik` |
|
||||
| `--page` | number | `1` | Page number (30 items per page, server-enforced) |
|
||||
| `--limit` | number | — | Cap total results returned by CLI (client-side) |
|
||||
@@ -420,7 +421,7 @@ bun run src/cli.ts locations --query "Sjælland"
|
||||
]
|
||||
```
|
||||
|
||||
> **Note**: Returns `[]` when no locations match. The `value` field for municipalities is the display name (string); for zip codes it is the numeric string. Use `value` directly as `--municipality` or `--zip` in `search`.
|
||||
> **Note**: Returns `[]` when no locations match. The `value` field for municipalities is the display name (string); for zip codes it is the numeric string. Pass `value` to the `search` flag matching its `category`: `--municipality`, `--zip`, `--region` (category `region`, a landsdel such as `Sjælland`) or `--administrative-region` (category `administrativeRegion`, such as `Region Midtjylland`). Under any other flag the value matches no jobs.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -105,7 +105,10 @@ export const search = defineCommand({
|
||||
description: "Zip code, e.g. 5000",
|
||||
}),
|
||||
region: option(z.string().optional(), {
|
||||
description: "Region name",
|
||||
description: "Landsdel from locations (category region), e.g. Sjælland, Østjylland",
|
||||
}),
|
||||
"administrative-region": option(z.string().optional(), {
|
||||
description: "Administrative region from locations (category administrativeRegion), e.g. Region Midtjylland",
|
||||
}),
|
||||
"job-type": option(z.string().optional(), {
|
||||
description: "Comma-separated job types: fuldtid,deltid,fleksjob,elev,studiejob,praktik",
|
||||
@@ -143,6 +146,12 @@ export const search = defineCommand({
|
||||
if (flags.region) {
|
||||
filters.push({ type: "region", value: flags.region, displayText: flags.region })
|
||||
}
|
||||
// `locations` returns two kinds of region, and the API filters each by its
|
||||
// own type: a "region" filter holding "Region Midtjylland" matches no job.
|
||||
if (flags["administrative-region"]) {
|
||||
const value = flags["administrative-region"]
|
||||
filters.push({ type: "administrativeRegion", value, displayText: value })
|
||||
}
|
||||
|
||||
const jobTypes = flags["job-type"]
|
||||
? flags["job-type"].split(",").map((t) => t.trim()).filter(Boolean)
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
// Both region kinds exactly as GET /api/search/locations?q=jylland returns them.
|
||||
const LOCATIONS = [
|
||||
{
|
||||
title: "Landsdel",
|
||||
items: [{ id: "region__218546", text: "Østjylland", value: "Østjylland", category: "region", slug: "oestjylland" }],
|
||||
},
|
||||
{
|
||||
title: "Region",
|
||||
items: [{
|
||||
id: "administrativeRegion__389101",
|
||||
text: "Region Midtjylland",
|
||||
value: "Region Midtjylland",
|
||||
category: "administrativeRegion",
|
||||
slug: "region-midtjylland",
|
||||
}],
|
||||
},
|
||||
];
|
||||
|
||||
async function searchFilters(args: string[]) {
|
||||
const bodies: unknown[] = [];
|
||||
const server = Bun.serve({
|
||||
hostname: "127.0.0.1",
|
||||
port: 0,
|
||||
async fetch(request) {
|
||||
bodies.push(await request.json());
|
||||
return Response.json({ items: [], currentPage: 1, totalItems: 0, itemsPrPage: 30, totalPages: 0 });
|
||||
},
|
||||
});
|
||||
const dir = mkdtempSync(join(tmpdir(), "jobdanmark-region-"));
|
||||
const preload = join(dir, "fetch.ts");
|
||||
writeFileSync(preload, `
|
||||
const originalFetch = globalThis.fetch;
|
||||
globalThis.fetch = (input, init) => {
|
||||
const url = new URL(input instanceof Request ? input.url : String(input));
|
||||
if (url.protocol === "data:") return originalFetch(input, init);
|
||||
if (url.origin !== "https://jobdanmark.dk" || url.pathname !== "/api/jobsearch/search/1") {
|
||||
throw new Error("Unexpected search fixture request");
|
||||
}
|
||||
return originalFetch(${JSON.stringify(server.url.origin)} + url.pathname, init);
|
||||
};
|
||||
`);
|
||||
try {
|
||||
const proc = Bun.spawn([
|
||||
process.execPath, "run", "--preload", preload,
|
||||
join(import.meta.dir, "../src/cli.ts"), "search", ...args,
|
||||
], { stdout: "pipe", stderr: "pipe" });
|
||||
const [stderr, exitCode] = await Promise.all([new Response(proc.stderr).text(), proc.exited]);
|
||||
expect(stderr).toBe("");
|
||||
expect(exitCode).toBe(0);
|
||||
expect(bodies).toHaveLength(1);
|
||||
return (bodies[0] as { filters: Array<{ type: string; value: string; displayText: string }> }).filters;
|
||||
} finally {
|
||||
server.stop(true);
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
const item = (category: string) => LOCATIONS.flatMap((g) => g.items).find((i) => i.category === category)!;
|
||||
|
||||
test("an administrativeRegion location is sent with the API's administrativeRegion filter type", async () => {
|
||||
const { value } = item("administrativeRegion");
|
||||
const filters = await searchFilters(["--administrative-region", value]);
|
||||
expect(filters).toEqual([{ type: "administrativeRegion", value, displayText: value }]);
|
||||
});
|
||||
|
||||
test("a region (landsdel) location keeps the region filter type", async () => {
|
||||
const { value } = item("region");
|
||||
const filters = await searchFilters(["--region", value]);
|
||||
expect(filters).toEqual([{ type: "region", value, displayText: value }]);
|
||||
});
|
||||
@@ -146,6 +146,16 @@ per-file diff commands.
|
||||
API's `occupations` parameter returns the 830 nurse ads for the same UUID. The new flag sends that
|
||||
parameter and accepts the full URI or the bare UUID; the docs stop pointing at the numeric flags.
|
||||
|
||||
- **`jobdanmark-search search` can filter by the administrative regions that `locations` returns**
|
||||
(#650, `.agents/skills/jobdanmark-search/cli/src/commands/search.ts`, `tests/region-filter-cli.test.ts`,
|
||||
`SKILL.md`, `cli/README.md`) - `locations` lists two kinds of region: landsdele such as `Østjylland`
|
||||
(category `region`) and the five administrative regions such as `Region Midtjylland` (category
|
||||
`administrativeRegion`), and the search API filters each by its own type. `--region` always sent
|
||||
type `region`, so an administrative region - and SKILL.md's own example `--region Midtjylland` - came
|
||||
back as a successful search with zero jobs. A new `--administrative-region` flag sends the
|
||||
`administrativeRegion` filter (2,486 jobs for `Region Midtjylland`), and the docs map each `locations`
|
||||
category to its flag and give `--region` examples that exist.
|
||||
|
||||
## [1.7.3] - 2026-10-19
|
||||
|
||||
### Added
|
||||
|
||||
Reference in new issue
Block a user