Skip to content

Commit

Permalink
Add documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
guillermocalvo committed May 30, 2024
1 parent 6221459 commit cc4453e
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 6 deletions.
11 changes: 11 additions & 0 deletions badge.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
---
{
"schemaVersion": 1,
"style": "flat",
"labelColor": "fff",
"color": "light-green",
"label": "Result",
"message": "{{ site.current_version }}",
"logoSvg": "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256' role='img'><path fill='#000' d='m128 0c-70.692496 0-128 57.307556-128 128 0 70.69245 57.307504 128 128 128 70.6925 0 128-57.30755 128-128 0-70.692444-57.3075-128-128-128z'/><path fill='#fff' d='m136.35564 57.292727-40.67397-13.292725-23.68167 75.404758 10.5988 3.14252 10.03853-31.546757 25.76122 7.912145 3.29783 7.051312-8.19024 24.69695 10.51558 3.34618 9.44724-28.93646-3.77957-8.198802 7.87469-4.149675 6.69237-21.031168zm-7.41926 8.851265 3.54748 6.440357-3.80826 12.228035-6.84766 3.38107-26.04807-7.791706 7.15442-22.743615zm-17.85826-43.378365c38.23512 0 69.22657 30.999275 69.22657 69.234375 0 38.235098-30.99145 69.234378-69.22657 69.234378-38.23513 0-69.23437-30.99928-69.23437-69.234378 0-38.2351 30.99924-69.234375 69.23437-69.234375z'/></svg>"
}
166 changes: 166 additions & 0 deletions index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
---
title: Micronaut Serialization for Result
description: Result-Micronaut-Serde provides Micronaut serialization support for Result objects
image: https://dev.leakyabstractions.com/result/result-magic-ball.png
---

# Micronaut Serialization for Result

This library provides [Micronaut serialization support][MICRONAUT_SERDE] for [Results objects][RESULT].


## Introduction

When using [<tt>Result</tt> objects][RESULT_REPO] with [Micronaut][MICRONAUT], we might run into some problems.
[This library][RESULT_MICRONAUT_SERDE_REPO] solves them by making Micronaut treat results as *Serdeable* so that they
can be serialized and deserialized.

Let's start by creating a record `ApiOperation` containing one ordinary and one <tt>Result</tt> field:

```java
{% include_relative result-micronaut-serde/src/test/java/example/ApiOperation.java %}
```


## Problem Overview

We will take a look at what happens when we try to serialize and deserialize <tt>ApiOperation</tt> objects with
Micronaut.

First, let's make sure we're using the latest versions of both the [Micronaut Framework][MICRONAUT_LAUNCH] and the
[Result library][RESULT_LATEST].


### Serialization Problem

Now, let's create a Micronaut controller that returns an instance of `ApiOperation` containing a successful result:

```java
{% include_relative result-micronaut-serde/src/test/java/example/ApiController.java fragment="get_endpoint" %}
```

And finally, let's run the application and try the `/operations/last` endpoint we just created:

```bash
curl 'http://localhost:8080/operations/last'
```

We'll see that we get a Micronaut `CodecException` caused by a `SerdeException`:

```
{% include_relative result-micronaut-serde/src/test/resources/serialization_error.txt %}
```

Although this may look strange, it's actually what we should expect. Even though we annotated `ApiOperation` as
`@Serdeable`, Micronaut doesn't know how to serialize result objects yet, so the data structure cannot be serialized.

```java
{% include_relative result-micronaut-serde/src/test/java/example/ProblemTest.java test="serialization_problem" %}
```

This is Micronaut's default serialization behavior. But we'd like to serialize the `result` field like this:

```json
{% include_relative result-micronaut-serde/src/test/resources/expected_serialized_result.json %}
```


### Deserialization Problem

Now, let's reverse our previous example, this time trying to receive an <tt>ApiOperation</tt> as the body of a `POST`
request:

```java
{% include_relative result-micronaut-serde/src/test/java/example/ApiController.java fragment="post_endpoint" %}
```

We'll see that now we get an `InvalidDefinitionException`. Let's view the stack trace:

```
{% include_relative result-micronaut-serde/src/test/resources/deserialization_error.txt %}
```

This behavior again makes sense. Essentially, Micronaut doesn't have a clue how to create new <tt>Result</tt> objects,
because <tt>Result</tt> is not annotated as `@Introspected` or `@Serdeable`.

```java
{% include_relative result-micronaut-serde/src/test/java/example/ProblemTest.java test="deserialization_problem" %}
```


## Solution

What we want, is for Micronaut to treat <tt>Result</tt> values as JSON objects that contain either a `success` or a
`failure` value. Fortunately, this problem has been solved for us. [This library][RESULT_MICRONAUT_SERDE_REPO] provides
serialization / deserialization support for <tt>Result</tt> objects.

All we need to do now is add the latest version as a Maven dependency:

- groupId: `com.leakyabstractions`
- artifactId: `result-micronaut-serde`
- version: `{{ site.current_version }}`

Once the library is in the classpath, all functionality is available for all normal Micronaut operations.


### Serialization Solution

Now, let's try and serialize our `ApiOperation` object again:

```java
{% include_relative result-micronaut-serde/src/test/java/example/SolutionTest.java test="serialization_solution_successful_result" %}
```

If we look at the serialized response, we'll see that this time the `result` field contains a `success` field:

```json
{% include_relative result-micronaut-serde/src/test/resources/serialization_solution_successful_result.json %}
```

Next, we can try serializing a failed result:

```java
{% include_relative result-micronaut-serde/src/test/java/example/SolutionTest.java test="serialization_solution_failed_result" %}
```

And we can verify that the serialized response contains a non-null `failure` value and a null `success` value:

```json
{% include_relative result-micronaut-serde/src/test/resources/serialization_solution_failed_result.json %}
```


### Deserialization Solution

Now, let's repeat our tests for deserialization. If we read our `ApiOperation` again, we'll see that we no longer get an
`InvalidDefinitionException`:

```java
{% include_relative result-micronaut-serde/src/test/java/example/SolutionTest.java test="deserialization_solution_successful_result" %}
```

Finally, let's repeat the test again, this time with a failed result. We'll see that yet again we don't get an
exception, and in fact, have a failed <tt>Result</tt>:

```java
{% include_relative result-micronaut-serde/src/test/java/example/SolutionTest.java test="deserialization_solution_failed_result" %}
```


## Conclusion

We've shown how to use [Result][RESULT] with [Micronaut][MICRONAUT] without any problems by leveraging the
[Micronaut Serde imports for Results][RESULT_MICRONAUT_SERDE_REPO], demonstrating how it enables Micronaut to treat
<tt>Result</tt> objects as ordinary fields.

The implementation of these examples can be found [here][EXAMPLE].


[EXAMPLE]: https://github.com/LeakyAbstractions/result-micronaut-serde/blob/main/result-micronaut-serde/src/test/java/example/SolutionTest.java
[MICRONAUT_LAUNCH]: https://micronaut.io/launch
[MICRONAUT]: https://micronaut.io/
[MICRONAUT_SERDE]: https://micronaut-projects.github.io/micronaut-serialization/latest/guide/
[RESULT]: https://dev.leakyabstractions.com/result/
[RESULT_MICRONAUT_SERDE_REPO]: https://github.com/LeakyAbstractions/result-micronaut-serde/
[RESULT_LATEST]: https://search.maven.org/artifact/com.leakyabstractions/result/
[RESULT_REPO]: https://github.com/LeakyAbstractions/result/
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,4 @@ Map<String, String> notify(@Body ApiOperation op) {
.orElseMap(f -> Map.of("error", op.name() + " failed: " + f));
}
// This endpoint returns a simple map for the sake of simplicity{% endif %}

}
14 changes: 9 additions & 5 deletions result-micronaut-serde/src/test/java/example/SolutionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class SolutionTest {

/** {% elsif include.test == "serialization_solution_successful_result" %} Test serialization solution with a successful result */
@Test
void serialization_solution_successful_result(ObjectMapper objectMapper) throws IOException {
void serialization_solution_successful_result(ObjectMapper objectMapper)
throws IOException {
// Given
ApiOperation op = new ApiOperation("clean", success("All good"));
// When
Expand All @@ -32,7 +33,8 @@ void serialization_solution_successful_result(ObjectMapper objectMapper) throws

/** {% elsif include.test == "serialization_solution_failed_result" %} Test serialization problem with a failed result */
@Test
void serialization_solution_failed_result(ObjectMapper objectMapper) throws IOException {
void serialization_solution_failed_result(ObjectMapper objectMapper)
throws IOException {
// Given
ApiOperation op = new ApiOperation("build", failure("Oops"));
// When
Expand All @@ -44,7 +46,8 @@ void serialization_solution_failed_result(ObjectMapper objectMapper) throws IOEx

/** {% elsif include.test == "deserialization_solution_successful_result" %} Test deserialization solution with a successful result */
@Test
void deserialization_solution_successful_result(ObjectMapper objectMapper) throws IOException {
void deserialization_solution_successful_result(ObjectMapper objectMapper)
throws IOException {
// Given
String json = """
{"name":"check","result":{"success":"Yay"}}""";
Expand All @@ -57,7 +60,8 @@ void deserialization_solution_successful_result(ObjectMapper objectMapper) throw

/** {% elsif include.test == "deserialization_solution_failed_result" %} Test deserialization solution with a failed result */
@Test
void deserialization_solution_failed_result(ObjectMapper objectMapper) throws IOException {
void deserialization_solution_failed_result(ObjectMapper objectMapper)
throws IOException {
// Given
String json = """
{"name":"start","result":{"failure":"Nay"}}""";
Expand All @@ -69,4 +73,4 @@ void deserialization_solution_failed_result(ObjectMapper objectMapper) throws IO
} // End{% endif %}{% if false %}

}
// {% endif %}
// {% endif %}

0 comments on commit cc4453e

Please sign in to comment.