fix(portal-clis): emit the contract's id field from jobnet and jobdanmark search results (#518)

/add-portal's contract says every search result carries at least id,
title, company, location, date, url, and the four other shipped CLIs
emit id. When #340 added the company/location/date/url aliases to these
two, id was left out: jobnet exposed the value only as jobAdId,
jobdanmark only as slug, so a consumer reading every portal's JSON
through one shape had to special-case both to call detail.

Purely additive: id equals jobAdId on jobnet and slug on jobdanmark, the
native keys stay, nothing is renamed. Each SKILL.md notes the alias. The
additive-contract test in each CLI now asserts id and its equality with
the native key; both fail on master.
This commit is contained in:
Ayobami Adegoke authored and GitHub committed 2026-10-05 19:27:43 +02:00
1 parent e9115b02f8
commit 895c02191d
7 files changed
+30 -4

No files matched your search

+1 -1
View File
@@ -224,6 +224,6 @@ All errors are written to **stderr** as `{ "error": "...", "code": "..." }` and
- All data is from the public Jobdanmark.dk API — no credentials required.
- Pagination is 1-indexed (`--page 1` is the first page). 30 items per page, server-enforced.
- The `detail` command fetches the HTML job page and parses embedded JSON-LD when available, with a rendered-HTML fallback for pages that omit structured data. It does not use a separate JSON API.
- `slug` in search results is extracted from the API's relative `url` field (the path after `/job/`).
- `slug` in search results is extracted from the API's relative `url` field (the path after `/job/`); it is also emitted as `id`, the portal-contract name every search CLI shares.
- `applicationDeadline` in search results can be `null` (no deadline set).
- Job type values for filters: `fuldtid`, `deltid`, `fleksjob`, `elev`, `studiejob`, `praktik`.
@@ -74,6 +74,10 @@ export function normalizeItem(item: ApiSearchItem): Record<string, unknown> {
applicationDeadline: item.applicationDeadline ?? null,
url: fullUrl,
slug,
// /scrape contract aliases (add-portal.md: every result carries at least
// id, title, company, location, date, url). `id` is the slug - the value
// `detail` takes - under the name every other portal CLI uses.
id: slug,
company: item.companyName,
location: extractCity(item.companyAddress),
date: toContractDate(item.publishedDate),
@@ -20,16 +20,19 @@ function item(): ApiSearchItem {
}
describe("Jobdanmark search normalization", () => {
test("additively emits the /scrape contract fields (company, location, date, deadline)", () => {
test("additively emits the /scrape contract fields (id, company, location, date, deadline, url)", () => {
const result = normalizeItem(item());
expect(result).toMatchObject({
id: "softwareudvikler-til-statens-it",
company: "Statens It",
location: "Ballerup",
date: "2026-07-27",
deadline: "2026-08-17",
url: "https://jobdanmark.dk/job/softwareudvikler-til-statens-it",
});
// `id` is the contract name for the value `detail <slug>` consumes.
expect(result.id).toBe(result.slug);
});
test("maps a missing address zip and a null deadline to null", () => {