8
8
import jakarta .validation .constraints .NotBlank ;
9
9
import jakarta .validation .constraints .NotEmpty ;
10
10
import jakarta .validation .constraints .NotNull ;
11
+
11
12
import java .util .ArrayList ;
12
13
import java .util .Arrays ;
13
14
import java .util .Collections ;
17
18
import lombok .extern .slf4j .Slf4j ;
18
19
import org .springframework .beans .factory .annotation .Value ;
19
20
import org .springframework .stereotype .Service ;
20
- import org .springframework .util .StringUtils ;
21
+ import org .apache .commons .lang3 .StringUtils ;
22
+
23
+ import static formflow .library .inputs .FieldNameMarkers .DYNAMIC_FIELD_MARKER ;
21
24
22
25
/**
23
26
* A service that validates flow inputs based on input definition.
@@ -45,8 +48,8 @@ public class ValidationService {
45
48
/**
46
49
* Autoconfigured constructor.
47
50
*
48
- * @param validator Validator from Jakarta package.
49
- * @param actionManager the <code>ActionManager</code> that manages the logic to be run at specific points
51
+ * @param validator Validator from Jakarta package.
52
+ * @param actionManager the <code>ActionManager</code> that manages the logic to be run at specific points
50
53
* @param inputConfigPath the package path where inputs classes are located
51
54
*/
52
55
public ValidationService (Validator validator , ActionManager actionManager ,
@@ -95,18 +98,48 @@ private Map<String, List<String>> performFieldLevelValidation(String flowName, F
95
98
}
96
99
97
100
formSubmission .getFormData ().forEach ((key , value ) -> {
101
+ boolean dynamicField = false ;
98
102
var messages = new ArrayList <String >();
99
103
List <String > annotationNames = null ;
100
104
101
105
if (key .contains ("[]" )) {
102
106
key = key .replace ("[]" , "" );
103
107
}
104
108
109
+ String originalKey = key ;
110
+
111
+ if (key .contains (DYNAMIC_FIELD_MARKER )) {
112
+ dynamicField = true ;
113
+ key = StringUtils .substringBefore (key , DYNAMIC_FIELD_MARKER );
114
+ }
115
+
105
116
try {
106
117
annotationNames = Arrays .stream (flowClass .getDeclaredField (key ).getDeclaredAnnotations ())
107
118
.map (annotation -> annotation .annotationType ().getName ()).toList ();
108
119
} catch (NoSuchFieldException e ) {
109
- throw new RuntimeException (e );
120
+ if (dynamicField ) {
121
+ throw new RuntimeException (
122
+ String .format (
123
+ "Input field '%s' has dynamic field marker '%s' in its name, but we are unable to " +
124
+ "find the field in the input file. Is it a dynamic field?" ,
125
+ originalKey , DYNAMIC_FIELD_MARKER
126
+ )
127
+ );
128
+ } else {
129
+ throw new RuntimeException (e );
130
+ }
131
+ }
132
+
133
+ // if it's acting like a dynamic field, then ensure that it is marked as one
134
+ if (dynamicField ) {
135
+ if (!annotationNames .contains ("formflow.library.data.annotations.DynamicField" )) {
136
+ throw new RuntimeException (
137
+ String .format (
138
+ "Field name '%s' (field: '%s') acts like it's a dynamic field, but the field does not contain the @DynamicField annotation" ,
139
+ key , originalKey
140
+ )
141
+ );
142
+ }
110
143
}
111
144
112
145
if (Collections .disjoint (annotationNames , requiredAnnotationsList ) && value .equals ("" )) {
@@ -118,7 +151,8 @@ private Map<String, List<String>> performFieldLevelValidation(String flowName, F
118
151
.forEach (violation -> messages .add (violation .getMessage ()));
119
152
120
153
if (!messages .isEmpty ()) {
121
- validationMessages .put (key , messages );
154
+ // uses original key to accommodate dynamic input names
155
+ validationMessages .put (originalKey , messages );
122
156
}
123
157
});
124
158
0 commit comments