* feat(bpmn): add Analyze/Import/Export endpoints to Elsa.Bpmn.Interchange
Thin FastEndpoints wrappers over Bpmn.Interchange, sharing one
BpmnInterchangeDocumentService so Analyze and Import can never disagree
about what a document costs. Import surfaces capability refusal
(BpmnCapabilityRequirements.Analyze, walked into nested processes) with
the missing capability and offending element ids, and reuses
BpmnWorkBinder to bind the root BpmnProcess scope. Export re-reads the
original XML persisted alongside the workflow definition and re-runs it
through BpmnXmlWriter, so retained extension elements, foreign
attributes and BPMN DI layout survive the round trip without being
reconstructed from the reduced Elsa activity graph.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(bpmn): make a stale BPMN export refuse instead of mislead
Export now refuses with 422 (naming the reason) when a workflow definition's
BPMN source is missing or no longer matches the definition's version, rather
than exporting stale or absent content while reporting success. Import
records the definition's version alongside the source XML so Export can
detect drift caused by a later save replacing custom properties wholesale.
Also: the interchange package now consumes the runtime host's declared
capability set from a new public Elsa.Bpmn.Hosting.BpmnRuntimeCapabilities
instead of restating it (one value, one home); the Import endpoint's
capability-refusal message no longer misattributes driving elements across
capabilities; the three BPMN REST endpoints get HTTP-level test coverage
(multipart validation, exception-to-status-code mapping, permission gating);
and the wiki documents the endpoints and Export's known limitation.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test(bpmn): alert when the library defines a capability Elsa does not declare
Restores the deleted comparison between BpmnRuntimeCapabilities.Declared (ours)
and BpmnHostCapabilities.Full (the library's) — these are two different
constants, not the tautology the earlier deletion assumed. The pinned
Bpmn.Semantics 0.1.1-preview.19 currently defines exactly the four flags Elsa
declares, so capability refusal at import/build is wired but unreachable; this
test is what will say the moment a library bump changes that, and its failure
message names the decision (implement and declare, or leave undeclared on
purpose) rather than just failing silently.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(bpmn): return 400 for a malformed export version and clarify a partial-import refusal
- Export/Endpoint.cs: a non-numeric or out-of-range VersionOptions query value now returns a
400 naming the offending value instead of throwing through FromString and bubbling into a 500.
- BpmnInterchangeDocumentService: the message shown when a definition carries BPMN source but not
its version marker (a second save that never completed after ImportAsync's first) now says so
explicitly, distinct from "never imported" and "stale".
- BpmnInterchangeDocumentService: replace the implicit filter in EnsureCapabilitiesSatisfied's
foreach with an explicit .Where(...), same behaviour.
- Test projects: extract the duplicated ReadAsset/Path.Combine helper in
BpmnInterchangeTestBase and BpmnInterchangeEndpointTests into a single BpmnAssetReader, guarded
against a rooted or nested file name.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(bpmn): write both BPMN import markers in one save
Move the BPMN source XML off the pre-import model and onto the same
explicit save that already records the definition's version, so a
failed or cancelled post-import save leaves neither custom property
behind instead of a partial, undiagnosable state. Update
BpmnAssetReader to use Path.Join instead of Path.Combine so its
rooted/nested-name guard is defence-in-depth rather than the only
thing standing between the code and a wrong path.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>