Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
4e6a03d
fix(schemaview): get_uri resolves elements of relatively-imported sch…
noelmcloughlin Sep 17, 2026
b39f78f
chore: update packages/linkml_runtime/src/linkml_runtime/utils/schema…
noelmcloughlin Sep 18, 2026
814b74b
Merge branch 'main' into issue_3878
noelmcloughlin Sep 18, 2026
b842129
fix(schemaview): restore .get() lookup in get_uri, reverting b39f78f9
noelmcloughlin Sep 18, 2026
c8ad021
docs(schemaview): say why get_uri uses .get() rather than a subscript
noelmcloughlin Sep 18, 2026
4f43bd2
build(deps): bump sqlalchemy
dependabot[bot] Sep 24, 2026
69938e5
Merge branch 'main' into issue_3878
kevinschaper Sep 24, 2026
9cf563c
fix(schemaview): keep track of which schema requested each import (#3…
noelmcloughlin Sep 24, 2026
1350e94
Merge branch 'main' into issue_3878
matentzn Sep 25, 2026
d2fb821
Merge pull request #4000 from noelmcloughlin/issue_3878
matentzn Sep 25, 2026
0cb65a8
build(deps): bump platformdirs in the patch-updates group
dependabot[bot] Sep 25, 2026
b07d0a0
fix(jsonschemagen): keep classes closed by default, per the metamodel
amc-corey-cox Sep 25, 2026
3864e08
Update metamodel test fixtures from linkml-model
github-actions[bot] Sep 25, 2026
5ef7622
Warn hybrid schemaloader-schemaview use
Silvanoc Sep 25, 2026
d8035d3
build(deps): bump platformdirs in the patch-updates group
dependabot[bot] Sep 28, 2026
4ca0022
build(deps-dev): bump tox from 4.61.5 to 4.63.0
dependabot[bot] Sep 28, 2026
123bc30
build(deps): bump platformdirs in the patch-updates group
dependabot[bot] Sep 29, 2026
95a39e2
build(deps-dev): bump typedb-driver from 3.12.3 to 3.13.6
dependabot[bot] Sep 29, 2026
fd4ed99
feat(openapigen): prepare support for multiple openapi versions
Silvanoc Sep 29, 2026
30203e5
build(deps-dev): bump tox from 4.63.0 to 4.64.1
dependabot[bot] Oct 1, 2026
62d30bf
build(deps): bump the github-actions group with 5 updates
dependabot[bot] Oct 1, 2026
760eb0d
feat(gen-shacl): translate class-level boolean expressions to SHACL l…
jdsika Sep 24, 2026
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
2 changes: 2 additions & 0 deletions .github/scripts/check_links.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
"snomed.info",
# LinkML metamodel - w3id redirects to linkml.io/linkml-model which may have issues
"w3id.org",
# Certificate expired Sep/24/2026
"datashapes.org",
}


Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/check-external-links.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
python-version: "3.12"

- name: Install uv
uses: astral-sh/setup-uv@v10.0.1
uses: astral-sh/setup-uv@v10.2.0
with:
version: ${{ env.UV_VERSION }}

Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/dependency-audit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:

# Pin uv to a known-good, recent release.
- name: Install uv and setup uv caching
uses: astral-sh/setup-uv@v10.0.1
uses: astral-sh/setup-uv@v10.2.0
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
Expand All @@ -56,7 +56,10 @@ jobs:
#
# So gate the audit on whether the change actually altered dependencies,
# relative to each event's natural base:
# * pull_request -> the PR base
# * pull_request -> the base the checked-out merge commit was built on
# (HEAD^1). Not github.event.pull_request.base.sha: that can be an
# older main commit, so the diff would include main's own uv.lock
# bumps and audit a PR that changed no dependencies.
# * push (main) -> the commit before the push (github.event.before)
# * merge_group -> the queue base
# * otherwise (workflow_dispatch, first/force push) -> audit
Expand All @@ -78,7 +81,7 @@ jobs:
run: |
set -euo pipefail
case "${{ github.event_name }}" in
pull_request) base="${{ github.event.pull_request.base.sha }}" ;;
pull_request) base="$(git rev-parse HEAD^1)" ;;
merge_group) base="${{ github.event.merge_group.base_sha }}" ;;
push) base="${{ github.event.before }}" ;;
*) base="" ;;
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/doc-pages.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
git fetch upstream --tags

- name: Install uv
uses: astral-sh/setup-uv@v10.0.1
uses: astral-sh/setup-uv@v10.2.0
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/docker-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ jobs:
echo "Ref: ${{ github.ref }}"

- name: Set up QEMU
uses: docker/setup-qemu-action@v4.2.0
uses: docker/setup-qemu-action@v4.4.0

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4.3.0
uses: docker/setup-buildx-action@v4.4.1

- name: Login to DockerHub
if: startsWith(github.ref, 'refs/tags/v')
Expand All @@ -61,7 +61,7 @@ jobs:
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}

- name: Build and push
uses: docker/build-push-action@v7.3.0
uses: docker/build-push-action@v7.4.0
with:
context: .
platforms: linux/amd64,linux/arm64/v8
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
git fetch upstream --tags

- name: Install uv
uses: astral-sh/setup-uv@v10.0.1
uses: astral-sh/setup-uv@v10.2.0
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
Expand Down
18 changes: 9 additions & 9 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
steps:
- uses: actions/checkout@v7
- name: Install uv
uses: astral-sh/setup-uv@v10.0.1
uses: astral-sh/setup-uv@v10.2.0
with:
version: ${{ env.UV_VERSION }}

Expand Down Expand Up @@ -76,7 +76,7 @@ jobs:
git fetch upstream --tags

- name: Install uv and setup uv caching
uses: astral-sh/setup-uv@v10.0.1
uses: astral-sh/setup-uv@v10.2.0
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
Expand Down Expand Up @@ -105,7 +105,7 @@ jobs:
shell: bash
- name: Upload linkml coverage report
if: github.repository == 'linkml/linkml' && github.actor != 'dependabot[bot]'
uses: codecov/codecov-action@v7.0.0
uses: codecov/codecov-action@v7.1.1
with:
name: codecov-linkml-${{ matrix.os }}-${{ matrix.python-version }}
token: ${{ secrets.CODECOV_TOKEN }}
Expand All @@ -114,7 +114,7 @@ jobs:
fail_ci_if_error: true
- name: Upload runtime coverage report
if: github.repository == 'linkml/linkml' && github.actor != 'dependabot[bot]'
uses: codecov/codecov-action@v7.0.0
uses: codecov/codecov-action@v7.1.1
with:
name: codecov-runtime-${{ matrix.os }}-${{ matrix.python-version }}
token: ${{ secrets.CODECOV_TOKEN }}
Expand All @@ -139,7 +139,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@v7
- name: Install uv
uses: astral-sh/setup-uv@v10.0.1
uses: astral-sh/setup-uv@v10.2.0
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
Expand All @@ -160,7 +160,7 @@ jobs:
uv run coverage report -m
- name: Upload linkml slow test coverage
if: github.repository == 'linkml/linkml' && github.actor != 'dependabot[bot]'
uses: codecov/codecov-action@v7.0.0
uses: codecov/codecov-action@v7.1.1
with:
name: codecov-linkml-slow-${{ matrix.os }}-${{ matrix.python-version }}
token: ${{ secrets.CODECOV_TOKEN }}
Expand Down Expand Up @@ -189,7 +189,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@v7
- name: Install uv
uses: astral-sh/setup-uv@v10.0.1
uses: astral-sh/setup-uv@v10.2.0
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
Expand All @@ -207,7 +207,7 @@ jobs:
uv run pytest tests/linkml/test_notebooks/ -m "not kroki" --cov --cov-report xml:coverage-notebooks.xml --cov-report term-missing
- name: Upload notebook coverage
if: github.repository == 'linkml/linkml' && github.actor != 'dependabot[bot]'
uses: codecov/codecov-action@v7.0.0
uses: codecov/codecov-action@v7.1.1
with:
name: codecov-notebooks-${{ matrix.os }}-${{ matrix.python-version }}
token: ${{ secrets.CODECOV_TOKEN }}
Expand All @@ -232,7 +232,7 @@ jobs:
python-version: 3.13

- name: Install uv
uses: astral-sh/setup-uv@v10.0.1
uses: astral-sh/setup-uv@v10.2.0
with:
version: ${{ env.UV_VERSION }}
- name: Build source and wheel archives
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/metamodel-compat.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
uses: actions/checkout@v7

- name: Install uv
uses: astral-sh/setup-uv@v10.0.1
uses: astral-sh/setup-uv@v10.2.0
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pypi-publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
python-version: 3.13

- name: Install uv
uses: astral-sh/setup-uv@v10.0.1
uses: astral-sh/setup-uv@v10.2.0
with:
version: ${{ env.UV_VERSION }}

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/rustgen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
git fetch upstream --tags

- name: Install uv and setup uv caching
uses: astral-sh/setup-uv@v10.0.1
uses: astral-sh/setup-uv@v10.2.0
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
Expand Down Expand Up @@ -63,7 +63,7 @@ jobs:

- name: Upload coverage report
if: github.repository == 'linkml/linkml' && github.actor != 'dependabot[bot]'
uses: codecov/codecov-action@v7.0.0
uses: codecov/codecov-action@v7.1.1
with:
name: codecov-results-rustgen
token: ${{ secrets.CODECOV_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/typedb-integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
--health-start-period 30s
steps:
- uses: actions/checkout@v7
- uses: astral-sh/setup-uv@v10.0.1
- uses: astral-sh/setup-uv@v10.2.0
- uses: actions/setup-python@v7.0.0
with:
python-version: "3.12"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/update-feature-dashboard.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
git fetch upstream --tags

- name: Install uv
uses: astral-sh/setup-uv@v10.0.1
uses: astral-sh/setup-uv@v10.2.0
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
Expand Down
25 changes: 12 additions & 13 deletions docs/generators/openapi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ OpenAPI
=======

`OpenAPI <https://www.openapis.org/>`_ is a specification for describing
RESTful HTTP APIs. The OpenAPI generator produces an OpenAPI v3.0.3
specification in YAML from a LinkML schema.
RESTful HTTP APIs. The OpenAPI generator produces an OpenAPI
specification in YAML from a LinkML schema. As of now it supports OpenAPI
specification versions v3.0.3 and v3.1.0.

.. note:: This generator produces a complete OpenAPI spec by combining a
user-provided *template* (containing the API header, endpoints, and
Expand All @@ -15,15 +16,16 @@ Overview

The generator works in two stages:

1. The user provides an **OpenAPI template** — a valid OpenAPI v3.0.3 YAML
1. The user provides an **OpenAPI template** — a valid OpenAPI YAML
file that defines the API metadata (title, version, servers), paths
(endpoints), and security schemes.
(endpoints), and security schemes. It also specifies the version of
the OpenAPI specification in its top-level attribute `openapi`.
2. The generator fills the ``components/schemas`` section with JSON Schema
definitions generated from the LinkML schema, keeping only those classes
that are transitively reachable from the endpoints.

Both the input template and the final output are automatically validated
against the OpenAPI 3.0.3 specification using
against the corresponding OpenAPI specification version using
`openapi-spec-validator <https://github.com/p1c2u/openapi-spec-validator>`_.

To run:
Expand All @@ -32,6 +34,9 @@ To run:

gen-openapi personinfo.yaml --template api-template.yaml > personinfo.openapi.yaml

The **template's top-level attribute `openapi` MUST specify the supported OpenAPI
version**.

The ``--template`` / ``-t`` option is required when generating a concrete
specification. If omitted, the generator prints a generic template that can
be used as a starting point:
Expand All @@ -47,14 +52,8 @@ The generator validates both the input template and the final output against
the OpenAPI specification using
`openapi-spec-validator <https://github.com/p1c2u/openapi-spec-validator>`_.

The ``openapi`` field in the template is checked against the expected
version for the chosen output format (currently ``openapi303`` →
``3.0.3``). If the versions do not match, the generator raises a
``ValueError``.

Additionally, the template's ``components/schemas`` section must declare each
resource that is referenced by an endpoint, using two custom extension
fields:
The template's ``components/schemas`` section must declare each resource
that is referenced by an endpoint, using two custom extension fields:

``x-linkml-schema``
The ``id`` of the LinkML schema being used. Must match exactly.
Expand Down
82 changes: 82 additions & 0 deletions docs/generators/shacl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,88 @@ Example Output:
shacl:targetClass <https://w3id.org/linkml/tests/kitchen_sink/Person> .


Class Expressions
^^^^^^^^^^^^^^^^^

Class-level boolean expressions become the SHACL logical constraint components
their metamodel definitions map to (`SHACL §4.6
<https://www.w3.org/TR/shacl/#core-components-logical>`__):

================== =====================================================
LinkML SHACL, on the class's ``sh:NodeShape``
================== =====================================================
``any_of`` ``sh:or`` over the member shapes
``all_of`` ``sh:and`` over the member shapes
``exactly_one_of`` ``sh:xone`` over the member shapes
``none_of`` one ``sh:not`` per member
================== =====================================================

Each member becomes an anonymous node shape. ``is_a`` gives ``sh:class``, and
nested expressions recurse. Each entry of ``slot_conditions`` gives an
``sh:property`` whose path is that of the slot as induced for the class, so
``slot_usage`` applies:

* ``required``, ``value_presence`` and the cardinalities give ``sh:minCount`` /
``sh:maxCount``;
* ``minimum_value`` / ``maximum_value`` give ``sh:minInclusive`` /
``sh:maxInclusive``, and ``equals_number`` gives both, so that ``5`` also
matches ``5.0``;
* ``pattern`` gives ``sh:pattern``;
* ``equals_string`` and ``equals_string_in`` give ``sh:in``; on an enum slot the
values are the permissible values as the enum renders them, the IRI of their
``meaning`` where they have one;
* ``range`` gives the same class, type or enum constraint as a slot's range.

SHACL allows ``sh:minInclusive``, ``sh:maxInclusive``, ``sh:in`` and
``sh:pattern`` at most once per shape. Where one condition needs one of them
twice, for example ``minimum_value`` next to ``equals_number``, the second value
goes into an ``sh:and`` member of the property shape, where it applies to the
same values.

A slot condition constrains only the values that are present, so it also holds
when the slot is absent - unless ``required: true``, ``value_presence: PRESENT``
or a minimum or exact cardinality of at least 1 requires the slot. Inside
``none_of``, at any depth, a condition that constrains values requires the slot,
so that an absent slot is not rejected by the negation - unless the condition
decides presence itself, through ``required``, ``value_presence`` or a maximum
or exact cardinality of 0. The JSON Schema generator requires the slot in a
class's own ``none_of`` for every condition that sets neither ``required`` nor
``value_presence``.

.. code-block:: yaml

GeodeticReferenceSystem:
slots: [code, name]
any_of:
- slot_conditions:
code:
required: true
- slot_conditions:
name:
required: true

.. code-block:: turtle

ex:GeodeticReferenceSystem a sh:NodeShape ;
sh:or ( [ sh:property [ sh:path ex:code ; sh:minCount 1 ] ]
[ sh:property [ sh:path ex:name ; sh:minCount 1 ] ] ) ;
...

An expression is attached to the shape of the class that declares it. Like
every ``sh:targetClass``, it reaches instances of subclasses where the data
graph states the ``rdfs:subClassOf`` (`SHACL §2.1.3.2
<https://www.w3.org/TR/shacl/#targetClass>`__); the ``sh:class`` that ``is_a``
gives recognises instances of subclasses the same way, as it does for a slot's
range.

An operator whose members use anything else is skipped as a whole and logged as
a warning, because leaving out one member would change what the operator
admits. That covers, for example, ``has_member`` or a slot-level ``any_of``
inside a slot condition, a condition on a name that is not a slot, a condition
on the identifier slot (the node's IRI rather than a property), and
``equals_string`` on a slot whose range does not hold strings.


Command Line
^^^^^^^^^^^^

Expand Down
2 changes: 1 addition & 1 deletion examples/tutorial/tutorial01/personinfo.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$defs": {
"Person": {
"additionalProperties": true,
"additionalProperties": false,
"description": "",
"properties": {
"age": {
Expand Down
Loading
Loading