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:
authored and GitHub committed 2026-10-19 20:03:09 +02:00
1 parent a869a5a2dc
commit 09a8c31b24
5 files changed
+101 -5

No files matched your search

+3 -2
View File
@@ -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 }]);
});
+10
View File
@@ -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