Skip to content

Commit cd97bc8

Browse files
Key property is missing in the Workflow (#234)
fixes #227 Signed-off-by: Spolti <[email protected]> Signed-off-by: Ricardo Zanini <[email protected]> Co-authored-by: Spolti <[email protected]>
1 parent 0641333 commit cd97bc8

File tree

10 files changed

+277
-14
lines changed

10 files changed

+277
-14
lines changed

api/src/main/java/io/serverlessworkflow/api/serializers/WorkflowSerializer.java

+3
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ public void serialize(Workflow workflow, JsonGenerator gen, SerializerProvider p
5353
gen.writeStringField("id", generateUniqueId());
5454
}
5555

56+
if (workflow.getKey() != null) {
57+
gen.writeStringField("key", workflow.getKey());
58+
}
5659
gen.writeStringField("name", workflow.getName());
5760

5861
if (workflow.getDescription() != null && !workflow.getDescription().isEmpty()) {

api/src/main/resources/schema/workflow.json

+16-9
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@
1111
"properties": {
1212
"id": {
1313
"type": "string",
14-
"description": "Workflow unique identifier",
15-
"minLength": 1
14+
"description": "Workflow unique identifier"
15+
},
16+
"key": {
17+
"type": "string",
18+
"description": "Workflow Domain-specific identifier"
1619
},
1720
"name": {
1821
"type": "string",
@@ -155,10 +158,14 @@
155158
}
156159
}
157160
},
158-
"required": [
159-
"id",
160-
"name",
161-
"version",
162-
"states"
163-
]
164-
}
161+
"required": [
162+
"name",
163+
"version",
164+
"states"
165+
],
166+
"dependencies":
167+
{
168+
"id": { "not": { "required": ["key"] } },
169+
"key": { "not": { "required": ["id"] } }
170+
}
171+
}

api/src/test/java/io/serverlessworkflow/api/test/MarkupToWorkflowTest.java

+42-1
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,11 @@ public void testSpecExamplesParsing(String workflowLocation) {
105105

106106
@ParameterizedTest
107107
@ValueSource(strings = {"/features/applicantrequest.json", "/features/applicantrequest.yml"})
108-
public void testSpecFreatureFunctionRef(String workflowLocation) {
108+
public void testSpecFeatureFunctionRef(String workflowLocation) {
109109
Workflow workflow = Workflow.fromSource(WorkflowTestUtils.readWorkflowFile(workflowLocation));
110110

111111
assertNotNull(workflow);
112+
assertNull(workflow.getKey());
112113
assertNotNull(workflow.getId());
113114
assertNotNull(workflow.getName());
114115
assertNotNull(workflow.getStates());
@@ -118,6 +119,46 @@ public void testSpecFreatureFunctionRef(String workflowLocation) {
118119
assertEquals(1, workflow.getFunctions().getFunctionDefs().size());
119120
}
120121

122+
@ParameterizedTest
123+
@ValueSource(
124+
strings = {
125+
"/features/applicantrequest-with-key.json",
126+
"/features/applicantrequest-with-key.yml"
127+
})
128+
public void testSpecFeatureFunctionRefWithKey(String workflowLocation) {
129+
Workflow workflow = Workflow.fromSource(WorkflowTestUtils.readWorkflowFile(workflowLocation));
130+
131+
assertNotNull(workflow);
132+
assertEquals("applicant-key-request", workflow.getKey());
133+
assertNull(workflow.getId());
134+
assertNotNull(workflow.getName());
135+
assertNotNull(workflow.getStates());
136+
assertTrue(workflow.getStates().size() > 0);
137+
138+
assertNotNull(workflow.getFunctions());
139+
assertEquals(1, workflow.getFunctions().getFunctionDefs().size());
140+
}
141+
142+
@ParameterizedTest
143+
@ValueSource(
144+
strings = {
145+
"/features/applicantrequest-with-id-and-key.json",
146+
"/features/applicantrequest-with-id-and-key.yml"
147+
})
148+
public void testSpecFeatureFunctionRefWithIdAndKey(String workflowLocation) {
149+
Workflow workflow = Workflow.fromSource(WorkflowTestUtils.readWorkflowFile(workflowLocation));
150+
151+
assertNotNull(workflow);
152+
assertEquals("applicant-key-request", workflow.getKey());
153+
assertEquals("applicant-with-key-and-id", workflow.getId());
154+
assertNotNull(workflow.getName());
155+
assertNotNull(workflow.getStates());
156+
assertTrue(workflow.getStates().size() > 0);
157+
158+
assertNotNull(workflow.getFunctions());
159+
assertEquals(1, workflow.getFunctions().getFunctionDefs().size());
160+
}
161+
121162
@ParameterizedTest
122163
@ValueSource(strings = {"/features/vetappointment.json", "/features/vetappointment.yml"})
123164
public void testSpecFreatureEventRef(String workflowLocation) {

api/src/test/resources/examples/applicantrequest.json

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"id": "applicantrequest",
3+
"key": "applicant-key-request",
34
"version": "1.0",
45
"specVersion": "0.8",
56
"name": "Applicant Request Decision Workflow",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"id": "applicant-with-key-and-id",
3+
"key": "applicant-key-request",
4+
"version": "1.0",
5+
"specVersion": "0.8",
6+
"name": "Applicant Request Decision Workflow",
7+
"description": "Determine if applicant request is valid",
8+
"start": "CheckApplication",
9+
"functions": [
10+
{
11+
"name": "sendRejectionEmailFunction",
12+
"operation": "http://myapis.org/applicationapi.json#emailRejection"
13+
}
14+
],
15+
"states":[
16+
{
17+
"name":"CheckApplication",
18+
"type":"switch",
19+
"dataConditions": [
20+
{
21+
"condition": "${ .applicants | .age >= 18 }",
22+
"transition": "StartApplication"
23+
},
24+
{
25+
"condition": "${ .applicants | .age < 18 }",
26+
"transition": "RejectApplication"
27+
}
28+
],
29+
"defaultCondition": {
30+
"transition": "RejectApplication"
31+
}
32+
},
33+
{
34+
"name": "StartApplication",
35+
"type": "operation",
36+
"actions": [
37+
{
38+
"subFlowRef": "startApplicationWorkflowId"
39+
}
40+
],
41+
"end": true
42+
},
43+
{
44+
"name":"RejectApplication",
45+
"type":"operation",
46+
"actionMode":"sequential",
47+
"actions":[
48+
{
49+
"functionRef": {
50+
"refName": "sendRejectionEmailFunction",
51+
"arguments": {
52+
"applicant": "${ .applicant }"
53+
}
54+
}
55+
}
56+
],
57+
"end": true
58+
}
59+
]
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
id: applicant-with-key-and-id
2+
key: applicant-key-request
3+
version: '1.0'
4+
specVersion: '0.8'
5+
name: Applicant Request Decision Workflow
6+
description: Determine if applicant request is valid
7+
start: CheckApplication
8+
functions:
9+
- name: sendRejectionEmailFunction
10+
operation: http://myapis.org/applicationapi.json#emailRejection
11+
states:
12+
- name: CheckApplication
13+
type: switch
14+
dataConditions:
15+
- condition: "${ .applicants | .age >= 18 }"
16+
transition: StartApplication
17+
- condition: "${ .applicants | .age < 18 }"
18+
transition: RejectApplication
19+
defaultCondition:
20+
transition: RejectApplication
21+
- name: StartApplication
22+
type: operation
23+
actions:
24+
- subFlowRef: startApplicationWorkflowId
25+
end: true
26+
- name: RejectApplication
27+
type: operation
28+
actionMode: sequential
29+
actions:
30+
- functionRef:
31+
refName: sendRejectionEmailFunction
32+
arguments:
33+
applicant: "${ .applicant }"
34+
end: true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
{
2+
"key": "applicant-key-request",
3+
"version": "1.0",
4+
"specVersion": "0.8",
5+
"name": "Applicant Request Decision Workflow",
6+
"description": "Determine if applicant request is valid",
7+
"start": "CheckApplication",
8+
"functions": [
9+
{
10+
"name": "sendRejectionEmailFunction",
11+
"operation": "http://myapis.org/applicationapi.json#emailRejection"
12+
}
13+
],
14+
"states":[
15+
{
16+
"name":"CheckApplication",
17+
"type":"switch",
18+
"dataConditions": [
19+
{
20+
"condition": "${ .applicants | .age >= 18 }",
21+
"transition": "StartApplication"
22+
},
23+
{
24+
"condition": "${ .applicants | .age < 18 }",
25+
"transition": "RejectApplication"
26+
}
27+
],
28+
"defaultCondition": {
29+
"transition": "RejectApplication"
30+
}
31+
},
32+
{
33+
"name": "StartApplication",
34+
"type": "operation",
35+
"actions": [
36+
{
37+
"subFlowRef": "startApplicationWorkflowId"
38+
}
39+
],
40+
"end": true
41+
},
42+
{
43+
"name":"RejectApplication",
44+
"type":"operation",
45+
"actionMode":"sequential",
46+
"actions":[
47+
{
48+
"functionRef": {
49+
"refName": "sendRejectionEmailFunction",
50+
"arguments": {
51+
"applicant": "${ .applicant }"
52+
}
53+
}
54+
}
55+
],
56+
"end": true
57+
}
58+
]
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
key: applicant-key-request
2+
version: '1.0'
3+
specVersion: '0.8'
4+
name: Applicant Request Decision Workflow
5+
description: Determine if applicant request is valid
6+
start: CheckApplication
7+
functions:
8+
- name: sendRejectionEmailFunction
9+
operation: http://myapis.org/applicationapi.json#emailRejection
10+
states:
11+
- name: CheckApplication
12+
type: switch
13+
dataConditions:
14+
- condition: "${ .applicants | .age >= 18 }"
15+
transition: StartApplication
16+
- condition: "${ .applicants | .age < 18 }"
17+
transition: RejectApplication
18+
defaultCondition:
19+
transition: RejectApplication
20+
- name: StartApplication
21+
type: operation
22+
actions:
23+
- subFlowRef: startApplicationWorkflowId
24+
end: true
25+
- name: RejectApplication
26+
type: operation
27+
actionMode: sequential
28+
actions:
29+
- functionRef:
30+
refName: sendRejectionEmailFunction
31+
arguments:
32+
applicant: "${ .applicant }"
33+
end: true

validation/src/main/java/io/serverlessworkflow/validation/WorkflowValidatorImpl.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,10 @@ public List<ValidationError> validate() {
112112
List<EventDefinition> events =
113113
workflow.getEvents() != null ? workflow.getEvents().getEventDefs() : null;
114114

115-
if (workflow.getId() == null || workflow.getId().trim().isEmpty()) {
116-
addValidationError("Workflow id should not be empty", ValidationError.WORKFLOW_VALIDATION);
115+
if ((workflow.getId() == null || workflow.getId().trim().isEmpty())
116+
&& (workflow.getKey() == null || workflow.getKey().trim().isEmpty())) {
117+
addValidationError(
118+
"Workflow id or key should not be empty", ValidationError.WORKFLOW_VALIDATION);
117119
}
118120

119121
if (workflow.getVersion() == null || workflow.getVersion().trim().isEmpty()) {

validation/src/test/java/io/serverlessworkflow/validation/test/WorkflowValidationTest.java

+25-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@ public void testIncompleteJsonWithSchemaValidation() {
4444
public void testIncompleteYamlWithSchemaValidation() {
4545
WorkflowValidator workflowValidator = new WorkflowValidatorImpl();
4646
List<ValidationError> validationErrors =
47-
workflowValidator.setSource("---\n" + "id: abc\n").validate();
47+
workflowValidator.setSource("---\n" + "key: abc\n").validate();
4848
Assertions.assertNotNull(validationErrors);
49+
System.out.println(validationErrors);
4950
Assertions.assertEquals(3, validationErrors.size());
5051
}
5152

@@ -93,6 +94,27 @@ public void testWorkflowMissingStates() {
9394
Assertions.assertEquals("No states found", validationErrors.get(0).getMessage());
9495
}
9596

97+
@Test
98+
public void testWorkflowMissingStatesIdAndKey() {
99+
WorkflowValidator workflowValidator = new WorkflowValidatorImpl();
100+
List<ValidationError> validationErrors =
101+
workflowValidator
102+
.setSource(
103+
"{\n"
104+
+ "\t\"name\": \"test workflow\",\n"
105+
+ " \"version\": \"1.0\",\n"
106+
+ " \"start\": \"SomeState\",\n"
107+
+ " \"states\": []\n"
108+
+ "}")
109+
.validate();
110+
Assertions.assertNotNull(validationErrors);
111+
Assertions.assertEquals(2, validationErrors.size());
112+
113+
Assertions.assertEquals(
114+
"Workflow id or key should not be empty", validationErrors.get(0).getMessage());
115+
Assertions.assertEquals("No states found", validationErrors.get(1).getMessage());
116+
}
117+
96118
@Test
97119
public void testOperationStateNoFunctionRef() {
98120
WorkflowValidator workflowValidator = new WorkflowValidatorImpl();
@@ -101,7 +123,7 @@ public void testOperationStateNoFunctionRef() {
101123
.setSource(
102124
"{\n"
103125
+ "\"id\": \"checkInbox\",\n"
104-
+ " \"name\": \"Check Inbox Workflow\",\n"
126+
+ "\"name\": \"Check Inbox Workflow\",\n"
105127
+ "\"description\": \"Periodically Check Inbox\",\n"
106128
+ "\"version\": \"1.0\",\n"
107129
+ "\"start\": \"CheckInbox\",\n"
@@ -140,6 +162,7 @@ public void testOperationStateNoFunctionRef() {
140162
Assertions.assertNotNull(validationErrors);
141163
Assertions.assertEquals(1, validationErrors.size());
142164

165+
// validationErrors.stream().forEach(v -> System.out.println(v.toString()));
143166
Assertions.assertEquals(
144167
"Operation State action functionRef does not reference an existing workflow function definition",
145168
validationErrors.get(0).getMessage());

0 commit comments

Comments
 (0)