Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve interface for InngestFunction creation #46

Merged
merged 4 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions inngest-core/src/main/kotlin/com/inngest/Comm.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package com.inngest

import com.beust.klaxon.Json
import com.beust.klaxon.Klaxon
import com.inngest.signingkey.getAuthorizationHeader
import com.fasterxml.jackson.databind.ObjectMapper
import com.inngest.signingkey.getAuthorizationHeader
import java.io.IOException

data class ExecutionRequestPayload(
Expand All @@ -20,11 +20,11 @@ data class ExecutionContext(
val env: String,
)

data class RegistrationRequestPayload(
internal data class RegistrationRequestPayload(
val appName: String,
val deployType: String = "ping",
val framework: String,
val functions: List<FunctionConfig> = listOf(),
val functions: List<InternalFunctionConfig> = listOf(),
val sdk: String,
val url: String,
val v: String,
Expand All @@ -48,12 +48,13 @@ data class CommError(
)

class CommHandler(
val functions: Map<String, InngestFunction>,
functions: Map<String, InngestFunction>,
val client: Inngest,
val config: ServeConfig,
private val framework: SupportedFrameworkName,
) {
val headers = Environment.inngestHeaders(framework).plus(client.headers)
internal val functions = functions.mapValues { (_, fn) -> fn.toInngestFunction() }

fun callFunction(
functionId: String,
Expand Down Expand Up @@ -111,8 +112,8 @@ class CommHandler(
return mapper.writeValueAsString(requestBody)
}

private fun getFunctionConfigs(): List<FunctionConfig> {
val configs: MutableList<FunctionConfig> = mutableListOf()
private fun getFunctionConfigs(): List<InternalFunctionConfig> {
val configs: MutableList<InternalFunctionConfig> = mutableListOf()
functions.forEach { entry -> configs.add(entry.value.getFunctionConfig(getServeUrl())) }
return configs
}
Expand Down
24 changes: 12 additions & 12 deletions inngest-core/src/main/kotlin/com/inngest/Function.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import com.beust.klaxon.Json
import java.util.function.BiFunction

// IDEA: Use data classes
data class FunctionOptions(
internal data class InternalFunctionOptions(
val id: String,
val name: String,
val triggers: Array<FunctionTrigger>,
val triggers: Array<InternalFunctionTrigger>,
)

data class FunctionTrigger
internal data class InternalFunctionTrigger
@JvmOverloads
constructor(
@Json(serializeNull = false) val event: String? = null,
Expand Down Expand Up @@ -68,10 +68,10 @@ data class StepConfig(
val runtime: HashMap<String, String> = hashMapOf("type" to "http"),
)

data class FunctionConfig(
internal data class InternalFunctionConfig(
val id: String,
val name: String,
val triggers: Array<FunctionTrigger>,
val triggers: Array<InternalFunctionTrigger>,
val steps: Map<String, StepConfig>,
)

Expand Down Expand Up @@ -99,18 +99,18 @@ data class FunctionContext(

data class SendEventPayload(val event_ids: Array<String>)

interface Function {
internal interface Function {
fun id(): String

fun config(): FunctionConfig
fun config(): InternalFunctionConfig
}

// TODO: make this implement the Function interface
open class InngestFunction(
val config: FunctionOptions,
internal open class InternalInngestFunction(
val config: InternalFunctionOptions,
val handler: (ctx: FunctionContext, step: Step) -> Any?,
) {
constructor(config: FunctionOptions, handler: BiFunction<FunctionContext, Step, out Any>) : this(
constructor(config: InternalFunctionOptions, handler: BiFunction<FunctionContext, Step, out Any>) : this(
config,
handler.toKotlin(),
)
Expand Down Expand Up @@ -193,10 +193,10 @@ open class InngestFunction(
}
}

fun getFunctionConfig(serveUrl: String): FunctionConfig {
fun getFunctionConfig(serveUrl: String): InternalFunctionConfig {
// TODO use URL objects instead of strings so we can fetch things like scheme
val scheme = serveUrl.split("://")[0]
return FunctionConfig(
return InternalFunctionConfig(
id = config.id,
name = config.name,
triggers = config.triggers,
Expand Down
74 changes: 74 additions & 0 deletions inngest-core/src/main/kotlin/com/inngest/InngestFunction.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.inngest

import com.beust.klaxon.Json

@Target(AnnotationTarget.CLASS)
@MustBeDocumented
annotation class FunctionConfig(
val id: String,
val name: String,
)

@Target(AnnotationTarget.CLASS)
@Repeatable
annotation class FunctionEventTrigger(
@Json(serializeNull = false) val event: String,
)

@Target(AnnotationTarget.CLASS)
@Repeatable
@MustBeDocumented
annotation class FunctionCronTrigger(
@Json(serializeNull = false) val cron: String,
)

@Target(AnnotationTarget.CLASS)
@Repeatable
@MustBeDocumented
annotation class FunctionIfTrigger(
@Json(serializeNull = false) val `if`: String,
)

abstract class InngestFunction {
abstract fun execute(
ctx: FunctionContext,
step: Step,
): Any?

private val config = this::class.annotations.find { it.annotationClass == FunctionConfig::class }

fun id(): String {
if (config == null || config !is FunctionConfig) {
throw Exception("InngestFuncConfig annotation is required to setup an InngestFunc")
}
return config.id
}

internal fun toInngestFunction(): InternalInngestFunction {
if (config == null || config !is FunctionConfig) {
throw Exception("FunctionConfig annotation is required to setup an InngestFunction")
}
val triggers = buildEventTriggers() + buildCronTriggers() + buildIfTriggers()
val fnConfig =
InternalFunctionOptions(
id = config.id,
name = config.name,
triggers = triggers.toTypedArray(),
)

return InternalInngestFunction(fnConfig, this::execute)
}

// TODO: DRY this
private fun buildEventTriggers(): List<InternalFunctionTrigger> =
this::class.annotations.filter { it.annotationClass == FunctionEventTrigger::class }
.map { InternalFunctionTrigger(event = (it as FunctionEventTrigger).event) }

private fun buildCronTriggers(): List<InternalFunctionTrigger> =
this::class.annotations.filter { it.annotationClass == FunctionCronTrigger::class }
.map { InternalFunctionTrigger(cron = (it as FunctionCronTrigger).cron) }

private fun buildIfTriggers(): List<InternalFunctionTrigger> =
this::class.annotations.filter { it.annotationClass == FunctionIfTrigger::class }
.map { InternalFunctionTrigger(event = (it as FunctionIfTrigger).`if`) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,70 +5,17 @@
import com.inngest.springboot.InngestConfiguration;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.time.Duration;
import java.util.HashMap;
import java.util.function.BiFunction;

@Configuration
public class DemoConfiguration extends InngestConfiguration {

@Override
public HashMap<String, InngestFunction> functions() {
String followUpEvent = "user.signup.completed";
FunctionTrigger fnTrigger = new FunctionTrigger("user-signup");
FunctionTrigger[] triggers = {fnTrigger};
FunctionOptions fnConfig = new FunctionOptions("fn-id-slug", "My function!", triggers);

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

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

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);
return new Result(y + 10);
}, Result.class);

System.out.println("res" + res);

step.waitForEvent("wait-for-hello", "hello", "10m", null);

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.sum : 0) * add, Integer.class);

HashMap<String, String> data = new HashMap<String, String>() {{
put("hello", "world");
}};
step.sendEvent("followup-event-id", new InngestEvent(followUpEvent, data));

return new HashMap<String, String>() {{
put("message", "cool - this finished running");
}};
};

FunctionTrigger followupFnTrigger = new FunctionTrigger(followUpEvent);
FunctionOptions followupFnConfig = new FunctionOptions(
"fn-follow-up",
"Follow up function!",
new FunctionTrigger[]{followupFnTrigger}
);
BiFunction<FunctionContext, Step, LinkedHashMap<String, Object>> followupHandler = (ctx, step) -> {
System.out.println("-> follow up handler called " + ctx.getEvent().getName());
return ctx.getEvent().getData();
};

HashMap<String, InngestFunction> functions = new HashMap<>();
functions.put("fn-id-slug", new InngestFunction(fnConfig, handler));
functions.put("fn-follow-up", new InngestFunction(followupFnConfig, followupHandler));

functions.put("fn-id-slug", new UserSignupFunction());
functions.put("fn-follow-up", new FollowupFunction());

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

import com.inngest.*;
import org.jetbrains.annotations.NotNull;

import java.util.LinkedHashMap;

@FunctionConfig(
id = "fn-follow-up",
name = "My follow up function!"
)
@FunctionEventTrigger(event = "user.signup.completed")
@FunctionEventTrigger(event = "random-event")
public class FollowupFunction extends InngestFunction {
@Override
public LinkedHashMap<String, Object> execute(@NotNull FunctionContext ctx, @NotNull Step step) {
System.out.println("-> follow up handler called " + ctx.getEvent().getName());
return ctx.getEvent().getData();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.inngest.springbootdemo;

import com.inngest.*;
import org.jetbrains.annotations.NotNull;

import java.time.Duration;
import java.util.HashMap;

@FunctionConfig(id = "fn-id-slug", name = "My function!")
@FunctionEventTrigger(event = "user-signup")
public class UserSignupFunction extends InngestFunction {
@Override
public HashMap<String, String> execute(@NotNull FunctionContext ctx, @NotNull Step step) {
int x = 10;

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

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);
return new Result(y + 10);
}, Result.class);

System.out.println("res" + res);

step.waitForEvent("wait-for-hello", "hello", "10m", null);

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.sum : 0) * add, Integer.class);

HashMap<String, String> data = new HashMap<String, String>() {{
put("hello", "world");
}};
step.sendEvent("followup-event-id", new InngestEvent("user.signup.completed", data));

return new HashMap<String, String>() {{
put("message", "cool - this finished running");
}};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.inngest.springbootdemo.testfunctions;

import com.inngest.*;

@FunctionConfig(id = "custom-result-fn", name = "Custom Result Function")
@FunctionEventTrigger(event = "test/custom.result.step")
public class CustomStepFunction extends InngestFunction {

private final int count = 0;

@Override
public TestFuncResult execute(FunctionContext ctx, Step step) {
int step1 = step.run("step1", () -> count + 1, Integer.class);
int tmp1 = step1 + 1;

int step2 = step.run("step2", () -> tmp1 + 1, Integer.class);
int tmp2 = step2 + 1;

return step.run("cast-to-type-add-one", () -> {
System.out.println("-> running step 1!! " + tmp2);
return new TestFuncResult(tmp2 + 1);
}, TestFuncResult.class);
}
}

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

import com.inngest.*;

@FunctionConfig(id = "no-step-fn", name = "No Step Function")
@FunctionEventTrigger(event = "test/no-step")
public class EmptyStepFunction extends InngestFunction {
@Override
public String execute(FunctionContext ctx, Step step) {
return "hello world";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.inngest.springbootdemo.testfunctions;

import com.inngest.*;

@FunctionConfig(id = "non-retriable-fn", name = "NonRetriable Function")
@FunctionEventTrigger(event = "test/non.retriable")
public class NonRetriableErrorFunction extends InngestFunction {

@Override
public String execute(FunctionContext ctx, Step step) {
step.run("fail-step", () -> {
throw new NonRetriableError("something fatally went wrong");
}, String.class);

return "Success";
}
}
Loading
Loading