Skip to content

Commit

Permalink
Fix Java interop serialization issues (#19)
Browse files Browse the repository at this point in the history
* Fixes steps serialization when called from Java

It's not possible to call reified inline Kotlin methods from Java
so steps return types will have to be explicitly passed.
Without `reified inline` generic types are lost by at runtime and
cannot be used anymore for deserialization purposes.

https://stackoverflow.com/questions/42741780/how-can-i-call-kotlin-methods-with-reified-generics-from-java/42742119#42742119

https://docs.oracle.com/javase/tutorial/java/generics/erasure.html

* Fix for the empty object serialization issue

For now steps return types will need to be:
    - Public classes
    - Having public `JsonProperty`s
so that getters can be called from the `inngest-core` package.

It's quite limiting I think. Getters do not work as expected with
Klaxon unless it's declared in Kotlin.

But we'll try to improve it later on.
  • Loading branch information
KiKoS0 authored Feb 24, 2024
1 parent 14572df commit aa95363
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 39 deletions.
9 changes: 7 additions & 2 deletions inngest-core/src/main/kotlin/com/inngest/State.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,18 @@ class State(val payloadJson: String) {
return sb.toString()
}

inline fun <reified T> getState(hashedId: String): T? {
inline fun <reified T> getState(hashedId: String): T? = getState(hashedId, T::class.java)

fun <T> getState(
hashedId: String,
type: Class<T>,
): T? {
val mapper = ObjectMapper()
val node: JsonNode = mapper.readTree(payloadJson)
val stepResult = node.path("steps").get(hashedId) ?: throw StateNotFound()
if (stepResult.has("data")) {
val dataNode = stepResult.get("data")
return mapper.treeToValue(dataNode, T::class.java)
return mapper.treeToValue(dataNode, type)
} else if (stepResult.has("error")) {
// TODO - Parse the error and throw it
return null
Expand Down
10 changes: 8 additions & 2 deletions inngest-core/src/main/kotlin/com/inngest/Step.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,20 @@ class Step(val state: State) {
* @param fn the function to run
*/
inline fun <reified T> run(
id: String,
noinline fn: () -> T,
): T = run(id, fn, T::class.java)

fun <T> run(
id: String,
fn: () -> T,
type: Class<T>,
): T {
val hashedId = state.getHashFromId(id)

try {
val stepResult = state.getState<T>(hashedId)
if (stepResult is T) {
val stepResult = state.getState(hashedId, type)
if (stepResult != null) {
return stepResult
}
} catch (e: StateNotFound) {
Expand Down
2 changes: 1 addition & 1 deletion inngest-core/src/test/kotlin/com/inngest/StateTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ internal class StateTest {
val state = State(json)
val hashedId = state.getHashFromId("something-not-in-state")
assertFailsWith<StateNotFound> {
state.getState<DummyClass>(hashedId)
state.getState<Int>(hashedId)
}
}

Expand Down
1 change: 0 additions & 1 deletion inngest-spring-boot-demo/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,3 @@ dependencyManagement {
tasks.withType<Test> {
useJUnitPlatform()
}

Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,6 @@
import java.time.Duration;
import java.util.HashMap;

import com.fasterxml.jackson.annotation.JsonProperty;


class Result {
@JsonProperty("sum")
private final int sum;

public Result(@JsonProperty("sum") int sum) {
this.sum = sum;
}

public int getSum() {
return sum;
}
}

// NOTE: We probably don't need this singleton anymore
// when revisiting the SDK's interface.
public class InngestSingleton {
Expand All @@ -33,33 +17,31 @@ public static synchronized CommHandler getInstance() {
FunctionTrigger[] triggers = {fnTrigger};
FunctionOptions fnConfig = new FunctionOptions("fn-id-slug", "My function!", triggers);

Function2<FunctionContext, Step, Void> handler = (ctx, step) -> {
Function2<FunctionContext, Step, HashMap<String, String>> handler = (ctx, step) -> {
int x = 10;

System.out.println("-> handler called " + ctx.getEvent().getName());

// Steps types are problematic for now as it's not possible to called
// reified types from Java.

// int y = step.run("add-ten", () -> x + 10);
int y = step.run("add-ten", () -> x + 10, Integer.class);

// Result res = step.run("cast-to-type-add-ten", () -> {
// System.out.println("-> running step 1!! " + x);
// // throw new Exception("An error!");
// return new Result(y + 10);
// });
Result res = step.run("cast-to-type-add-ten", () -> {
System.out.println("-> running step 1!! " + x);
return new Result(y + 10);
}, Result.class);

// System.out.println("res" + res);
// int add = step.run("add-one-hundred", () -> {
// System.out.println("-> running step 2 :) " + (res != null ? res.getSum() : ""));
// return (res != null ? res.getSum() : 0) + 100;
// });
System.out.println("res" + res);
int add = step.run("add-one-hundred", () -> {
System.out.println("-> running step 2 :) " + (res != null ? res.sum : ""));
return (res != null ? res.sum : 0) + 100;
}, Integer.class);

step.sleep("wait-one-sec", Duration.ofSeconds(2));

// step.run("last-step", () -> (res != null ? res.getSum() : 0) * add);
step.run("last-step", () -> (res != null ? res.sum : 0) * add, Integer.class);

return null;
return new HashMap<String, String>() {{
put("message", "cool - this finished running");
}};
};
InngestFunction function = new InngestFunction(fnConfig, handler);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.inngest.springbootdemo;

import com.fasterxml.jackson.annotation.JsonProperty;

public class Result {
@JsonProperty("sum")
public final int sum;

public Result(@JsonProperty("sum") int sum) {
this.sum = sum;
}
}

0 comments on commit aa95363

Please sign in to comment.