Conversation
Adversarial audit findings (PIV converter, audited at stack tip incl. the hardening PR)Two substantive findings, both empirically demonstrated with pyshacl end-to-end probes and cross-checked against LinkML's reference rule semantics ( A1 — real bug: greedy dispatch drops extra pre/postcondition operators → false positives. A2 — edge case: boolean-guard shadows PIV for Verified clean (attacked, held up): absent-target semantics (violation via Test gaps (cosmetic): These findings equally apply to the consolidated branch of #18. Suggest addressing A1/A2 as a follow-up commit on this stack before upstreaming. |
2e56a36 to
8999ad6
Compare
8999ad6 to
e161ce7
Compare
|
Mirrored upstream as linkml#3989. |
…view.py Co-authored-by: Silvano Cirujano Cuesta <silvano.cirujano-cuesta@siemens.com>
…nkml#3855) * fix(schemaview): keep track of which schema requested each import * fix(schemaview): keep URL import keys intact in the closure
fix(schemaview): get_uri resolves elements of relatively-imported schemas
* test(openapigen): parametrize OAS version in templates and fixture Make the top-level \`openapi\` attribute of the composed OpenAPI templates a parameter instead of hardcoding '3.0.3'. The OPENAPI_HEADER fragment now takes an oas_version placeholder, threaded through the openapi_template and single_endpoint_template composition helpers (defaulting to 3.0.3). The openapi_spec fixture is parametrized over the OAS_VERSIONS mapping (OpenAPI version string -> OpenApiGenerator output format name) and wires the chosen format into the generator via gen_openapi_spec. The TEMPLATE_HEAD constant is refactored into the template_head() helper so the fixture and the standalone tests compose the same template. Only 3.0.3 is driven so far; adding a new OAS version is a one-line extension of OAS_VERSIONS. Signed-off-by: Silvano Cirujano Cuesta <silvano.cirujano-cuesta@siemens.com> * feat(openapigen): prepare support for multiple openapi version Generalize the generator so the OpenAPI version is not hard-coded to v3.0.3. The version is now read from the template's top-level `openapi` attribute and validated against the list of supported versions, paving the way for v3.1.0 support. Signed-off-by: Silvano Cirujano Cuesta <silvano.cirujano-cuesta@siemens.com> * feat(openapigen): add support for openapi v3.1.0 When the template declares OpenAPI v3.1.0, schemas are now generated via PydanticGenerator instead of JsonSchemaGenerator. Because OpenAPI 3.1.0 is fully aligned with JSON Schema 2020-12, the only post-processing needed is rewriting `$defs` references to `components/schemas` and stripping `linkml_meta` annotations. Schema names invalid under OpenAPI 3.1 are sanitized and their `$ref`s rewritten. Adds v3.1.0-specific test fixtures and docs for the two supported versions. Signed-off-by: Silvano Cirujano Cuesta <silvano.cirujano-cuesta@siemens.com> * test(openapigen): parametrize version-agnostic tests over both OAS versions --------- Signed-off-by: Silvano Cirujano Cuesta <silvano.cirujano-cuesta@siemens.com> Co-authored-by: N <13322818+noelmcloughlin@users.noreply.github.com>
e161ce7 to
7622d70
Compare
The rules-to-SHACL-SPARQL converter added in linkml#3451 recognised a single named pattern. This adds the presence-implies-value pattern: a precondition asserting `value_presence: PRESENT` on one slot, and a postcondition constraining another slot with `equals_string` or `equals_string_in`. It reads as "if the guard slot is present, the target slot must be present and hold one of the allowed values", and generalises the existing boolean guard to arbitrary enum values. The boolean guard is now gated on the target slot's range actually being `boolean`. Without that gate a slot of range `string` carrying `equals_string: "true"` was translated as a boolean comparison, which does not match the string `"true"` in the data and so flagged conforming instances as violations. String-ranged slots now fall through to the presence-implies-value pattern and compare as strings. Pattern matching is exact: each converter requires its conditions to set precisely the operators it translates. A rule whose conditions carry anything further -- extra scalar operators, or expression-level any_of / all_of / none_of / exactly_one_of -- is skipped rather than partially translated, since dropping a term would either widen the precondition (false positives) or weaken the postcondition (false negatives). Slot resolution goes through induced slots so that `slot_usage` overrides, `slot_uri` overrides and alias-form keys resolve to the same IRI that `sh:path` emits. Co-authored-by: jdsika <carlo.van-driesten@vdl.digital>
7622d70 to
bae4a43
Compare
Summary
Adds the presence-implies-value pattern to the rules → SHACL-SPARQL
converter: a precondition asserting
value_presence: PRESENTon a guard slotplus a postcondition constraining another slot with
equals_string/equals_string_inbecomes ash:SPARQLConstraintthat flags focus nodes wherethe guard is present but the target does not hold an allowed value.
It reads as "if the guard slot is present, the target slot must be present and
hold one of the allowed values" and generalises the existing boolean guard to
arbitrary enum values. Enum permissible values resolve to their
meaningIRIs;values without
meaningcompare as string literals.$thisis pre-bound perSHACL §5.3.1.
Example from the modeled use case: if
sun_altitudeis present,daytimemustbe
dayortwilight— cross-parameter consistency that per-slot SHACLproperty shapes cannot express.
Bug fixed along the way
The pre-existing boolean guard was not gated on the target slot's range. A slot
of range
stringcarryingequals_string: "true"was translated as a booleancomparison, which does not match the string
"true"in the data — so conforminginstances were reported as violations. The guard now requires range
boolean,and string-ranged slots fall through to the presence-implies-value pattern and
compare as strings.
Translation contract: skip, never mis-translate
Pattern matching is exact. Each converter requires its conditions to set
precisely the operators it translates. A rule carrying anything further — extra
scalar operators, or expression-level
any_of/all_of/none_of/exactly_one_of— is skipped rather than partially translated, because droppinga term would either widen the precondition (false positives) or weaken the
postcondition (false negatives).
Slot resolution goes through induced slots, so
slot_usageoverrides,slot_urioverrides and alias-form keys all resolve to the same IRI thatsh:pathemits.Stack position
Note
No dependencies. This PR applies directly to
main. The rules → SPARQLframework it extends (
_add_rules,_rule_to_sparql, the boolean-guard andexclusive-value patterns) is already in
mainvia linkml#3451.Testing
pytest tests/linkml/test_generators/test_shaclgen.py— 105 passed, withthe full generator suite green (1774 passed, 52 skipped, 3 xfailed).
Coverage includes SPARQL syntax validation and
pyshaclend-to-end conforming/ violating round-trips for each pattern, plus negative tests asserting that
unsupported operator combinations are skipped rather than mis-translated.
Review notes
This PR was re-cut from an earlier five-PR stack. The corrections that were
previously separate follow-ups are folded into the feature they correct, so
there is no longer a PR that introduces a defect and another that fixes it.
Two defects found during that re-cut are fixed here: the boolean-guard range
gate above, and a duplicate
test_rule_with_elseconditions_warnsdefinitionthat silently shadowed the existing test of the same name.