Skip to content

Commit

Permalink
Jackson integration library (#163)
Browse files Browse the repository at this point in the history
Co-authored-by: Julius van Dis <[email protected]>
  • Loading branch information
wilmveel and ZzAve authored Jan 26, 2024
1 parent 1a84d6b commit e6e8f1c
Show file tree
Hide file tree
Showing 20 changed files with 654 additions and 7 deletions.
11 changes: 6 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,12 @@ jobs:
gpg-passphrase: GPG_PASSPHRASE
- name: Run
run: |
./gradlew :src:compiler:core:publish
./gradlew :src:compiler:lib:publish
./gradlew :src:converter:openapi:publish
./gradlew :src:plugin:gradle:publish
./gradlew :src:plugin:maven:publish
./gradlew :src:compiler:core:publish \
:src:compiler:lib:publish \
:src:converter:openapi:publish \
:src:plugin:gradle:publish \
:src:plugin:maven:publish \
:src:integration:jackson:publish
release-lib-npm:

Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,16 @@ wirespec compile ./todo.ws -o ./tmp -l Kotlin
* Maven
* Gradle

## Extentions

* IntelliJ IDEA
* Visual Studio Code

## Integration
Wirespec offers integration libraries with differ libraries.

* [Jackson](src/integration/jackson)

# CLI

## Install
Expand Down
2 changes: 2 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,6 @@ include(
"src:plugin:maven",
"src:plugin:gradle",
"src:converter:openapi",
"src:integration:jackson",
"src:integration:wirespec",
)
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class JavaEmitter(
|
|public interface Wirespec {
|${SPACER}interface Enum {};
|${SPACER}interface Refined { String value(); };
|${SPACER}interface Refined { String getValue(); };
|${SPACER}interface Endpoint {};
|${SPACER}enum Method { GET, PUT, POST, DELETE, OPTIONS, HEAD, PATCH, TRACE };
|${SPACER}record Content<T> (String type, T body) {};
Expand Down Expand Up @@ -159,6 +159,8 @@ class JavaEmitter(
|${SPACER}static boolean validate($name record) {
|${SPACER}${validator.emit()}
|${SPACER}}
|${SPACER}@Override
|${SPACER}public String getValue() { return value; }
|}
|""".trimMargin()
}
Expand Down Expand Up @@ -316,7 +318,7 @@ class JavaEmitter(
fun String.sanitizeSymbol() = replace(".", "").replace(" ", "_")

companion object {
private val reservedKeywords = listOf(
val reservedKeywords = listOf(
"abstract", "continue", "for", "new", "switch",
"assert", "default", "goto", "package", "synchronized",
"boolean", "do", "if", "private", "this",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ class CompileRefinedTest {
static boolean validate(TodoId record) {
return java.util.regex.Pattern.compile("^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}${'$'}").matcher(record.value).find();
}
@Override
public String getValue() { return value; }
}
""".trimIndent()
Expand Down
96 changes: 96 additions & 0 deletions src/integration/jackson/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Jackson integration lib

This library exposes a jackson module which adds specific serializers and deserializers to handel Wirespec refined and enum types. For more details about Jackson see: https://github.com/FasterXML/jackson

## Usage
```xml
<dependency>
<groupId>community.flock.wirespec.integration</groupId>
<artifactId>jackson</artifactId>
<version>{VERSION}</version>
</dependency>
```

Register the Wirespec module

```java
ObjectMapper objectMapper = new ObjectMapper()
.registerModules(new WirespecModule());
```

## Docs

### Refined
The wirespec Java and Kotlin emitter add an extra wrapper class for refined types. When objects are serialized wrapper class becomes visible in json representation.

```java

record TodoId(Sring value){}
record Todo(TodoId id, String name, boolean done){}
```
When serialized to json with the default object mapper this wil result in the following output

```json
{
"id": {
"value": "123"
},
"name": "My todo",
"done": true
}
```
The Jackson module corrects this and flattens the output of the refined types

```json
{
"id": "123",
"name": "My todo",
"done": true
}
```

### Enum

For Java and Kotlin some values are sanitized because the compiler does not except certain keywords. Wirespec emits an extra label with the original value for every enum. The toString method is overwritten and returns the orignal value. This module uses the toString method to serialize and deserialize enum values

```wirespec
enum MyEnum {
true, false
}
```

The java emitter will generate the following enum class. The value true and false will be escaped because these are reserved keywords.

```java
public enum MyEnum implements Wirespec.Enum {
_true("true"),
_false("false");

public final String label;

MyEnum(String label) {
this.label = label;
}

@Override
public String toString() {
return this.label;
}
}
```

### Reserved keywords
In java reserved keywords cannot be used as field name. The Wirespec [JavaEmitter](https://github.com/flock-community/wirespec/blob/master/src/compiler/core/src/commonMain/kotlin/community/flock/wirespec/compiler/core/emit/JavaEmitter.kt#L314) prefixes the fields with a `_`. The Jackson Module corrects this with a NamingStrategy that removes the `_` only for java record types

```wirespec
type MyType {
final: Boolean
}
```

```java
public record MyType ( String _final){}
```

## Generate test classes
To test this module test classes are generated from a Wirespec specification. To regenerate the test classes run the following test [GenerateTestClasses.kt](src%2FjvmTest%2Fkotlin%2Fcommunity%2Fflock%2Fwirespec%2Fintegration%2Fjackson%2Fkotlin%2FGenerateTestClasses.kt)
52 changes: 52 additions & 0 deletions src/integration/jackson/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
plugins {
kotlin("multiplatform")
kotlin("jvm") apply false
id("com.github.johnrengelman.shadow") apply false
id("com.goncalossilva.resources") version "0.4.0"
}

group = "${Settings.GROUP_ID}.integration"
version = Settings.version

repositories {
mavenCentral()
maven(uri("https://s01.oss.sonatype.org/service/local/repo_groups/public/content"))
}

kotlin {
jvm {
withJava()
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
}
sourceSets {
commonMain {
dependencies {
compileOnly(project(":src:integration:wirespec"))
}
}
commonTest {
dependencies {
implementation(project(":src:integration:wirespec"))
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
implementation(kotlin("test-junit"))
}
}
val jvmMain by getting {
dependencies {
implementation(project(":src:compiler:core"))
compileOnly("com.fasterxml.jackson.core:jackson-databind:2.16.1")
}
}
val jvmTest by getting {
dependencies {
implementation("com.fasterxml.jackson.core:jackson-databind:2.16.1")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.2")
}
}
}
}
23 changes: 23 additions & 0 deletions src/integration/jackson/src/commonTest/resources/wirespec/todos.ws
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
refined TodoId /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/g

type Todo {
id: TodoId,
name: String,
final: Boolean,
category: TodoCategory
}

type TodoInput {
name: String,
done: Boolean
}

type Error {
code: String,
description: String
}

enum TodoCategory {
WORK,
LIFE
}
Loading

0 comments on commit e6e8f1c

Please sign in to comment.