Skip to content

Commit ddc3308

Browse files
committed
Add Spring Boot starter for Arcade SDK
Adds a `ArcadeClient` bean if properties are available
1 parent eb7c3bb commit ddc3308

6 files changed

Lines changed: 136 additions & 2 deletions

File tree

arcade-java-example/build.gradle.kts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,15 @@ repositories {
99

1010
dependencies {
1111
implementation(project(":arcade-java"))
12+
13+
// Only needed for SpringBootExample
14+
implementation(project(":arcade-spring-boot-starter"))
15+
implementation("org.springframework.boot:spring-boot-starter:3.5.10")
1216
}
1317

1418
tasks.withType<JavaCompile>().configureEach {
1519
// Allow using more modern APIs, like `List.of` and `Map.of`, in examples.
16-
options.release.set(9)
20+
options.release.set(17)
1721
}
1822

1923
application {
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package dev.arcade.example;
2+
3+
import dev.arcade.client.ArcadeClient;
4+
import dev.arcade.models.tools.ExecuteToolRequest;
5+
import dev.arcade.models.tools.ExecuteToolResponse;
6+
import dev.arcade.models.tools.ToolExecuteParams;
7+
import org.springframework.boot.ApplicationRunner;
8+
import org.springframework.boot.SpringApplication;
9+
import org.springframework.boot.autoconfigure.SpringBootApplication;
10+
import org.springframework.context.annotation.Bean;
11+
12+
/**
13+
* Example of calling a tool using the Arcade Java SDK.
14+
*/
15+
@SpringBootApplication
16+
public class SpringBootExample {
17+
18+
/**
19+
* Starts the Spring Boot application.
20+
* @param args All args are passed into the SpringApplication
21+
*/
22+
public static void main(String[] args) {
23+
SpringApplication.run(SpringBootExample.class, args);
24+
}
25+
26+
/**
27+
* Injects an <code>ArcadeClient</code>, and returns an ApplicationRunner that makes a tool call.
28+
* @param client Arcade Client is autoinjected if ARCADE_API_KEY, or equivalent application.properties var is set.
29+
* @return Runs code on application start.
30+
*/
31+
@Bean
32+
ApplicationRunner appRunner(ArcadeClient client) {
33+
return args -> {
34+
String userId = System.getenv("ARCADE_USER_ID"); // the Spotify tool require a userId
35+
if (userId == null) {
36+
throw new IllegalArgumentException("Missing ARCADE_USER_ID environment variable");
37+
}
38+
39+
ToolExecuteParams params = ToolExecuteParams.builder()
40+
.executeToolRequest(ExecuteToolRequest.builder()
41+
.toolName("Spotify.ResumePlayback@1.0.2")
42+
.userId(userId)
43+
.build())
44+
.build();
45+
ExecuteToolResponse executeToolResponse = client.tools().execute(params);
46+
executeToolResponse
47+
.output()
48+
.ifPresentOrElse(
49+
output -> System.out.println("Tool output: " + output._value()),
50+
() -> System.out.println("No output for this tool"));
51+
};
52+
}
53+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
2+
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
3+
4+
plugins {
5+
id("arcade.kotlin")
6+
id("java")
7+
id("io.spring.dependency-management") version "1.1.7"
8+
application
9+
}
10+
11+
dependencies {
12+
api(project(":arcade-java"))
13+
implementation("org.springframework.boot:spring-boot-starter:3.5.10")
14+
}
15+
16+
tasks.withType<JavaCompile>().configureEach {
17+
// Allow using more modern APIs, like `List.of` and `Map.of`, in examples.
18+
options.release.set(17)
19+
}
20+
21+
tasks.withType<KotlinCompile> {
22+
compilerOptions {
23+
freeCompilerArgs = listOf(
24+
"-Xjvm-default=all",
25+
"-Xjdk-release=17",
26+
// Suppress deprecation warnings because we may still reference and test deprecated members.
27+
// TODO: Replace with `-Xsuppress-warning=DEPRECATION` once we use Kotlin compiler 2.1.0+.
28+
"-nowarn",
29+
)
30+
jvmTarget.set(JvmTarget.JVM_17)
31+
}
32+
}
33+
34+
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package dev.arcade.springboot
2+
3+
import dev.arcade.client.ArcadeClient
4+
import dev.arcade.client.okhttp.ArcadeOkHttpClient
5+
import dev.arcade.core.ClientOptions
6+
import org.springframework.boot.autoconfigure.AutoConfiguration
7+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
8+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
9+
import org.springframework.boot.context.properties.ConfigurationProperties
10+
import org.springframework.boot.context.properties.EnableConfigurationProperties
11+
import org.springframework.context.annotation.Bean
12+
import org.springframework.util.StringUtils
13+
14+
@AutoConfiguration
15+
@EnableConfigurationProperties(ArcadeAutoConfiguration.Config::class)
16+
open class ArcadeAutoConfiguration {
17+
18+
@Bean
19+
@ConditionalOnMissingBean
20+
@ConditionalOnProperty(prefix = "arcade", name = ["api-key"])
21+
open fun arcadeClient(config: Config): ArcadeClient {
22+
val clientBuilder = ArcadeOkHttpClient.builder().fromEnv()
23+
24+
if (config.apiKey != null && StringUtils.hasText(config.apiKey)) {
25+
clientBuilder.apiKey(config.apiKey)
26+
}
27+
28+
if (config.baseUrl != null && StringUtils.hasText(config.baseUrl)) {
29+
clientBuilder.baseUrl(config.baseUrl)
30+
}
31+
32+
return clientBuilder.build()
33+
}
34+
35+
@ConfigurationProperties(prefix = "arcade")
36+
@JvmRecord
37+
data class Config(
38+
val apiKey: String? = System.getenv("ARCADE_API_KEY"),
39+
val baseUrl: String? = ClientOptions.PRODUCTION_URL,
40+
) {}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dev.arcade.springboot.ArcadeAutoConfiguration

settings.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ val projectNames = rootDir.listFiles()
99
file.listFiles()?.asSequence().orEmpty().any { it.name == "build.gradle.kts" }
1010
}
1111
.map { it.name }
12-
.toList()
12+
.toList() +
13+
listOf("arcade-spring-boot-starter")
1314
println("projects: $projectNames")
1415
projectNames.forEach { include(it) }

0 commit comments

Comments
 (0)