Skip to content

Commit 17293fc

Browse files
authored
Merge pull request #159 from datasharingframework/Documentation_update
Documentation update
2 parents f082905 + e888861 commit 17293fc

6 files changed

Lines changed: 277 additions & 6 deletions

File tree

‎docs/src/process-development/api-v2/dsf/index.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ icon: creative
1818
- [Read Access Tag](read-access-tag.md)
1919
- [Requester and Recipient](requester-and-recipient.md)
2020
- [Spring Framework Integration](spring-framework-integration.md)
21+
- [Understanding the Process Authorization Extension](./understanding-the-process-authorization-extension.md)
2122
- [Versions, Placeholders and URLs](versions-placeholders-urls.md)

‎docs/src/process-development/api-v2/dsf/requester-and-recipient.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,4 +251,4 @@ The `recipeint` element uses one of the following Codings:
251251

252252

253253
## Related Topics
254-
[ActivityDefinition](../fhir/activitydefinition.md)
254+
[ActivityDefinition](../fhir/activitydefinition.md), [Understanding the Process Authorization Extension](./understanding-the-process-authorization-extension.md)
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
---
2+
title: Understanding the Process Authorization Extension
3+
icon: creative
4+
---
5+
6+
The authorization rules live inside a `<extension url="http://dsf.dev/fhir/StructureDefinition/extension-process-authorization">` block in the `ActivityDefinition`.
7+
8+
```xml
9+
<!-- The outermost container for one complete authorization rule block.
10+
One block covers exactly one message name, one Task profile, and the
11+
requester/recipient rules for it.
12+
Add a second block if you have a second message type. -->
13+
<extension url="http://dsf.dev/fhir/StructureDefinition/extension-process-authorization">
14+
15+
<!-- Which BPMN Message name does this rule apply to? -->
16+
<extension url="message-name">
17+
<valueString value="startDicProcess" />
18+
</extension>
19+
20+
<!-- Which StructureDefinition (Task profile) must an incoming Task conform to? -->
21+
<extension url="task-profile">
22+
<valueCanonical value="http://example.org/fhir/StructureDefinition/task-start-dic-process|#{version}" />
23+
</extension>
24+
25+
<!-- ─── REQUESTER: Who is allowed to SEND this message? ────────────────
26+
valueCoding selects a rule from the DSF authorization CodeSystem. -->
27+
<extension url="requester">
28+
<valueCoding>
29+
<!-- The CodeSystem that defines the available roles.
30+
Always this fixed DSF URL. -->
31+
<system value="http://dsf.dev/fhir/CodeSystem/process-authorization" />
32+
33+
<!-- Which role from that CodeSystem?
34+
LOCAL_ALL = any local client (certificate or OIDC user)
35+
LOCAL_ROLE = any local client with a specific OIDC practitioner role
36+
LOCAL_ORGANIZATION = one specific local organization (needs nested extension)
37+
REMOTE_ORGANIZATION = one specific remote organization (needs nested extension) -->
38+
<code value="LOCAL_ALL" />
39+
</valueCoding>
40+
</extension>
41+
42+
<!-- ─── RECIPIENT: Who is allowed to RECEIVE / execute this process? ───
43+
Same structure as requester above. -->
44+
<extension url="recipient">
45+
<valueCoding>
46+
<system value="http://dsf.dev/fhir/CodeSystem/process-authorization" />
47+
<code value="LOCAL_ALL" />
48+
</valueCoding>
49+
</extension>
50+
51+
</extension>
52+
```
53+
54+
When access needs to be restricted to a **specific organization** or a **specific OIDC role**, a nested `<extension>` inside `<valueCoding>` has to be added to narrow the rule further:
55+
56+
```xml
57+
<!-- Example: only a specific remote organization may send -->
58+
<extension url="requester">
59+
<valueCoding>
60+
<!-- Narrow down: which specific organization? -->
61+
<extension url="http://dsf.dev/fhir/StructureDefinition/extension-process-authorization-organization">
62+
<valueIdentifier>
63+
<!-- The DSF system for organization identifiers -->
64+
<system value="http://dsf.dev/sid/organization-identifier" />
65+
<!-- The concrete organization identifier -->
66+
<value value="cos.dsf.test" />
67+
</valueIdentifier>
68+
</extension>
69+
<system value="http://dsf.dev/fhir/CodeSystem/process-authorization" />
70+
<!-- Must match the nested extension: REMOTE_ORGANIZATION for a remote org,
71+
LOCAL_ORGANIZATION for a local org -->
72+
<code value="REMOTE_ORGANIZATION" />
73+
</valueCoding>
74+
</extension>
75+
76+
<!-- Example: only local clients with the OIDC practitioner role DSF_ADMIN -->
77+
<extension url="requester">
78+
<valueCoding>
79+
<!-- Narrow down: which OIDC practitioner role? -->
80+
<extension url="http://dsf.dev/fhir/StructureDefinition/extension-process-authorization-practitioner-role">
81+
<valueCoding>
82+
<system value="http://dsf.dev/fhir/CodeSystem/practitioner-role" />
83+
<code value="DSF_ADMIN" />
84+
</valueCoding>
85+
</extension>
86+
<system value="http://dsf.dev/fhir/CodeSystem/process-authorization" />
87+
<code value="LOCAL_ROLE" />
88+
</valueCoding>
89+
</extension>
90+
```
91+
92+
> **Reading tip:** The outer `<code>` can be thought of as the *category* of the rule (e.g. "a local client with a specific role") and the inner `<extension>` as the *precise value* within that category (e.g. "the role `DSF_ADMIN`"). Both are needed together.
93+
94+
## Related Topics
95+
[ActivityDefinition](../fhir/activitydefinition.md), [Creating ActivityDefinitions](../guides/creating-activity-definitions.md), [Requester and Recipient Examples](./requester-and-recipient.md)

‎docs/src/process-development/api-v2/guides/creating-activity-definitions.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -757,4 +757,4 @@ All other elements can technically be omitted. Still, the following elements are
757757
</details>
758758

759759
## Related Topics
760-
[ActivityDefinition](../fhir/activitydefinition.md), [Creating CodeSystems for DSF Processes](creating-codesystems-for-dsf-processes.md), [Creating ValueSets for DSF Processes](creating-valuesets-for-dsf-processes.md), [Task](../fhir/task.md)
760+
[ActivityDefinition](../fhir/activitydefinition.md), [Creating CodeSystems for DSF Processes](creating-codesystems-for-dsf-processes.md), [Creating ValueSets for DSF Processes](creating-valuesets-for-dsf-processes.md), [Requester and Recipient Examples](../dsf/requester-and-recipient.md), [Task](../fhir/task.md), [Understanding the Process Authorization Extension](../dsf/understanding-the-process-authorization-extension.md)

‎docs/src/process-development/api-v2/guides/user-tasks-in-the-dsf.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Below is a template for a [Questionnaire](https://www.hl7.org/fhir/R4/questionna
2121
```xml
2222
<Questionnaire xmlns="http://hl7.org/fhir">
2323
<meta>
24-
<profile value="http://dsf.dev/fhir/StructureDefinition/questionnaire"/>
24+
<profile value="http://dsf.dev/fhir/StructureDefinition/questionnaire|2.0.0"/>
2525
<tag>
2626
<system value="http://dsf.dev/fhir/CodeSystem/read-access-tag"/>
2727
<code value="ALL"/>

‎docs/src/process-development/linter-tool/validation.md‎

Lines changed: 178 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,28 @@ The linter performs comprehensive validation on BPMN 2.0 process definitions usi
112112
- The process engine only deploys and executes processes marked as executable
113113
- Non-executable processes are typically used for documentation or as templates
114114

115+
##### Process Version Tag Validation
116+
117+
- **Requirement**:
118+
- Process must define `camunda:versionTag`
119+
- Expected placeholder value in DSF process plugins: `#{version}`
120+
- Error: `BPMN_PROCESS_VERSION_TAG_MISSING_OR_EMPTY` (missing, empty/blank, or literal `"null"`)
121+
- Warning: `BPMN_PROCESS_VERSION_TAG_NO_PLACEHOLDER` (present but without `#{version}`)
122+
- Success: `SUCCESS` when `camunda:versionTag` contains `#{version}`
123+
124+
- **Valid Example**:
125+
- ✅ `<process id="example_process" isExecutable="true" camunda:versionTag="#{version}">`
126+
127+
- **Error Examples**:
128+
- ❌ `<process id="example_process" isExecutable="true">` (missing `camunda:versionTag`)
129+
- ❌ `<process id="example_process" isExecutable="true" camunda:versionTag="">` (empty value)
130+
- ❌ `<process id="example_process" isExecutable="true" camunda:versionTag="null">` (literal `null`)
131+
132+
- **Warning Examples**:
133+
- ⚠️ `<process id="example_process" isExecutable="true" camunda:versionTag="1.0.0">`
134+
- ⚠️ `<process id="example_process" isExecutable="true" camunda:versionTag="some-fixed-version">`
135+
136+
115137
#### Task Validation
116138

117139
##### Service Tasks
@@ -638,9 +660,51 @@ Task resources are validated against the DSF Task base profile (`http://dsf.dev/
638660
- Error: `FHIR_TASK_INPUT_SLICE_COUNT_BELOW_SLICE_MIN`
639661
- Error: `FHIR_TASK_INPUT_SLICE_COUNT_EXCEEDS_SLICE_MAX`
640662

641-
- **Terminology Validation**:
642-
- Code/system combinations validated against DSF CodeSystems
643-
- Error: `FHIR_TASK_UNKNOWN_CODE`
663+
- **Terminology Validation**:
664+
- Generic coding validation (outside `Task.input.type.coding`) checks code/system pairs against known DSF CodeSystems
665+
- Error: `FHIR_TASK_UNKNOWN_CODE`
666+
667+
- **Task.input.type.coding Terminology Validation (binding-driven)**:
668+
- Validation for `Task.input.type.coding` is performed in three strict, ordered checks:
669+
1. **Known CodeSystem check**:
670+
- `Task.input.type.coding.system` must be a known CodeSystem URI
671+
- Error: `FHIR_TASK_INPUT_CODING_SYSTEM_UNKNOWN`
672+
- If this check fails, subsequent checks for that input are skipped
673+
2. **Binding context check**:
674+
- The system must be allowed by the slice-specific binding context from the referenced `StructureDefinition`
675+
- Resolution order:
676+
- `fixedUri` on `Task.input:sliceName.type.coding.system` (exact match required)
677+
- `binding.valueSet` on `Task.input:sliceName.type` (fallback: `...type.coding`) and membership in `ValueSet.compose.include.system`
678+
- Error: `FHIR_TASK_INPUT_CODING_SYSTEM_NOT_IN_VALUE_SET`
679+
- If this check fails, code validation for that input is skipped
680+
3. **Code-in-system check**:
681+
- `Task.input.type.coding.code` must exist in the specified CodeSystem
682+
- Error: `FHIR_TASK_INPUT_CODING_CODE_UNKNOWN_FOR_SYSTEM`
683+
684+
- **Binding Resolution Notes**:
685+
- ValueSet checks are strict and context-aware; no permissive fallback to unrelated ValueSets is used
686+
- If a slice declares `binding.valueSet` but the ValueSet is not loaded into the cache, validation fails explicitly with:
687+
`FHIR_TASK_INPUT_CODING_SYSTEM_NOT_IN_VALUE_SET`
688+
- If no resolvable binding context exists for a slice (`fixedUri` and `binding.valueSet` both missing), validation fails explicitly with:
689+
`FHIR_TASK_INPUT_CODING_SYSTEM_NOT_IN_VALUE_SET`
690+
- Inputs already failing structural checks (`FHIR_TASK_INPUT_REQUIRED_CODING_SYSTEM_AND_CODING_CODE`) are not re-reported by terminology checks
691+
692+
- **Task.input Fixed Constraint Validation (`fixedUri` / `fixedCode`)**:
693+
- Validates actual `Task.input.type.coding.system` and `Task.input.type.coding.code` pairs against fixed constraints declared in the referenced `StructureDefinition` slices
694+
- Validation direction is **Task → StructureDefinition** (actual instance values are checked against allowed fixed pairs)
695+
- **System mismatch for existing code**:
696+
- If a Task input code exists in SD constraints, but with a different system
697+
- Error: `FHIR_TASK_INPUT_FIXED_URI_MISMATCH`
698+
- **Code mismatch for existing system**:
699+
- If a Task input system exists in SD constraints, but with a different code
700+
- Error: `FHIR_TASK_INPUT_FIXED_CODE_MISMATCH`
701+
- **Pair not allowed by SD constraints**:
702+
- If a non-BPMN Task input `(system, code)` pair is not defined by any SD `fixedUri/fixedCode` constraint
703+
- Error: `FHIR_TASK_INPUT_PAIR_NOT_ALLOWED_BY_SD`
704+
- **Validation Behavior**:
705+
- Check runs only when profile can be resolved and fixed constraints are extractable from the referenced `StructureDefinition`
706+
- BPMN message inputs (`http://dsf.dev/fhir/CodeSystem/bpmn-message`) are excluded from this specific pair-not-allowed check because they are validated in dedicated Task input rules
707+
644708

645709
#### StructureDefinition Resources
646710

@@ -702,6 +766,24 @@ According to FHIR profiling specification §5.1.0.14:
702766
- No individual slice's maximum cardinality may exceed base element's maximum
703767
- Error: `STRUCTURE_DEFINITION_SLICE_MAX_TOO_HIGH`
704768

769+
##### Binding and Fixed Coding Validation
770+
- **`binding.valueSet` Reference Validation**:
771+
- Checks whether `binding.valueSet` URLs in differential elements can be resolved to a known ValueSet in project resources
772+
- For unresolved references with `strength="required"`:
773+
- Warning: `STRUCTURE_DEFINITION_BINDING_VALUESET_UNRESOLVED`
774+
- For unresolved references with non-required strengths (`extensible`, `preferred`, `example`):
775+
- Info: `STRUCTURE_DEFINITION_BINDING_VALUESET_UNRESOLVED_NON_REQUIRED`
776+
- Success: `SUCCESS` when the referenced ValueSet is resolvable
777+
- **`fixedUri` / `fixedCode` Validation Against CodeSystems**:
778+
- For differential elements using `fixedUri` on `*.system`, verifies that the referenced CodeSystem exists in project terminology cache
779+
- If referenced CodeSystem is unknown:
780+
- Info: `STRUCTURE_DEFINITION_FIXED_URI_CODESYSTEM_NOT_FOUND`
781+
- Note: `fixedCode` validation is skipped for this path when CodeSystem is unknown
782+
- If `fixedCode` is present and the CodeSystem is known, verifies that the code exists in that CodeSystem
783+
- Error: `STRUCTURE_DEFINITION_FIXED_CODE_NOT_IN_CODESYSTEM`
784+
- Success: `SUCCESS` when `fixedUri` resolves (and `fixedCode`, if present, is valid)
785+
786+
705787
#### ValueSet Resources
706788

707789
ValueSet resources are validated against the DSF ValueSet base profile.
@@ -1023,6 +1105,99 @@ ValueSet resources are validated against the DSF ValueSet base profile.
10231105
- `dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/ProcessPluginDefinition.java`
10241106
- The `getResourceVersion()` method extracts the resource version from the plugin version
10251107

1108+
#### Spring Configuration Registration
1109+
1110+
In the DSF runtime, the Camunda engine does not instantiate Java delegate or
1111+
listener classes directly. Spring creates those instances via `@Bean` methods
1112+
declared in `@Configuration` classes. For those beans to be available to the
1113+
BPE at runtime, the corresponding `@Configuration` classes must be explicitly
1114+
returned by `ProcessPluginDefinition#getSpringConfigurations()`. Forgetting
1115+
to add a `@Bean` for a BPMN-referenced class typically surfaces as a
1116+
`BeanCreationException` or `ClassNotFoundException` only at deployment time.
1117+
1118+
- **Every BPMN-referenced class must be provided as a `@Bean`**:
1119+
- The linter collects every `camunda:class` reference used by
1120+
`serviceTask`, `sendTask`, `messageEventDefinition`,
1121+
`camunda:executionListener` and `camunda:taskListener` elements in the
1122+
plugin's BPMN files (List 1).
1123+
- The linter calls `getSpringConfigurations()` to get the registered
1124+
`@Configuration` classes and inspects the return types of their
1125+
`@Bean` methods (List 2).
1126+
- For every class in List 1, the linter checks whether it is provided
1127+
as a `@Bean` (exact type or supertype) in any `@Configuration` from
1128+
List 2.
1129+
- If a BPMN-referenced class has **no matching `@Bean`** in any
1130+
registered configuration, this is reported as an error.
1131+
- Error: `PLUGIN_DEFINITION_SPRING_CONFIGURATION_MISSING`
1132+
- Success: `SUCCESS` when all BPMN delegate/listener references are
1133+
covered by a `@Bean` in a registered `@Configuration`, or when no
1134+
BPMN delegate/listener references exist.
1135+
1136+
- **Code Example**:
1137+
```java
1138+
@Override
1139+
public List<Class<?>> getSpringConfigurations() {
1140+
return List.of(DataSharingConfig.class, DataSharingVariablesConfig.class);
1141+
}
1142+
```
1143+
```java
1144+
@Configuration
1145+
public class DataSharingConfig {
1146+
1147+
// Every class referenced via camunda:class in the BPMN must have
1148+
// a corresponding @Bean here (or in another registered @Configuration).
1149+
@Bean
1150+
public SelectDicTargets selectDicTargets() {
1151+
return new SelectDicTargets(api);
1152+
}
1153+
1154+
@Bean
1155+
public SelectDmsTarget selectDmsTarget() {
1156+
return new SelectDmsTarget(api);
1157+
}
1158+
}
1159+
```
1160+
#### Spring Bean Scope (`SpringConfigurationLinter`)
1161+
1162+
In Camunda and the Data Sharing Framework (DSF), Spring beans that act as delegates, execution listeners, or task listeners should usually be configured with **prototype** scope (`SCOPE_PROTOTYPE`). If the `@Scope` annotation is omitted, Spring defaults to **singleton**. That can be acceptable for strictly stateless classes; as soon as someone adds mutable instance state, the risk of race conditions and hard-to-debug concurrency bugs rises sharply—often only visible under load.
1163+
1164+
After a BPMN-referenced class is **covered** by a `@Bean` in a registered `@Configuration` (see [Spring Configuration Registration](#spring-configuration-registration)), the linter inspects the covering `@Bean` method for `@Scope` and the referenced implementation class for **mutable** instance fields (not `static`, not `final`). Emitted rules:
1165+
1166+
- **Success** – `SPRING_BEAN_SCOPE_PROTOTYPE`: `@Scope` is **prototype** (e.g. `ConfigurableBeanFactory.SCOPE_PROTOTYPE` or `@Scope("prototype")`). No further scope items for that reference in this pass.
1167+
- **Error** – `SPRING_BEAN_SCOPE_MUTABLE_SINGLETON`: The bean is **effectively singleton** (missing `@Scope` *or* explicit non-prototype scope such as `singleton`) **and** the referenced class has **mutable** instance fields. Emitted **once** per reference before the following warnings.
1168+
- **Warning** – `SPRING_BEAN_SCOPE_MISSING`: The covering `@Bean` has **no** `@Scope` (Spring’s default is singleton, which is risky for Camunda hooks unless the implementation is provably stateless).
1169+
- **Warning** – `SPRING_BEAN_SCOPE_SINGLETON_EXPLICIT`: `@Scope` is **explicitly** set to a **non-prototype** value (typically `singleton`); the developer should verify that the delegate or listener is safe to share across process instances.
1170+
1171+
**Why this matters:** The rule stays aligned with DSF wiring (only configurations from `getSpringConfigurations()`) and surfaces dangerous combinations—singleton or default-singleton scope plus mutable instance state—before they appear in production.
1172+
1173+
**Example scenarios**
1174+
1175+
*Missing `@Scope` → warning (and error if the delegate/listener class has mutable instance fields)*
1176+
1177+
```java
1178+
@Bean
1179+
// Missing @Scope annotation
1180+
public SetCorrelationKeyListener setCorrelationKeyListener() {
1181+
return new SetCorrelationKeyListener(api);
1182+
}
1183+
```
1184+
1185+
*Explicit singleton → warning (and error if mutable instance fields exist)*
1186+
```java
1187+
@Bean
1188+
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) // or @Scope("singleton")
1189+
public SetCorrelationKeyListener setCorrelationKeyListener() {
1190+
return new SetCorrelationKeyListener(api);
1191+
}
1192+
```
1193+
*Prototype (recommended) → success*
1194+
```java
1195+
@Bean
1196+
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // or @Scope("prototype")
1197+
public SetCorrelationKeyListener setCorrelationKeyListener() {
1198+
return new SetCorrelationKeyListener(api);
1199+
}
1200+
```
10261201
#### Leftover Resource Detection
10271202

10281203
The linter performs project-level analysis to identify unreferenced resources:

0 commit comments

Comments
 (0)