directory

command module
v0.0.0-...-c6bc86c Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 9, 2026 License: AGPL-3.0 Imports: 0 Imported by: 0

README

Directory service

OpenAPI specification

The bundled specification defined in api.yaml is available at GET /directory/openapi.yaml (application/yaml) and GET /directory/openapi.json (application/json). No Directory role is required.

curl http://localhost:8086/directory/openapi.yaml
curl http://localhost:8086/directory/openapi.json

Both endpoints return the complete specification, including references and extensions. YAML comments and source formatting are not preserved. The path selects the representation; query parameters and the Accept header do not change it.

Catalog coverage year filtering

Set catalogConfig.queryConfig.year to dc.date = {term} for CQL or @attr 1=30 {term} for PQF to enable publication year filtering. There is no default: an omitted or empty template disables both filtering and validation. When enabled, a supplied ISO18626 publicationInfo.publicationDate must be exactly four ASCII digits (YYYY); other formats return a lookup error. An absent date leaves queries unchanged. The year clause is ANDed with each identifier, ISBN, ISSN, or title lookup, never searched by itself.

For the GVI marc21plus1 service, the server filters individual MARC 924 holdings. CrossLink does not additionally interpret holdings date ranges. The template can also be used with other catalogs supporting coverage queries.

Local database

Start a temporary PostgreSQL server and create the Directory database:

docker run --name crosslink-directory-postgres --rm -d \
  -e POSTGRES_PASSWORD=directory -p 54322:5432 postgres
until docker exec crosslink-directory-postgres pg_isready -U postgres; do sleep 1; done
PGPASSWORD=directory psql -p 54322 -U postgres -h localhost \
  -c 'create database directory;'

Run the service with a matching connection string. Database migrations are applied automatically during startup:

DATABASE_URL=postgresql://postgres:directory@localhost:54322/directory make run

Import

POST /directory/import loads complete directory entries, tiers, and networks from a newline-delimited JSON (NDJSON) stream. It requires the directory.consortium.all permission. Like the broker import API, it processes each record in its own transaction and supports fail, skip, and update conflict policies.

The export-crosslink-directory.sql script exports a mod-rs tenant's directory as NDJSON accepted by this endpoint. It sets illConfig.isPickupLocation for entries with the legacy pickup tag, independently of their LMS location code or NCIP/ISO18626 configuration. Untagged entries are not designated as pickup locations. The script maps each entry's policy.ill.InstitutionalLoanToBorrowRatio custom text property to lendToBorrowRatio, preserving the ratio string. Missing properties export as null. Invalid ratios or multiple matching properties stop the export before NDJSON output; the error identifies the affected entries.

The optional lendToBorrowRatio field expresses desired loans:borrows (for example, 50:2). Each component must be positive, with one to four integer digits and at most two decimal places (0.01 through 9999.99). Leading zeros count toward the four integer digits; the whole string is at most 15 characters. These limits apply to entry creation, updates, and imports. Existing ratios outside these limits must be corrected before applying migration 016; the migration validates existing entries without changing their ratios.

The optional illConfig.loadBalancingPolicy accepts deficit or proportional. The broker reads this setting from the entry identified by CONSORTIUM_SYMBOL. An omitted or null policy, missing illConfig, or an unset consortium symbol uses proportional. Requester settings do not select the policy. PATCH omission preserves the current value; explicit null clears it. Imports also accept this optional field.

With a lender's desired loans:borrows ratio, deficit scores actualBorrows * (desiredLoans / desiredBorrows) - actualLoans and prioritizes higher scores. proportional scores (actualLoans / max(actualBorrows, 1)) / (desiredLoans / desiredBorrows) and prioritizes lower scores. Other rota priorities still take precedence.

Before adding migrations, check current main for the next available version. Directory startup and integration test setup stop if migration initialization or application fails. The load-balancing migrations follow 014_catalog_query_year: ratio in 015, ratio bounds in 016, and policy in 017. These numbers apply to databases following the mainline history. Persistent preview databases that already applied the earlier branch numbering need schema and migration-history reconciliation before upgrading; disposable test databases can be recreated.

Request format and sample data

Each record has type, key, and data fields. Put one complete JSON object on each physical line, without an enclosing array. Blank lines are ignored.

Type Key Data
entry Entry UUID Complete entry fields, owned collections, and configurations
tier Consortium UUID and tier name level, type, cost, and entry UUIDs
network Consortium UUID and network name reciprocal and entry UUIDs with individual priority values

Symbol authorities and values are trimmed and uppercased. Entry UUIDs are the stable import identity; symbols are metadata. Referenced entries must already exist or have been imported by an earlier successful record: put consortiums before institutions, institutions before branches, and members before tiers and networks. The same applies to references in illConfig.lendersOfLastResort. An entry UUID cannot appear twice in a tier or network's membership list.

Save the following as directory.ndjson. This example assumes an empty directory; only one consortium entry is allowed. For an existing directory, use its consortium UUID and choose the appropriate conflict policy.

{"type":"entry","key":"00000000-0000-0000-0000-000000000001","data":{"name":"Example consortium","type":"Consortium","parent":null,"description":null,"organizationId":null,"contactName":null,"email":null,"fromEmail":null,"tenant":null,"vendor":null,"phoneNumber":null,"lmsLocationCode":null,"hrid":null,"timeZone":null,"symbols":[{"authority":"ISIL","symbol":"EXAMPLE-CON"}],"endpoints":[],"addresses":[],"closures":[],"lmsConfig":null,"catalogConfig":null,"illConfig":null,"holdingsPolicy":null}}
{"type":"entry","key":"00000000-0000-0000-0000-000000000002","data":{"name":"Example library","type":"Institution","parent":"00000000-0000-0000-0000-000000000001","description":null,"organizationId":null,"contactName":null,"email":null,"fromEmail":null,"tenant":null,"vendor":null,"phoneNumber":null,"lmsLocationCode":null,"hrid":null,"timeZone":null,"symbols":[{"authority":"ISIL","symbol":"EXAMPLE-LIB"}],"endpoints":[],"addresses":[],"closures":[],"lmsConfig":null,"catalogConfig":null,"illConfig":null,"holdingsPolicy":null}}
{"type":"tier","key":{"consortium":"00000000-0000-0000-0000-000000000001","name":"Standard loan"},"data":{"level":"standard","type":"loan","cost":0,"entries":["00000000-0000-0000-0000-000000000002"]}}
{"type":"network","key":{"consortium":"00000000-0000-0000-0000-000000000001","name":"Main network"},"data":{"reciprocal":true,"entries":[{"entry":"00000000-0000-0000-0000-000000000002","priority":1}]}}

Entry imports require every field shown, including explicit null values for absent optional values or configurations and empty arrays for empty collections. Non-null configuration objects also have required fields. Unknown fields are rejected; see the ImportEntryRecord, ImportTierRecord, and ImportNetworkRecord schemas in api.yaml for the complete contract. Entry database IDs are supplied by the import key; this allows mod-rs UUIDs to be preserved.

Complete imports include raw host settings: lmsConfig.vendor, ncipNamespaceEnabled, bibIdNormalization, catalogConfig.profile, MARC availability predicates, and OPAC parser options. These fields are required in their respective non-null configuration objects; use null to inherit defaults. Explicit false and empty arrays are preserved. Imports replace stored configuration, so include all overrides when restoring an entry. Configure at most one SRU/ZOOM endpoint and at most one holdings parser.

Import with curl

For the local service started above:

curl --fail-with-body \
  -X POST \
  -H 'Content-Type: application/x-ndjson' \
  -H 'X-Okapi-Permissions: ["directory.consortium.all"]' \
  --data-binary @directory.ndjson \
  'http://localhost:8086/directory/import?conflictPolicy=fail'

Use --data-binary to preserve line boundaries. The permissions header above is for direct local access. When accessing the service through a gateway, use that deployment's authentication and tenant headers with an account granted directory.consortium.all.

Conflict policies

Set conflictPolicy in the URL to fail, skip, or update. All three policies create a resource when its key does not exist. For an existing key:

Policy Behavior
fail (default) Leaves the resource unchanged, increments failed, and adds an error detail. Continues with later records.
skip Leaves the resource unchanged, increments skipped, and adds a diagnostic to errors.
update Replaces the resource's data, preserves its root database ID, and increments imported.

An entry matches by its key UUID; a tier or network matches by the resolved consortium UUID and exact name. For example, reimporting the sample with skip leaves all four resources unchanged. With update, changing the network's priority to 5 replaces that library's priority with 5.

Updates synchronize complete aggregates, rather than patching individual fields. For entries, submitted symbols, endpoints, addresses, closures, and configurations replace the existing values; owned child IDs can change. An empty collection removes its existing contents, and a null configuration removes that configuration. Entry imports do not change tier or network memberships; import the corresponding tier or network to replace its full membership list. For example, updating a network with "entries":[] removes all its memberships. Validation still applies under every policy, and a failed record leaves that record's existing data unchanged.

Response and limits

A completed stream returns HTTP 200 even if some records failed or were skipped. The initial sample import returns:

{
  "entries": {"imported": 2, "failed": 0, "skipped": 0},
  "tiers": {"imported": 1, "failed": 0, "skipped": 0},
  "networks": {"imported": 1, "failed": 0, "skipped": 0},
  "errors": [],
  "errorsOmitted": 0
}

Always inspect the counters and errors, even when curl succeeds. Error details include line, error, and, when available, type and key. Here, line is the one-based nonblank record number, not the physical line number. At most 1,000 error or skip details are retained; errorsOmitted counts additional details.

Status Meaning
400 Missing body, unsupported conflict policy, or incorrect content type
401 Missing consortial-admin permission
413 Request exceeds 1 GiB or a record exceeds 1 MiB
500 Import stream could not be read

Record-level errors do not stop later records. Fatal stream errors stop the import, but records already committed remain committed, including when the response is HTTP 413 or 500.

Build and test

The SQLC and OpenAPI generator versions are pinned as Go tools in go.mod. Generate the database and API sources with:

make generate

Generated Go sources are build artifacts and are not stored in the repository. The standard build, test, lint, and run targets generate them automatically:

make all
make check
make lint
make run

Run make generate before invoking go build or go test directly.

Environment variables

Name Description Default value
HOST Address on which the HTTP server listens localhost
HTTP_PORT Port on which the HTTP server listens 8086
DATABASE_URL PostgreSQL connection string used by the service and database migrations postgresql://postgres:directory@localhost:54322/directory
LOG_LEVEL Log level: debug, info, warn, or error info
LOG_FORMAT Log output format; set to json for structured JSON logs text

Host integration profiles

See Host LMS and catalog profiles for lmsConfig.vendor, catalogConfig.profile, precedence, parser overrides, diagnostics and migration.

Pickup locations

Set illConfig.isPickupLocation to true to offer an institution or branch in the request form's pickup-location selector. This flag is independent of lmsConfig.requesterPickupLocation: manual workflows can offer locations without an LMS code. Migration 012 enables the flag for existing entries whose requester pickup code is non-null (including empty strings), preserving other ILL settings. Other entries remain unselected unless explicitly enabled. Imports can set the flag in illConfig.

The broker accepts the requesting institution's UUID or a descendant's UUID. An explicit selection supplies that entry's shipping address and, when needed, its LMS pickup code. Omitting the selection retains existing defaults. LMS operations that consume a pickup code still require one for explicit selections.

Filter Directory entries with CQL isPickupLocation=true or isPickupLocation=false. Missing flags (including entries without ILL configuration) match false. The former requesterPickupLocation CQL filter is no longer supported.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
cmd
directory command
import
db

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL