diff --git a/inngest-core/build.gradle.kts b/inngest-core/build.gradle.kts index 55b126a8..a0efb163 100644 --- a/inngest-core/build.gradle.kts +++ b/inngest-core/build.gradle.kts @@ -10,7 +10,10 @@ repositories { dependencies { implementation("com.beust:klaxon:5.5") implementation("com.fasterxml.jackson.core:jackson-core:2.16.1") + implementation("com.fasterxml.jackson.core:jackson-databind:2.16.1") + implementation("com.squareup.okhttp3:okhttp:4.12.0") + testImplementation(kotlin("test")) } @@ -20,4 +23,6 @@ tasks.named("test") { } // TODO - Move this to share conventions gradle file -java { toolchain { languageVersion.set(JavaLanguageVersion.of(20)) } } +java { + toolchain { languageVersion.set(JavaLanguageVersion.of(8)) } +} diff --git a/inngest-core/src/main/kotlin/com/inngest/Comm.kt b/inngest-core/src/main/kotlin/com/inngest/Comm.kt index bfa3d17f..e230e599 100644 --- a/inngest-core/src/main/kotlin/com/inngest/Comm.kt +++ b/inngest-core/src/main/kotlin/com/inngest/Comm.kt @@ -2,10 +2,11 @@ package com.inngest import com.beust.klaxon.Json import com.beust.klaxon.Klaxon -import java.net.URI -import java.net.http.HttpClient -import java.net.http.HttpRequest -import java.net.http.HttpResponse +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import java.io.IOException data class ExecutionRequestPayload( val ctx: ExecutionContext, @@ -125,17 +126,30 @@ class CommHandler(val functions: HashMap) { v = "0.0.1", functions = getFunctionConfigs(), ) - val requestBody = Klaxon().toJsonString(requestPayload) + val jsonRequestBody = Klaxon().toJsonString(requestPayload) + + val client = OkHttpClient() + + val jsonMediaType = "application/json".toMediaType() + val requestBody = jsonRequestBody.toRequestBody(jsonMediaType) - val client = HttpClient.newBuilder().build() // TODO - Add headers? val request = - HttpRequest.newBuilder() - .uri(URI.create(registrationUrl)) - .POST(HttpRequest.BodyPublishers.ofString(requestBody)) + Request.Builder() + .url(registrationUrl).addHeader("Content-Type", "application/json") + .post(requestBody) .build() - val response = client.send(request, HttpResponse.BodyHandlers.ofString()) - // TODO - Decode response and relay any message + + client.newCall(request).execute().use { response -> + if (!response.isSuccessful) throw IOException("Unexpected code $response") + + // TODO - Decode response and relay any message +// for ((name, value) in response.headers) { +// println("$name: $value") +// } +// +// println(response.body!!.string()) + } // TODO - Add headers to output val body: Map = mapOf() diff --git a/inngest-spring-boot-demo/build.gradle.kts b/inngest-spring-boot-demo/build.gradle.kts new file mode 100644 index 00000000..95827dfb --- /dev/null +++ b/inngest-spring-boot-demo/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + java + id("org.springframework.boot") version "2.7.18" + id("io.spring.dependency-management") version "1.1.4" +} + +group = "com.inngest" +version = "0.0.1" + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 +} + +repositories { + mavenCentral() +} + +dependencies { + implementation(project(":inngest-core")) + + implementation("org.springframework.boot:spring-boot-starter-web") + testImplementation("org.springframework.boot:spring-boot-starter-test") +} + +tasks.withType { + useJUnitPlatform() +} + diff --git a/inngest-spring-boot-demo/src/main/java/com/inngest/springbootdemo/InngestController.java b/inngest-spring-boot-demo/src/main/java/com/inngest/springbootdemo/InngestController.java new file mode 100644 index 00000000..bb4ea2e7 --- /dev/null +++ b/inngest-spring-boot-demo/src/main/java/com/inngest/springbootdemo/InngestController.java @@ -0,0 +1,49 @@ +package com.inngest.springbootdemo; + +import com.inngest.CommResponse; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping(value = "/api", produces = MediaType.APPLICATION_JSON_VALUE) +public class InngestController { + + private static final HttpHeaders commonHeaders = new HttpHeaders(); + + static { + String inngestSdk = "inngest-kt:v0.0.1"; + commonHeaders.add("x-inngest-sdk", inngestSdk); + } + + @GetMapping("/inngest") + public ResponseEntity index() { + String response = InngestSingleton.getInstance().introspect(); + + return ResponseEntity.ok().headers(commonHeaders).body(response); + } + + @PutMapping("/inngest") + public ResponseEntity put() { + String response = InngestSingleton.getInstance().register(); + return ResponseEntity.ok().headers(commonHeaders).body(response); + } + + @PostMapping(value = "/inngest", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity handleRequest( + @RequestParam(name = "fnId") String functionId, + @RequestBody String body + ) { + try { + CommResponse response = InngestSingleton.getInstance().callFunction(functionId, body); + + return ResponseEntity.status(response.getStatusCode().getCode()).headers(commonHeaders) + .body(response.getBody()); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(e.toString()); + } + } +} diff --git a/inngest-spring-boot-demo/src/main/java/com/inngest/springbootdemo/InngestSingleton.java b/inngest-spring-boot-demo/src/main/java/com/inngest/springbootdemo/InngestSingleton.java new file mode 100644 index 00000000..ed7856f1 --- /dev/null +++ b/inngest-spring-boot-demo/src/main/java/com/inngest/springbootdemo/InngestSingleton.java @@ -0,0 +1,73 @@ +package com.inngest.springbootdemo; + +import com.inngest.*; +import kotlin.jvm.functions.Function2; + +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 { + private static CommHandler instance; + + public static synchronized CommHandler getInstance() { + if (instance == null) { + FunctionTrigger fnTrigger = new FunctionTrigger("user-signup", null, null); + FunctionTrigger[] triggers = {fnTrigger}; + FunctionOptions fnConfig = new FunctionOptions("fn-id-slug", "My function!", triggers); + + Function2 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); + +// 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); +// }); + +// 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; +// }); + + step.sleep("wait-one-sec", Duration.ofSeconds(2)); + +// step.run("last-step", () -> (res != null ? res.getSum() : 0) * add); + + return null; + }; + InngestFunction function = new InngestFunction(fnConfig, handler); + + HashMap functions = new HashMap<>(); + functions.put("fn-id-slug", function); + + instance = new CommHandler(functions); + } + return instance; + } +} diff --git a/inngest-spring-boot-demo/src/main/java/com/inngest/springbootdemo/SpringBootDemoApplication.java b/inngest-spring-boot-demo/src/main/java/com/inngest/springbootdemo/SpringBootDemoApplication.java new file mode 100644 index 00000000..f7ae8790 --- /dev/null +++ b/inngest-spring-boot-demo/src/main/java/com/inngest/springbootdemo/SpringBootDemoApplication.java @@ -0,0 +1,12 @@ +package com.inngest.springbootdemo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootDemoApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootDemoApplication.class, args); + } +} diff --git a/inngest-spring-boot-demo/src/main/resources/application.yml b/inngest-spring-boot-demo/src/main/resources/application.yml new file mode 100644 index 00000000..a1f56d6e --- /dev/null +++ b/inngest-spring-boot-demo/src/main/resources/application.yml @@ -0,0 +1,4 @@ +server: + port: 8080 # We probably want to change this to 8081 to avoid port conflicts + # but I kept it for now because of the hardcoded URL that are sent + # to the inngest dev-server. diff --git a/inngest-spring-boot-demo/src/test/java/com/inngest/springbootdemo/InngestControllerTest.java b/inngest-spring-boot-demo/src/test/java/com/inngest/springbootdemo/InngestControllerTest.java new file mode 100644 index 00000000..a15316af --- /dev/null +++ b/inngest-spring-boot-demo/src/test/java/com/inngest/springbootdemo/InngestControllerTest.java @@ -0,0 +1,25 @@ +package com.inngest.springbootdemo; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@WebMvcTest(InngestController.class) +public class InngestControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Test + public void shouldReturnSyncPayload() throws Exception { + mockMvc.perform(get("/api/inngest")) + .andExpect(status().isOk()) + .andExpect(content().contentType("application/json")) + .andExpect(jsonPath("$.appName").value("my-app")) + .andExpect(jsonPath("$.sdk").value("kotlin")); + } +} diff --git a/inngest-spring-boot-demo/src/test/java/com/inngest/springbootdemo/SpringBootDemoApplicationTests.java b/inngest-spring-boot-demo/src/test/java/com/inngest/springbootdemo/SpringBootDemoApplicationTests.java new file mode 100644 index 00000000..c01ed3ed --- /dev/null +++ b/inngest-spring-boot-demo/src/test/java/com/inngest/springbootdemo/SpringBootDemoApplicationTests.java @@ -0,0 +1,13 @@ +package com.inngest.springbootdemo; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class SpringBootDemoApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 769b571f..ffb1c5c7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,4 +12,4 @@ plugins { rootProject.name = "inngest-sdk" -include("inngest-core", "inngest-test-server") +include("inngest-core", "inngest-test-server", "inngest-spring-boot-demo")