OpenEnergyPlatform / OpenEnergyPlatform/oeplatform
A browser-written scenario bundle cannot be patched through the REST API
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 65
- Forks
- 29
- Avg merge
- 15h 25m
- Merged PRs (30d)
- 32
Description
Description of the issue
A scenario bundle written through the browser cannot be changed through the REST API
if it carries an organisation, a funder or a contact person. PATCH answers 400
with a shape violation the caller did not cause and cannot fix through the field it was
editing.
Reproduced locally, against the same validator the API uses:
PATCH /api/v0/scenario-bundles/<uid>/ {"abstract": "Ein neuer Text"}
→ 400
"Study target: This should end in class organisation."
focus_node: https://openenergyplatform.org/ontology/oekg/<uid>
Why it happens
Three facts meet:
- The API validates the post-state, not the diff (WF-05). A diff validates
vacuously — nothing targets an untyped node — so validating it would be a confident
false pass. The whole bundle, with the change in it, is what gets judged. ex:StudyShaperequiressh:classonoeo:OEO_00000510(has organisation),
oeo:OEO_00000509(has funding source) andoeo:OEO_00000508(has contact person).- The browser never writes
rdf:typeon those nodes.factsheet/views.pymints an
organisation, funder or contact person with a label and no type. This is recorded as
measured, not suspected, in the map's WF-05 notes: "the live graph carries sh:class
violations from the UI".
So the API refuses to write into a bundle it considers invalid, and the bundles the
browser wrote are invalid in exactly that way. The API's own bundles are unaffected:
writing those types is something the create path does deliberately, for this reason.
Scope — larger than it first looks
This is not new with #2444 (scenario sub-resources). The read that feeds validation
has gone one hop from the start, and an organisation is one hop from its bundle, so
this has been live since PATCH shipped in #2438 and is on develop now. What
#2444 changes is only the reach: three hops instead of one, which adds a scenario's
study regions and the nodes hanging off them to the same rule.
I could not measure how many bundles are affected: the public SPARQL view requires a
CSRF token and the API path an API key, neither of which an agent has. But organisation
and contact person are core bundle fields, so the honest expectation is most
browser-written bundles, not a few.
Ideas of solution
Three ways out. They are not exclusive — the third is worth doing whichever of the
first two is chosen.
1. Leave it, and treat the refusal as the signal (no code change)
The API declines to add to a record that does not conform, and says exactly what is
wrong. Defensible: the shape is the contract, and writing more into an invalid bundle
makes the cleanup harder later.
Against it: the client is told about a defect it did not cause, in a field it was not
touching, with no action available to it — the shape violation is in data the browser
wrote and the API offers no way to type an existing node. In practice this reads as "the
API is broken", and it silently makes the API useless for the bundles that already exist.
2. Refuse only violations the write introduced
Validate the pre-state as well, and refuse only what is new. "Your write must not make
this worse" replaces "this must be perfect".
For it: keeps the promise that matters — nothing invalid is written by this API —
while not holding a client responsible for damage done before it arrived. A write that
happens to fix a violation still passes.
Against it: a second validation pass per write (measured at 0.023 s for 222 triples,
0.730 s at 12,969 — so cheap for normal bundles, not free for large ones), and the
comparison has to be by violation identity rather than by count, or a write that swaps
one violation for another slips through.
3. Repair the live graph
Write the missing rdf:type on every organisation, funder and contact person the
browser minted, as a one-off management command. That removes the question rather than
answering it, and it is needed anyway: those bundles are invalid against the canonical
shape whoever reads them.
This one belongs to the sibling map
OEKG/_wayfinder-oekg-shacl/Map - OEKG SHACL validation enforced.md, which owns making
the live graph conform.
Also worth fixing at the source, independently of all three: the browser's write path
should type the nodes it mints, or the next browser-written bundle re-creates the
problem the day after any cleanup.
Recommendation
2 plus 3, in that order: option 2 makes the API usable against existing data
immediately and is a contained change; option 3 removes the underlying defect and is
where the real fix lives. Option 1 is the honest status quo and should be chosen
explicitly if it is chosen, rather than arrived at by nobody deciding.
Deployment
Nothing to deploy for the issue itself. Option 2 would be a code change with no
migration. Option 3 would be a write to the production knowledge graph and should be
counted first and run with the usual care.
Workflow checklist
- I am aware of the workflow in
CONTRIBUTING.md
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing the PATCH validation path for scenario bundles and reading the WF-05 notes, then inspect factsheet/views.py to understand how browser-created nodes are written. Compare the pre-state and post-state validation behavior, and review the proposed management-command scope. Done means the chosen behavior is implemented and covered for existing invalid bundles without allowing new violations.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend-api-design, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100