* feat(bpmn): add document GET/PUT endpoints for BPMN JSON round-tripping (W21)
Studio holds a BPMN process as the library's own JSON payload, not as .bpmn
XML, so binding a task (W11) or moving a shape (W14) had no write path back
to the server: bpmn/import only takes XML, and saving the activity JSON
through the ordinary definition save leaves Bpmn:SourceXml stale, so export
then refuses with 422.
Adds GET/PUT bpmn/definitions/{definitionId}/document, sharing the same
analyze-then-commit path Import runs (BpmnInterchangeDocumentService.ReadDocument/
ImportDocumentAsync), so a document read by GET and written back unchanged by
PUT can never disagree with what Import or Export would do with the same
bytes. Records which process a document was imported from
(Bpmn:SourceProcessId) so a multi-process document keeps importing the same
process on every edit. Binds and writes the document body with plain
System.Text.Json defaults, not FastEndpoints' configured serializer, since
Bpmn.Model's JSON payload format (integer enums, explicit property names)
disagrees with Elsa's own API-wide JSON conventions.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor(bpmn): deduplicate document endpoint response and exception handling
Import and the document Put endpoint shared a byte-identical Response type and
an identical exception-to-status-code cascade; Export and the document Get
endpoint shared an identical cascade too. Extract both into a shared
BpmnImportResponse and two small cascade helpers under Endpoints/Bpmn, used by
all four endpoints with unchanged status codes and error messages. Also add
endpoint coverage for PUT against a definition imported before
Bpmn:SourceProcessId existed, for both the single- and two-process cases.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(bpmn): add optimistic concurrency to the document endpoints and restore Import.Response
GET bpmn/definitions/{id}/document now returns a strong ETag derived from the
definition's Version, its Bpmn:SourceVersion custom property, and a new
Bpmn:DocumentRevision counter (needed because an unpublished draft is edited
in place, so Version/SourceVersion alone do not always change on save). PUT
requires a matching If-Match: missing returns 428, stale returns 412 (checked
before any import work), and a successful PUT returns a new, different ETag.
Also restores the public Elsa.Bpmn.Interchange.Endpoints.Bpmn.Import.Response
type the prior dedupe commit renamed to BpmnImportResponse without cause,
which would have broken source and binary consumers of the preview package;
Import and the document Put endpoint now both use Import.Response again.
Also replaces a foreach-with-continue over a JSON array with .Where(...) in
the document Put test helper, per CodeQL, with no behaviour change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(bpmn): derive the document ETag from the stored document and graph
The document ETag combined the definition's Version, Bpmn:SourceVersion and a
Bpmn:DocumentRevision counter only the document PUT incremented. An unpublished
draft is edited in place under the same version, and both the workflow
definition importer and the designer's save replace CustomProperties
wholesale, so a POST bpmn/import into the same definition or a designer save
of the draft left all three unchanged. A client holding the pre-write ETag
could then PUT and silently overwrite that write.
The ETag is now a SHA-256 hash over the stored definition's id, version, BPMN
source (Bpmn:SourceXml) and activity graph (StringData), computed in one place
for GET and for PUT's precondition check. Every writer changes at least one
input: a document PUT or an import rewrites the source, a designer save
rewrites the graph, a new draft version changes the id and version. Identical
stored content now yields an identical ETag, so a PUT of unchanged content
returns the ETag GET did.
Bpmn:DocumentRevision and the PUT's pre-import read of it are gone. The ETag a
PUT returns is computed from the definition its import persisted. If-Match
must equal the current strong ETag exactly (weak tags and lists never match,
412), and the wildcard "*" is refused along with a missing header (428),
since it matches whatever is stored.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
||
|---|---|---|
| .. | ||
| activities-and-authoring.md | ||
| architecture.md | ||
| bpmn-workflows.md | ||
| build-run-operate.md | ||
| diagnostics-console-logs.md | ||
| diagnostics-structured-logs.md | ||
| expressions-and-scripting.md | ||
| extension-guide.md | ||
| health-checks.md | ||
| http-scheduling-resilience.md | ||
| identity-tenancy-security.md | ||
| module-system.md | ||
| opentelemetry-workflows.md | ||
| output-converters.md | ||
| persistence.md | ||
| README.md | ||
| repository-map.md | ||
| specs-and-adrs.md | ||
| testing-guide.md | ||
| workflow-api.md | ||
| workflow-core.md | ||
| workflow-management.md | ||
| workflow-runtime.md | ||
Elsa Core Wiki
This wiki is a repo-local, code-grounded map of Elsa Core. It is intended for contributors who need the same kind of fast orientation that a DeepWiki-style generated wiki gives: what the system is, where the important code lives, how the pieces connect, and how to safely extend or test them.
The source of truth is still the code, specs, ADRs, and tests. Each page links back to the relevant files so you can jump from explanation to implementation.
Start Here
Elsa Core is a modular .NET workflow engine. The main solution is Elsa.sln. Production code lives under src, tests under test, specifications under specs, and architecture decisions under doc/adr.
The shortest mental model:
- An application calls
services.AddElsa(...). - Elsa builds an
IModuleand configures feature objects. - Features register services, activities, API endpoints, middleware, hosted services, and persistence stores.
- Workflow definitions are created by code, JSON, imported files, or providers.
- The runtime starts, resumes, dispatches, and persists workflow instances.
- APIs, SignalR hubs, HTTP endpoint activities, diagnostics, and persistence packages layer around that core.
flowchart LR
App["Host app"] --> Module["Elsa module system"]
Module --> Core["Workflow core"]
Module --> Management["Workflow management"]
Module --> Runtime["Workflow runtime"]
Module --> Api["Workflow API"]
Module --> Extensions["HTTP, Scheduling, Expressions, Identity, Tenants"]
Management --> Persistence["Stores / EF Core providers"]
Runtime --> Persistence
Runtime --> Logs["Execution logs and diagnostics"]
Api --> Studio["Elsa Studio / API clients"]
Page Map
| Page | Use it for |
|---|---|
| Repository Map | Top-level folders, projects, and where to look first. |
| Architecture | The main system layers and request/execution flow. |
| Module System | How IModule, FeatureBase, feature dependencies, and shell features work. |
| Workflow Core | Activities, execution contexts, pipelines, variables, bookmarks, graphs, and flowchart execution. |
| Workflow Management | Workflow definitions, instances, import/export, materializers, validation, and activity descriptors. |
| Workflow Runtime | Dispatch, triggers, bookmarks, queues, background activity scheduling, graceful shutdown, and recovery. |
| Workflow API | FastEndpoints, route prefixing, API categories, SignalR, and client-facing contracts. |
| Activities And Authoring | How workflows are authored in C#, JSON, ElsaScript, and host methods. |
| Output Converters | How to register, configure, validate, discover, and operate bound-value converters. |
| Expressions And Scripting | Expression evaluators and language feature packages. |
| HTTP, Scheduling, And Resilience | Inbound HTTP workflows, outbound HTTP, scheduled triggers, and resilience strategies. |
| Persistence | In-memory stores, EF Core stores, provider packages, migrations, and multi-provider rules. |
| Diagnostics Structured Logs | ILogger capture, live feed, REST/SignalR surface, redaction, and SQLite persistence. |
| Diagnostics Console Logs | Raw stdout/stderr capture, live feed, REST/SignalR surface, and redaction. |
| Health Checks | Elsa runtime readiness probes, liveness/readiness mapping, and Kubernetes probe guidance. |
| Identity, Tenancy, And Security | Users, applications, roles, API keys, tenant resolution, and authorization touch points. |
| Testing Guide | Test project layout, fixture choices, and targeted commands. |
| Extension Guide | How to add features, activities, expression providers, stores, endpoints, and ingress sources. |
| OpenTelemetry Workflow Instrumentation | First-party workflow and activity traces and metrics emitted through System.Diagnostics. |
| Specs And ADRs | How current specs and ADRs explain design intent. |
| Build, Run, And Operate | Build commands, sample hosts, runtime knobs, Docker notes, and operational endpoints. |
Source Landmarks
- Main public entry: src/modules/Elsa/Extensions/DependencyInjectionExtensions.cs
- Default umbrella feature: src/modules/Elsa/Features/ElsaFeature.cs
- Module implementation: src/common/Elsa.Features/Implementations/Module.cs
- Core workflow feature: src/modules/Elsa.Workflows.Core/Features/WorkflowsFeature.cs
- Management feature: src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs
- Runtime feature: src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs
- API feature: src/modules/Elsa.Workflows.Api/Features/WorkflowsApiFeature.cs
- Reference server: src/apps/Elsa.Server.Web/Program.cs
- Structured-log persistence design: specs/005-structured-log-persistence/plan.md
Contributor Workflow
Use targeted reads first, then targeted tests. For most changes, start with the relevant module page, inspect the linked feature class and contracts, add or update tests in the matching test/unit, test/integration, or test/component project, and run the narrowest dotnet test command that proves the behavior.
When changing public behavior, update the related README, spec quickstart, or wiki page in the same PR. This repository is strongly modular, so the best changes keep ownership boundaries clear.