Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,14 @@ uv run --project etl/tools ltlod-reconcile <admin-units|persons> --input … --o

make up # deploy LinkedDataHub at https://localhost:4443/ (root Makefile;
# bootstraps secrets + server cert, then docker compose up -d)
make install # one-time: PUT app/ scaffolding (root + containers + taxonomy
# schemes) via LDH CLI; needs ../LinkedDataHub checkout (LDH_HOME=…)
make install # set up the dataspace via LDH CLI: make it public + PUT app/
# scaffolding (root + containers + taxonomy schemes) + install
# app/ns.ttl (1:N views) into the admin ontologies/namespace/
# doc; needs ../LinkedDataHub (LDH_HOME=…).
# Interactive, LinkedDataHub-Apps style: prompts for Base URL /
# cert / password / proxy, defaults = the local stack (Enter×4
# or `printf '\n\n\n\n' | make install`); enter another Base URL
# + owner cert to install onto any LDH instance
make load # bulk-load datasets/current/*/*.trig into fuseki-end-user TDB2;
# regenerate with `make -C etl` first (committed data has prod base);
# ends with `make public` (anonymous read, LDH make-public.sh equivalent)
Expand Down Expand Up @@ -66,6 +72,16 @@ merge on load). Unmatched entities go to `cache/unmatched*.csv`, never force-mat
container, PUT via LDH CLI by `make install`); each carries an
`rdf:_1 <#select-children>` → `ldh:Object`/`ldh:ChildrenView` block, without
which LDH renders no children listing at all.
- **1:N entity views**: cross-entity listings (county → municipalities, committee
→ members, party → nominees) are `ldh:inverseView` definitions in `app/ns.ttl`
(the LDH namespace ontology, northwind-traders style): `<property>
ldh:inverseView <ldh:View>` + `spin:query` → `sp:Select` with `$about`. LDH
shows a view on every instance whose `rdf:type` matches the property's declared
`rdfs:range` — exact type match, no subsumption, so view targeting relies on
discriminating types in the data (`schema:PoliticalParty` for parties,
`cv:PublicOrganisation` for the Seimas). `app/import-ns.sh` (called by `make
install`) resets + POSTs the ontology into the admin `ontologies/namespace/`
document (served at `{base}ns`) and evicts the server-side ontology cache.
- **Vocabulary cascade**: W3C specs first → domain-specific third-party vocabs
(EU SEMIC, OP authority tables, FOAF) → schema.org as general fallback → custom
(`http://linkeddata.lt/ns#`) last. Rationale per domain: `etl/ONTOLOGY-NOTES.md`.
Expand Down Expand Up @@ -122,14 +138,20 @@ merge on load). Unmatched entities go to `cache/unmatched*.csv`, never force-mat
adds `dh:Item` + `sioc:has_container` otherwise, plus `dct:created`/
`acl:owner`) — never put sioc triples in `app/*.ttl`. `make install` is
idempotent: PUT replaces the whole named graph.
- **Public read access is class-based**: `make public` grants
- **Public read access is class-based**: `make public` (direct-to-fuseki) and
`make install` (LDH CLI `make-public.sh`, works remotely) grant the same
`acl:accessToClass def:Root, dh:Container, dh:Item, nfo:FileDataObject` —
ETL documents match because mappings type them `dh:Item`/`dh:Container`.
(Untyped docs would also pass: LDH's ACL query leaves `$Type` unbound when
a document has no `rdf:type`, matching any `acl:accessToClass` — see
`AuthorizationFilter` + `aclQuery` in LDH web.xml.)
- `COMPOSE_PROJECT_NAME=ltlod` isolates container/volume names from other local
LDH stacks, but ports 81/4443/5443 still clash — one stack at a time.
- **502 on all public endpoints after restarting backend containers** (fuseki,
varnish): nginx resolves upstream container IPs at startup — restart nginx
too. `fuseki-end-user` can be OOM-killed (exit 137) under memory pressure
when other Docker workloads run; `docker compose up -d fuseki-end-user`
revives it (LDH health recovers on its own).

## Verification

Expand Down
50 changes: 38 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,47 @@ stop:
logs:
docker compose logs -f linkeddatahub

# Create/update the container scaffolding (root + containers + taxonomy scheme
# docs from app/) via LDH CLI PUTs. Run once after `make up`; re-running is
# safe (PUT replaces). Order: make up -> make install -> make load.
# Install the app structure (root + containers + taxonomy schemes + the
# namespace ontology with 1:N views) onto a LinkedDataHub instance via LDH CLI
# PUTs. Interactive, LinkedDataHub-Apps style: prompts for the target instance
# with defaults from the local docker-compose stack (.env, ssl/, secrets/) —
# press Enter to install locally, or enter another Base URL + owner cert to
# install on any LDH instance. Re-running is safe (PUT replaces). Local order:
# make up -> make install -> make load.
install:
@[ -d "$(LDH_HOME)/bin" ] || \
{ echo "ERROR: LDH CLI not found — clone https://github.com/AtomGraph/LinkedDataHub to $(LDH_HOME) or pass LDH_HOME=…"; exit 1; }
@[ -n "$$(docker compose ps -q linkeddatahub)" ] || \
{ echo "ERROR: linkeddatahub container not found — run 'make up' first."; exit 1; }
@echo "Waiting for LinkedDataHub health (first-boot seeding must finish)..."
@until [ "$$(docker inspect -f '{{.State.Health.Status}}' $$(docker compose ps -q linkeddatahub))" = "healthy" ]; do \
sleep 5; echo " ...waiting"; \
done
PATH="$$(find "$$(cd $(LDH_HOME) && pwd)/bin" -type d | tr '\n' ':')$(JENA_HOME)/bin:$$PATH" \
./app/install.sh "$(BASE_URI)" ssl/owner/cert.pem \
"$$(cat secrets/owner_cert_password.txt)" "$(PROXY_URI)"
@read -p "Enter Base URL [$(BASE_URI)]: " BASE_URL; \
BASE_URL=$${BASE_URL:-$(BASE_URI)}; \
read -p "Enter Certificate Path [ssl/owner/cert.pem]: " CERT_PATH; \
CERT_PATH=$${CERT_PATH:-ssl/owner/cert.pem}; \
[ -f "$$CERT_PATH" ] || { echo "ERROR: certificate not found: $$CERT_PATH"; exit 1; }; \
PW_DEFAULT=""; \
[ -f secrets/owner_cert_password.txt ] && PW_DEFAULT="$$(cat secrets/owner_cert_password.txt)"; \
if [ -n "$$PW_DEFAULT" ]; then \
read -r -s -p "Enter Certificate Password [from secrets/owner_cert_password.txt]: " PASSWORD; \
else \
read -r -s -p "Enter Certificate Password (required): " PASSWORD; \
fi; \
echo ""; \
PASSWORD=$${PASSWORD:-$$PW_DEFAULT}; \
if [ -z "$$PASSWORD" ]; then echo "Password cannot be empty. Aborting."; exit 1; fi; \
PROXY_DEFAULT=""; \
[ "$$BASE_URL" = "$(BASE_URI)" ] && PROXY_DEFAULT="$(PROXY_URI)"; \
read -p "Enter Proxy URL (optional) [$$PROXY_DEFAULT]: " PROXY_URL; \
PROXY_URL=$${PROXY_URL:-$$PROXY_DEFAULT}; \
if [ "$$BASE_URL" = "$(BASE_URI)" ] && [ -n "$$(docker compose ps -q linkeddatahub 2>/dev/null)" ]; then \
echo "Waiting for LinkedDataHub health (first-boot seeding must finish)..."; \
until [ "$$(docker inspect -f '{{.State.Health.Status}}' $$(docker compose ps -q linkeddatahub))" = "healthy" ]; do \
sleep 5; echo " ...waiting"; \
done; \
fi; \
export PATH="$$(find "$$(cd $(LDH_HOME) && pwd)/bin" -type d | tr '\n' ':')$(JENA_HOME)/bin:$$PATH"; \
if [ -n "$$PROXY_URL" ]; then \
./app/install.sh "$$BASE_URL" "$$CERT_PATH" "$$PASSWORD" "$$PROXY_URL"; \
else \
./app/install.sh "$$BASE_URL" "$$CERT_PATH" "$$PASSWORD"; \
fi

# Bulk-load datasets/current/*/*.trig into the end-user TDB2 store. APPEND-ONLY:
# clean rebuild = `make down && rm -rf fuseki/end-user && make up && make load`.
Expand Down
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@ du Fuseki, Varnish kešai).

```shell
make up # sugeneruoja slaptažodžius + serverio sertifikatą ir paleidžia LDH
make install # vienkartinis: sukuria konteinerių dokumentus per LDH CLI (reikia ../LinkedDataHub)
make install # suteikia viešą skaitymo prieigą, sukuria konteinerių dokumentus ir vardų erdvės
# ontologiją per LDH CLI (reikia ../LinkedDataHub);
# interaktyvus: Enter×4 = lokali aplinka, kitas Base URL = bet kuri LDH instancija
make -C etl # perkuria rinkinius su numatytąja baze https://localhost:4443/
make load # užkrauna datasets/current/*/*.trig tiesiai į triplestore
```
Expand All @@ -170,9 +172,12 @@ Duomenų struktūra kuriama dviem lygiais:
schemos iš `app/` katalogo dokumentas po dokumento **per LDH CLI** (`put.sh`,
kaip [LinkedDataHub-Apps](https://github.com/AtomGraph/LinkedDataHub-Apps)
projektuose) — taip dokumentai gauna `ldh:ChildrenView` bloką, dėl kurio
konteinerių puslapiai rodo vaikų sąrašus. Reikia šalia išklonintos
[LinkedDataHub](https://github.com/AtomGraph/LinkedDataHub) repozitorijos
(`../LinkedDataHub`, keičiama per `make install LDH_HOME=…`).
konteinerių puslapiai rodo vaikų sąrašus. Kartu įdiegiama vardų erdvės
ontologija (`app/ns.ttl`) su 1:N rodiniais (`ldh:inverseView`): apskrities
puslapis rodo jos savivaldybes, komiteto — dabartinius ir buvusius narius,
Seimo — padalinius, partijos — jos iškeltus Seimo narius. Reikia šalia
išklonintos [LinkedDataHub](https://github.com/AtomGraph/LinkedDataHub)
repozitorijos (`../LinkedDataHub`, keičiama per `make install LDH_HOME=…`).
- **Duomenys** (`make load`): ETL rinkiniai — vien `dh:Item` dokumentai su
`sioc:has_container` nuorodomis į karkasą — rašomi **tiesiogiai į
`fuseki-end-user` TDB2 saugyklą** (`tdb2.tdbloader` per vienkartinį
Expand Down
1 change: 1 addition & 0 deletions app/.ldhignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
root.ttl
ns.ttl
56 changes: 56 additions & 0 deletions app/import-ns.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env bash
# Installs/updates the LTLOD namespace ontology (ns.ttl) into the admin
# dataspace's ontologies/namespace/ document, which LinkedDataHub serves at
# {base}ns. Mirrors LinkedDataHub-Apps demo/northwind-traders:
# 1. PATCH-reset the ontology document (drop everything except the document
# resource and its foaf:primaryTopic),
# 2. POST ns.ttl with a prepended @base <{base}ns> directive so its : prefix
# (<#>) resolves to the end-user namespace,
# 3. clear the ontology from server memory so it reloads fresh.
# Requires LinkedDataHub's bin/ subdirs on $PATH (patch.sh, post.sh,
# clear-ontology.sh) — `make install` in the root Makefile sets this up.
set -euo pipefail

if [ "$#" -ne 3 ] && [ "$#" -ne 4 ]; then
echo "Usage: $0" '$base $cert_pem_file $cert_password [$proxy]' >&2
echo "Example: $0" 'https://localhost:4443/ ./ssl/owner/cert.pem Password https://localhost:5443/' >&2
echo "Note: special characters such as $ need to be escaped in passwords!" >&2
exit 1
fi

base="$1"
cert_pem_file=$(realpath "$2")
cert_password="$3"
proxy="${4:-$base}"

app_dir="$(cd "$(dirname "$0")" && pwd)"

admin_uri() {
echo "$1" | sed 's|://|://admin.|'
}

admin_base=$(admin_uri "$base")
admin_proxy=$(admin_uri "$proxy")

printf "\n### Resetting namespace ontology document: %sontologies/namespace/\n" "$admin_base"
{ echo "BASE <${admin_base}ontologies/namespace/>"; cat "$app_dir/patch-ontology.ru"; } | patch.sh \
-f "$cert_pem_file" \
-p "$cert_password" \
--proxy "$admin_proxy" \
"${admin_base}ontologies/namespace/"

printf "\n### Appending ns.ttl to the namespace ontology\n"
{ echo "@base <${base}ns> ."; cat "$app_dir/ns.ttl"; } | post.sh \
-f "$cert_pem_file" \
-p "$cert_password" \
--proxy "$admin_proxy" \
--content-type "text/turtle" \
"${admin_base}ontologies/namespace/"

printf "\n### Clearing ontology from server memory: %sns#\n" "$base"
clear-ontology.sh \
-f "$cert_pem_file" \
-p "$cert_password" \
-b "$admin_base" \
--proxy "$admin_proxy" \
--ontology "${base}ns#"
19 changes: 14 additions & 5 deletions app/install.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
#!/usr/bin/env bash
# Creates/updates the LTLOD container scaffolding (root document + containers +
# taxonomy scheme containers) on a running LinkedDataHub instance via the LDH
# CLI. Requires LinkedDataHub's bin/ subdirs and Jena's bin/ (for `turtle`) on
# $PATH — `make install` in the root Makefile sets this up.
# Deliberately does NOT call make-public.sh: `make load` ends with `make public`.
# Sets up the LTLOD dataspace on a running LinkedDataHub instance via the LDH
# CLI: makes it publicly readable, then creates/updates the container
# scaffolding (root document + containers + taxonomy scheme containers) and the
# namespace ontology (ns.ttl with 1:N entity views). Requires LinkedDataHub's
# bin/ subdirs and Jena's bin/ (for `turtle`) on $PATH — `make install` in the
# root Makefile sets this up. The public grant equals `make public`
# (bin/make-public.sh) but goes through LDH's HTTP API, so it also works
# against remote instances; both are idempotent.
set -euo pipefail

if [ "$#" -ne 3 ] && [ "$#" -ne 4 ]; then
Expand All @@ -20,6 +23,9 @@ proxy="${4:-$base}"

app_dir="$(cd "$(dirname "$0")" && pwd)"

printf "\n### Creating authorization to make the dataspace public\n"
make-public.sh -b "$base" -f "$cert_pem_file" -p "$cert_password" --proxy "$proxy"

printf "\n### Updating root document: %s\n" "$base"
turtle --base="$base" < "$app_dir/root.ttl" | put.sh \
-f "$cert_pem_file" \
Expand All @@ -30,3 +36,6 @@ turtle --base="$base" < "$app_dir/root.ttl" | put.sh \

printf "\n### Updating container documents\n"
"$app_dir/update-folder.sh" "$base" "$cert_pem_file" "$cert_password" "$app_dir" "$app_dir" "$proxy"

printf "\n### Updating namespace ontology\n"
"$app_dir/import-ns.sh" "$base" "$cert_pem_file" "$cert_password" "$proxy"
Loading
Loading