Skip to content
Draft
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
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v20.17.0
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[![Maven Central](https://img.shields.io/maven-central/v/community.flock.wirespec.compiler/lib)](https://mvnrepository.com/artifact/community.flock.wirespec.compiler/core-jvm)
[![Apache 2.0 License](https://img.shields.io/badge/License-Apache_2.0-blue)](LICENSE)
[![Maven Central](https://img.shields.io/maven-central/v/community.flock.wirespec.compiler/core-jvm)](https://mvnrepository.com/artifact/community.flock.wirespec.compiler/core-jvm)
![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/flock-community/wirespec/build.yml)
[![Apache 2.0 License](https://img.shields.io/badge/License-Apache_2.0-blue)](LICENSE)
[![Documentation](https://img.shields.io/badge/docs-wirespec.io-a97bff.svg?logo=kotlin)](https://wirespec.io)

<div align="center">
<h1>
Expand All @@ -20,7 +21,7 @@ Simplify your API development workflows, accelerate implementation, and guarante


Wirespec is a modern tool that enhances software development by streamlining the process of designing, documenting, and implementing APIs.
While the software industry offers numerous solutions for designing contracts between services, Wirespec distinguishes itself by using a simple language model and multi-language compatibility.
While the software industry offers many solutions for designing contracts between services, Wirespec distinguishes itself by using a simple language model and multi-language compatibility.

Here are some key reasons why you might want to use Wirespec:

Expand Down Expand Up @@ -57,16 +58,18 @@ wirespec compile ./todo.ws -o ./tmp -l Kotlin
* Maven
* Gradle

## Extentions
## Extensions

* IntelliJ IDEA
* Visual Studio Code

## Integration

Wirespec offers integration libraries with differ libraries.
Wirespec offers integration libraries like:

* [Avro](src/integration/avro)
* [Jackson](src/integration/jackson)
* [Spring](src/integration/spring)

# CLI

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
package community.flock.wirespec.example.maven.custom.app

import community.flock.wirespec.example.maven.custom.app.common.Serialization
import community.flock.wirespec.example.maven.custom.app.common.WirespecClient
import community.flock.wirespec.example.maven.custom.app.todo.LiveTodoRepository
import community.flock.wirespec.example.maven.custom.app.todo.TodoHandler
import community.flock.wirespec.example.maven.custom.app.todo.TodoService
import community.flock.wirespec.example.maven.custom.app.todo.todoModule
import community.flock.wirespec.example.maven.custom.app.user.LiveUserAdapter
import community.flock.wirespec.example.maven.custom.app.user.UserHandler
import community.flock.wirespec.example.maven.custom.app.user.UserService
import community.flock.wirespec.example.maven.custom.app.user.userModule
import community.flock.wirespec.generated.kotlin.Client
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.Application
import io.ktor.server.application.install
Expand All @@ -23,5 +32,20 @@ fun Application.config() {
}

fun Application.app() {
todoModule(LiveTodoRepository())
object : TodoService {
override val todoRepository = LiveTodoRepository()
}
.let(::TodoHandler)
.let(::todoModule)

object : UserService {
override val userAdapter = LiveUserAdapter(
client = Client(
serialization = Serialization,
handler = WirespecClient::handle,
),
)
}
.let(::UserHandler)
.let(::userModule)
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ import kotlinx.serialization.serializer
import kotlin.reflect.KType
import kotlin.reflect.full.createType

class WirespecClient(
private val httpClient: HttpClient = HttpClient(),
) {
object WirespecClient {

private val httpClient: HttpClient = HttpClient()

fun handle(request: Wirespec.RawRequest): Wirespec.RawResponse = runBlocking {
val response =
httpClient.request("http://localhost:8080/") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,8 @@ private interface TodoApi :
DeleteTodoById.Handler

class TodoHandler(
liveTodoRepository: TodoRepository,
private val service: TodoService,
) : TodoApi {
private val service =
object : TodoService {
override val todoRepository = liveTodoRepository
}

override suspend fun getTodos(request: GetTodos.Request): GetTodos.Response200 = service
.getAllTodos()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import community.flock.wirespec.generated.kotlin.endpoint.PostTodo
import community.flock.wirespec.kotlin.Wirespec
import io.ktor.http.HttpMethod
import io.ktor.server.application.Application
import io.ktor.server.application.ApplicationCall
import io.ktor.server.application.call
import io.ktor.server.request.ApplicationRequest
import io.ktor.server.request.httpMethod
import io.ktor.server.request.path
import io.ktor.server.request.receive
Expand All @@ -18,15 +18,12 @@ import io.ktor.server.routing.route
import io.ktor.server.routing.routing
import io.ktor.util.toMap

fun Application.todoModule(todoRepository: TodoRepository) {
val handler = TodoHandler(todoRepository)

fun Application.todoModule(handler: TodoHandler) {
routing {
with(GetTodos.Handler) {
route(pathTemplate, method.let(HttpMethod::parse)) {
handle {
call
.toRawRequest()
call.request.toRawRequest()
.let(server(Serialization)::from)
.let { handler.getTodos(it) }
.run { call.respond(body) }
Expand All @@ -37,8 +34,7 @@ fun Application.todoModule(todoRepository: TodoRepository) {
with(GetTodoById.Handler) {
route(pathTemplate, method.let(HttpMethod::parse)) {
handle {
call
.toRawRequest()
call.request.toRawRequest()
.let(server(Serialization)::from)
.let { handler.getTodoById(it) }
.run { call.respond(body) }
Expand All @@ -49,8 +45,7 @@ fun Application.todoModule(todoRepository: TodoRepository) {
with(PostTodo.Handler) {
route(pathTemplate, method.let(HttpMethod::parse)) {
handle {
call
.toRawRequest()
call.request.toRawRequest()
.let(server(Serialization)::from)
.let { handler.postTodo(it) }
.run { call.respond(body) }
Expand All @@ -61,8 +56,7 @@ fun Application.todoModule(todoRepository: TodoRepository) {
with(DeleteTodoById.Handler) {
route(pathTemplate, method.let(HttpMethod::parse)) {
handle {
call
.toRawRequest()
call.request.toRawRequest()
.let(server(Serialization)::from)
.let { handler.deleteTodoById(it) }
.run { call.respond(body) }
Expand All @@ -72,10 +66,10 @@ fun Application.todoModule(todoRepository: TodoRepository) {
}
}

suspend fun ApplicationCall.toRawRequest() = Wirespec.RawRequest(
method = request.httpMethod.value,
path = request.path().split("/").filter { it.isNotBlank() },
queries = request.queryParameters.toMap(),
headers = request.headers.toMap(),
body = receive<String>(),
suspend fun ApplicationRequest.toRawRequest() = Wirespec.RawRequest(
method = httpMethod.value,
path = path().split("/").filter { it.isNotBlank() },
queries = queryParameters.toMap(),
headers = headers.toMap(),
body = call.receive<String>(),
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ fun TodoService.getTodoById(id: Todo.Id) = todoRepository.getTodoById(id)
fun TodoService.saveTodo(todo: Todo) = todoRepository.saveTodo(todo)

fun TodoService.deleteTodoById(id: Todo.Id) = todoRepository.deleteTodoById(id)

interface HasTodoService {
val todoService: TodoService
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package community.flock.wirespec.example.maven.custom.app.user

import community.flock.wirespec.example.maven.custom.app.user.UserConverter.internalize
import community.flock.wirespec.generated.kotlin.UsersModule
import community.flock.wirespec.generated.kotlin.endpoint.DeleteUserByName
import community.flock.wirespec.generated.kotlin.endpoint.GetUserByName
import community.flock.wirespec.generated.kotlin.endpoint.GetUsers
Expand All @@ -9,30 +10,30 @@ import community.flock.wirespec.generated.kotlin.model.UserDto
import kotlinx.coroutines.runBlocking

class LiveUserAdapter(
private val client: UserClient,
private val client: UsersModule,
) : UserAdapter {
override fun getAllUsers(): List<User> = runBlocking {
when (val res = client.getUsers(GetUsers.Request)) {
when (val res = client.getUsers()) {
is GetUsers.Response200 -> res.body.map { it.internalize() }
}
}

override fun getUserByName(name: String): User = runBlocking {
when (val res = client.getUserByName(GetUserByName.Request(name))) {
when (val res = client.getUserByName(name)) {
is GetUserByName.Response200 -> res.body.internalize()
is GetUserByName.Response404 -> TODO()
}
}

override fun saveUser(user: User): User = runBlocking {
when (val res = client.postUser(PostUser.Request(UserDto(name = user.name)))) {
when (val res = client.postUser(UserDto(name = user.name))) {
is PostUser.Response200 -> res.body.internalize()
is PostUser.Response409 -> TODO()
}
}

override fun deleteUserByName(name: String): User = runBlocking {
when (val res = client.deleteUserByName(DeleteUserByName.Request(name))) {
when (val res = client.deleteUserByName(name)) {
is DeleteUserByName.Response200 -> res.body.internalize()
is DeleteUserByName.Response404 -> TODO()
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package community.flock.wirespec.example.maven.custom.app.user

class UserHandler(private val service: UserService) {
fun getAllUsers() = service.getAllUsers()
fun getUserByName(name: String) = service.getUserByName(name)
fun putOneUser(user: User) = service.saveUser(user)
fun deleteUserByName(name: String) = service.deleteUserByName(name)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package community.flock.wirespec.example.maven.custom.app.user

import io.ktor.http.HttpMethod.Companion.Delete
import io.ktor.http.HttpMethod.Companion.Get
import io.ktor.http.HttpMethod.Companion.Post
import io.ktor.server.application.Application
import io.ktor.server.application.call
import io.ktor.server.response.respond
import io.ktor.server.routing.route
import io.ktor.server.routing.routing

fun Application.userModule(handler: UserHandler) {
routing {
val basePath = "/api/users"
val namePathParam = "name"
route(basePath, Get) {
handle {
call.respond(handler.getAllUsers())
}
}
route("$basePath/{$namePathParam}", Get) {
handle {
val name = call.parameters["name"] ?: error("Invalid request")
call.respond(handler.getUserByName(name))
}
}
route(basePath, Post) {
handle {
call.respond(handler.getAllUsers())
}
}
route("$basePath/{$namePathParam}", Delete) {
handle {
val name = call.parameters["name"] ?: error("Invalid request")
call.respond(handler.deleteUserByName(name))
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package community.flock.wirespec.example.maven.custom.app

import community.flock.wirespec.example.maven.custom.app.todo.LiveTodoRepository
import community.flock.wirespec.example.maven.custom.app.todo.TodoHandler
import community.flock.wirespec.example.maven.custom.app.todo.TodoService
import community.flock.wirespec.example.maven.custom.app.todo.todoModule
import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText
Expand All @@ -14,7 +16,11 @@ class ApplicationTest {
fun testRoot() = testApplication {
application {
config()
todoModule(LiveTodoRepository())

object : TodoService {
override val todoRepository = LiveTodoRepository()
}.let(::TodoHandler)
.let(::todoModule)
}

client.get("/api/todos").apply {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
package community.flock.wirespec.example.maven.custom.app.user

import community.flock.wirespec.generated.kotlin.UsersModule
import community.flock.wirespec.generated.kotlin.endpoint.DeleteUserByName
import community.flock.wirespec.generated.kotlin.endpoint.GetUserByName
import community.flock.wirespec.generated.kotlin.endpoint.GetUsers
import community.flock.wirespec.generated.kotlin.endpoint.PostUser
import community.flock.wirespec.generated.kotlin.model.UserDto

class TestUserClient : UserClient {
class TestUserClient : UsersModule {
private val users =
mutableSetOf(
UserDto("name"),
)

override suspend fun getUsers(request: GetUsers.Request) = users
override suspend fun getUsers() = users
.toList()
.let(GetUsers::Response200)

override suspend fun getUserByName(request: GetUserByName.Request) = users
.find { it.name == request.path.name }
override suspend fun getUserByName(name: String) = users
.find { it.name == name }
?.let(GetUserByName::Response200)
?: GetUserByName.Response404(Unit)

override suspend fun postUser(request: PostUser.Request) = request.body
override suspend fun postUser(body: UserDto) = body
.takeIf(users::add)
?.let(PostUser::Response200)
?: PostUser.Response409(Unit)

override suspend fun deleteUserByName(request: DeleteUserByName.Request) = users
.find { it.name == request.path.name }
override suspend fun deleteUserByName(name: String) = users
.find { it.name == name }
?.also(users::remove)
?.let(DeleteUserByName::Response200)
?: DeleteUserByName.Response404(Unit)
Expand Down
2 changes: 1 addition & 1 deletion examples/npm-typescript/src/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const mockHandler = vi.fn<HandleFetch>(() => Promise.resolve({

test('testGetTodoById', async () => {
const client = Client(wirespecSerialization, (req) => wirespecFetch(req, mockHandler))
const res = await client.GetTodoById({id: "123"})
const res = await client.getTodoById({id: "123"})
expect(res.status).toEqual(200)
expect(res.headers).toEqual({})
expect(res.body).toEqual({
Expand Down
Loading