Skip to content

Commit c6f0b58

Browse files
Do runtime check to ensure update validator has the same parameters as the update it validates (#2323)
Check update validator has a matching update method
1 parent 1d86a57 commit c6f0b58

File tree

2 files changed

+113
-15
lines changed

2 files changed

+113
-15
lines changed

temporal-sdk/src/main/java/io/temporal/common/metadata/POJOWorkflowInterfaceMetadata.java

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,7 @@
2020

2121
package io.temporal.common.metadata;
2222

23-
import io.temporal.workflow.QueryMethod;
24-
import io.temporal.workflow.SignalMethod;
25-
import io.temporal.workflow.WorkflowInterface;
26-
import io.temporal.workflow.WorkflowMethod;
23+
import io.temporal.workflow.*;
2724
import java.lang.reflect.Method;
2825
import java.lang.reflect.Modifier;
2926
import java.util.*;
@@ -97,7 +94,7 @@ public static POJOWorkflowInterfaceMetadata newInstanceSkipWorkflowAnnotationChe
9794
* <ul>
9895
* <li>{@code anInterface} to be annotated with {@link WorkflowInterface}
9996
* <li>All methods of {@code anInterface} to be annotated with {@link WorkflowMethod}, {@link
100-
* QueryMethod} or {@link SignalMethod}
97+
* QueryMethod}, {@link UpdateMethod}, {@link UpdateValidatorMethod} or {@link SignalMethod}
10198
* </ul>
10299
*
103100
* @param anInterface interface to create metadata for
@@ -137,15 +134,17 @@ public static POJOWorkflowInterfaceMetadata newInstance(
137134
* #newInstance(Class, boolean)} is that the interfaces passing here can be not annotated with
138135
* {@link WorkflowInterface} at all and even not having {@link WorkflowInterface} as a parent. It
139136
* also can have all kinds of additional methods that are not annotated with {@link
140-
* WorkflowMethod}, {@link QueryMethod} or {@link SignalMethod}. Such unannotated methods or
141-
* methods that are not part of some {@link WorkflowInterface} will be ignored.
137+
* WorkflowMethod}, {@link QueryMethod}, {@link UpdateMethod}, {@link UpdateValidatorMethod} or
138+
* {@link SignalMethod}. Such unannotated methods or methods that are not part of some {@link
139+
* WorkflowInterface} will be ignored.
142140
*
143141
* @param anInterface interface to create metadata for
144142
* @param forceProcessWorkflowMethods if true, methods of the {@code anInterface} that are
145-
* annotated with {@link WorkflowMethod}, {@link QueryMethod} or {@link SignalMethod} are
146-
* processed like {@code current} is a workflow interface even if it is not annotated with
147-
* {@link WorkflowInterface} itself. For example, this is useful when we have a query-only
148-
* interface to register as a listener or call as a stub.
143+
* annotated with {@link WorkflowMethod}, {@link QueryMethod}, {@link UpdateMethod}, {@link
144+
* UpdateValidatorMethod} or {@link SignalMethod} are processed like {@code current} is a
145+
* workflow interface even if it is not annotated with {@link WorkflowInterface} itself. For
146+
* example, this is useful when we have a query-only interface to register as a listener or
147+
* call as a stub.
149148
* @return metadata for the interface
150149
*/
151150
static POJOWorkflowInterfaceMetadata newImplementationInstance(
@@ -161,10 +160,11 @@ static POJOWorkflowInterfaceMetadata newImplementationInstance(
161160
* @param validateWorkflowAnnotation check if the interface has a {@link WorkflowInterface}
162161
* annotation with methods
163162
* @param forceProcessWorkflowMethods if true, methods of the {@code anInterface} that are
164-
* annotated with {@link WorkflowMethod}, {@link QueryMethod} or {@link SignalMethod} are
165-
* processed like {@code current} is a workflow interface even if it is not annotated with
166-
* {@link WorkflowInterface} itself. For example, this is useful when we have a query-only
167-
* interface to register as a listener or call as a stub.
163+
* annotated with {@link WorkflowMethod}, {@link QueryMethod}, {@link UpdateMethod}, {@link
164+
* UpdateValidatorMethod} or {@link SignalMethod} are processed like {@code current} is a
165+
* workflow interface even if it is not annotated with {@link WorkflowInterface} itself. For
166+
* example, this is useful when we have a query-only interface to register as a listener or
167+
* call as a stub.
168168
*/
169169
private static POJOWorkflowInterfaceMetadata newInstanceInternal(
170170
Class<?> anInterface,
@@ -189,6 +189,39 @@ private static POJOWorkflowInterfaceMetadata newInstanceInternal(
189189
"Interface doesn't contain any methods: " + anInterface.getName());
190190
}
191191
}
192+
// Validate that all @UpdateValidatorMethod methods have corresponding @UpdateMethod
193+
Map<String, POJOWorkflowMethodMetadata> updateMethods = new HashMap<>();
194+
for (POJOWorkflowMethodMetadata methodMetadata : result.methods.values()) {
195+
if (methodMetadata.getType() == WorkflowMethodType.UPDATE) {
196+
updateMethods.put(methodMetadata.getName(), methodMetadata);
197+
}
198+
}
199+
200+
for (POJOWorkflowMethodMetadata methodMetadata : result.methods.values()) {
201+
if (methodMetadata.getType() == WorkflowMethodType.UPDATE_VALIDATOR) {
202+
UpdateValidatorMethod validator =
203+
methodMetadata.getWorkflowMethod().getAnnotation(UpdateValidatorMethod.class);
204+
POJOWorkflowMethodMetadata update = updateMethods.get(validator.updateName());
205+
if (update == null) {
206+
throw new IllegalArgumentException(
207+
"Missing @UpdateMethod with name \""
208+
+ validator.updateName()
209+
+ "\" for @UpdateValidatorMethod \""
210+
+ methodMetadata.getWorkflowMethod().getName()
211+
+ "\"");
212+
}
213+
if (!Arrays.equals(
214+
update.getWorkflowMethod().getGenericParameterTypes(),
215+
methodMetadata.getWorkflowMethod().getGenericParameterTypes())) {
216+
throw new IllegalArgumentException(
217+
"Update method \""
218+
+ update.getWorkflowMethod().getName()
219+
+ "\" and Validator method \""
220+
+ methodMetadata.getWorkflowMethod().getName()
221+
+ "\" do not have the same parameters");
222+
}
223+
}
224+
}
192225
return result;
193226
}
194227

temporal-sdk/src/test/java/io/temporal/common/metadata/POJOWorkflowInterfaceMetadataTest.java

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,33 @@ private Class<?> generateWorkflowInterfaceWithQueryMethod(
211211
return methodDefinition.make().load(this.getClass().getClassLoader()).getLoaded();
212212
}
213213

214+
@Test
215+
public void workflowInterfaceWithUpdateValidator() {
216+
POJOWorkflowInterfaceMetadata metadata =
217+
POJOWorkflowInterfaceMetadata.newInstance(LUpdate.class);
218+
}
219+
220+
@Test
221+
public void workflowInterfaceWithBadUpdateValidator() {
222+
assertThrows(
223+
IllegalArgumentException.class,
224+
() -> POJOWorkflowInterfaceMetadata.newInstance(LUpdateBadValidator.class));
225+
}
226+
227+
@Test
228+
public void workflowInterfaceValidatorWithNoUpdate() {
229+
assertThrows(
230+
IllegalArgumentException.class,
231+
() -> POJOWorkflowInterfaceMetadata.newInstance(LUpdateValidatorWithNoUpdate.class));
232+
}
233+
234+
@Test
235+
public void interfaceWithInvalidValidator() {
236+
assertThrows(
237+
IllegalArgumentException.class,
238+
() -> POJOWorkflowInterfaceMetadata.newImplementationInstance(J.class, true));
239+
}
240+
214241
public interface O {
215242
void someMethod();
216243
}
@@ -272,12 +299,50 @@ public interface I {
272299
void i();
273300
}
274301

302+
public interface J {
303+
@UpdateValidatorMethod(updateName = "update")
304+
void validate(String input);
305+
}
306+
275307
@WorkflowInterface
276308
public interface K {
277309
@WorkflowMethod
278310
void f(Map<String, EncodedValuesTest.Pair> input);
279311
}
280312

313+
@WorkflowInterface
314+
public interface L {
315+
@WorkflowMethod
316+
void l();
317+
}
318+
319+
@WorkflowInterface
320+
public interface LUpdate extends L {
321+
@UpdateMethod
322+
void update(Map<String, Integer> input);
323+
324+
@UpdateValidatorMethod(updateName = "update")
325+
void validate(Map<String, Integer> input);
326+
}
327+
328+
@WorkflowInterface
329+
public interface LUpdateBadValidator extends L {
330+
@UpdateMethod
331+
void update(Map<String, Integer> input);
332+
333+
@UpdateValidatorMethod(updateName = "update")
334+
void validate(Map<String, String> input);
335+
}
336+
337+
@WorkflowInterface
338+
public interface LUpdateValidatorWithNoUpdate extends L {
339+
@UpdateMethod
340+
void update(Map<String, Integer> input);
341+
342+
@UpdateValidatorMethod(updateName = "wrongUpdate")
343+
void validate(Map<String, Integer> input);
344+
}
345+
281346
public interface DE extends D, E {}
282347

283348
@WorkflowInterface

0 commit comments

Comments
 (0)