Skip to content
Open
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
4 changes: 2 additions & 2 deletions examples/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<version>3.5.10</version>
</parent>

<groupId>community.flock.wirespec.example.maven</groupId>
Expand All @@ -14,7 +14,7 @@

<modules>
<module>maven-preprocessor</module>
<module>maven-spring-avro</module>
<!-- <module>maven-spring-avro</module>-->
<module>maven-spring-compile</module>
<module>maven-spring-convert</module>
<module>maven-spring-custom</module>
Expand Down
6 changes: 6 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ maven_plugin = "0.4.3"
maven_plugin_api = "3.9.8"
maven_plugin_annotations = "3.13.1"
maven_project = "2.2.1"
mockk = "1.13.13"
mockito = "5.11.0"
nexus-publish = "2.0.0"
spotless = "7.0.4"
spring_boot = "3.3.4"
Expand Down Expand Up @@ -51,11 +53,15 @@ kotlin_test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotl
kotlin_test_common = { module = "org.jetbrains.kotlin:kotlin-test-common", version.ref = "kotlin" }
kotlin_test_annotations_common = { module = "org.jetbrains.kotlin:kotlin-test-annotations-common", version.ref = "kotlin" }
kotlin_test_junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
kotlinx_coroutines_jdk8 = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8", version.ref = "kotlinx_coroutines" }
kotlinx_coroutines_test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx_coroutines" }
kotlinx_coroutines_reactor = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-reactor", version.ref = "kotlinx_coroutines" }
kotlinx_io_core = { module = "org.jetbrains.kotlinx:kotlinx-io-core", version.ref = "kotlinx_io" }
kotlinx_openapi_bindings = { module = "community.flock.kotlinx.openapi.bindings:kotlin-openapi-bindings", version.ref = "kotlinx_openapi_bindings" }
kotlinx_rgxgen = { module = "community.flock.kotlinx.rgxgen:kotlin-rgxgen", version.ref = "kotlinx_rgxgen" }
kotlinx_serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx_serialization" }
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
mockito = { module = "org.mockito:mockito-core", version.ref = "mockito" }
maven_plugin_api = { module = "org.apache.maven:maven-plugin-api", version.ref = "maven_plugin_api" }
maven_plugin_annotations = { module = "org.apache.maven.plugin-tools:maven-plugin-annotations", version.ref = "maven_plugin_annotations" }
maven_project = { module = "org.apache.maven:maven-project", version.ref = "maven_project" }
Expand Down
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ include(
"src:converter:openapi",
"src:integration:avro",
"src:integration:jackson",
"src:integration:jvm",
"src:integration:wirespec",
"src:integration:spring",
"src:tools:generator",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ data object JavaShared : Shared {
|import java.lang.reflect.ParameterizedType;
|import java.util.List;
|import java.util.Map;
|import java.util.concurrent.CompletableFuture;
|
|public interface Wirespec {
|${Spacer}interface Enum { String getLabel(); }
Expand Down Expand Up @@ -59,6 +60,7 @@ data object JavaShared : Shared {
|${Spacer}interface ParamDeserializer { <T> T deserializeParam(List<String> values, Type type); }
|${Spacer}record RawRequest(String method, List<String> path, Map<String, List<String>> queries, Map<String, List<String>> headers, byte[] body) {}
|${Spacer}record RawResponse(int statusCode, Map<String, List<String>> headers, byte[] body) {}
|${Spacer}interface Transportation { CompletableFuture<RawResponse> transport(RawRequest request); }
|${Spacer}static Type getType(final Class<?> actualTypeArguments, final Class<?> rawType) {
|${Spacer(2)}if(rawType != null) {
|${Spacer(3)}return new ParameterizedType() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ data object KotlinShared : Shared {
|${Spacer}interface ParamDeserializer { fun <T> deserializeParam(values: List<String>, kType: KType): T }
|${Spacer}data class RawRequest(val method: String, val path: List<String>, val queries: Map<String, List<String>>, val headers: Map<String, List<String>>, val body: ByteArray?)
|${Spacer}data class RawResponse(val statusCode: Int, val headers: Map<String, List<String>>, val body: ByteArray?)
|${Spacer}interface Transportation { suspend fun transport(request: RawResponse): RawRequest }
|}
""".trimMargin()
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,5 +135,9 @@ data object PythonShared : Shared {
| headers: Dict[str, List[str]]
| body: Optional[str]
|
| class Transportation(ABC):
| @abstractmethod
| def transport(self, request: RawRequest) -> RawResponse: pass
|
""".trimMargin()
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ data object TypeScriptShared : Shared {
|${Spacer}export type Method = "GET" | "PUT" | "POST" | "DELETE" | "OPTIONS" | "HEAD" | "PATCH" | "TRACE"
|${Spacer}export type RawRequest = { method: Method, path: string[], queries: Record<string, string>, headers: Record<string, string>, body?: string }
|${Spacer}export type RawResponse = { status: number, headers: Record<string, string>, body?: string }
|${Spacer}export type Transportation = (request: RawRequest) => Promise<RawResponse>
|${Spacer}export type Request<T> = { path: Record<string, unknown>, method: Method, queries?: Record<string, unknown>, headers?: Record<string, unknown>, body?:T }
|${Spacer}export type Response<T> = { status:number, headers?: Record<string, unknown>, body?:T }
|${Spacer}export type Serialization = { serialize: <T>(type: T) => string; deserialize: <T>(raw: string | undefined) => T }
Expand Down
49 changes: 49 additions & 0 deletions src/integration/jvm/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
plugins {
id("module.publication")
id("module.spotless")
alias(libs.plugins.kotlin.multiplatform)
}

group = "${libs.versions.group.id.get()}.integration"
version = System.getenv(libs.versions.from.env.get()) ?: libs.versions.default.get()

repositories {
mavenCentral()
mavenLocal()
}

kotlin {
jvm {
withJava()
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(libs.versions.java.get()))
}
}
}
sourceSets {
jvmMain {
dependencies {
implementation(libs.kotlinx.coroutines.jdk8)
}
}
commonMain {
dependencies {
compileOnly(project(":src:integration:wirespec"))
}
}
jvmTest {
dependencies {
implementation(libs.kotlinx.coroutines.test)
}
}
commonTest {
dependencies {
implementation(project(":src:integration:wirespec"))
implementation(libs.kotlin.junit)
implementation(libs.mockk)
implementation(libs.mockito)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package community.flock.wirespec.integration.jvm.java;

import community.flock.wirespec.java.Wirespec;

import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

public class HttpClientTransportation implements Wirespec.Transportation {

private final String baseUrl;

private final HttpClient client;

public HttpClientTransportation(String baseUrl) {
this(baseUrl, HttpClient.newBuilder().build());
}

public HttpClientTransportation(String baseUrl, HttpClient client) {
this.baseUrl = baseUrl;
this.client = client;
}

@Override
public CompletableFuture<Wirespec.RawResponse> transport(Wirespec.RawRequest request) {

String pathString = request.path().stream()
.filter(it -> !it.isBlank())
.collect(Collectors.joining("/"));

String base = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl;
String uriString = base + "/" + pathString;

if (!request.queries().isEmpty()) {
String queryString = request.queries().entrySet().stream()
.flatMap(entry -> entry.getValue().stream()
.map(v -> entry.getKey() + "=" + URLEncoder.encode(v, StandardCharsets.UTF_8)))
.collect(Collectors.joining("&"));
uriString += "?" + queryString;
}
URI uri = URI.create(uriString);

String[] headers = request.headers().entrySet().stream()
.flatMap(entry -> entry.getValue().stream()
.filter(value -> !value.isBlank())
.flatMap(value -> List.of(entry.getKey(), value).stream()))
.toArray(String[]::new);

HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
.uri(uri)
.method(
request.method().toUpperCase(),
request.body() != null
? HttpRequest.BodyPublishers.ofByteArray(request.body())
: HttpRequest.BodyPublishers.noBody()
);

if (headers.length > 0) {
requestBuilder.headers(headers);
}

return client.sendAsync(requestBuilder.build(), HttpResponse.BodyHandlers.ofByteArray())
.thenApply(response -> new Wirespec.RawResponse(
response.statusCode(),
response.headers().map(),
response.body()
));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package community.flock.wirespec.integration.jvm.kotlin

import community.flock.wirespec.kotlin.Wirespec
import kotlinx.coroutines.future.await
import java.net.URI
import java.net.URLEncoder
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.nio.charset.StandardCharsets

class HttpClientTransportation(
private val baseUrl: String,
private val client: HttpClient = HttpClient.newBuilder().build(),
) : Wirespec.Transportation {

override suspend fun transport(request: Wirespec.RawRequest): Wirespec.RawResponse {
val pathString = request.path
.filter { it.isNotBlank() }
.joinToString("/")
val base = if (baseUrl.endsWith("/")) baseUrl.dropLast(1) else baseUrl
val uriString = if (pathString.isBlank()) base else "$base/$pathString"

val uri = if (request.queries.isNotEmpty()) {
val queryString = request.queries.entries.joinToString("&") { (key, values) ->
values.joinToString("&") { value ->
"$key=${URLEncoder.encode(value, StandardCharsets.UTF_8)}"
}
}
URI.create("$uriString?$queryString")
} else {
URI.create(uriString)
}

val headersList = request.headers.entries.flatMap { (key, values) ->
values.filter { it.isNotBlank() }.flatMap { value ->
listOf(key, value)
}
}

val requestBuilder = HttpRequest.newBuilder()
.uri(uri)
.method(
request.method.uppercase(),
request.body?.let { HttpRequest.BodyPublishers.ofByteArray(it) }
?: HttpRequest.BodyPublishers.noBody(),
)
.apply {
if (headersList.isNotEmpty()) {
headers(*headersList.toTypedArray())
}
}

return client.sendAsync(requestBuilder.build(), HttpResponse.BodyHandlers.ofByteArray())
.await()
.let { response ->
Wirespec.RawResponse(
statusCode = response.statusCode(),
headers = response.headers().map(),
body = response.body(),
)
}
}
}
Loading
Loading