Skip to content

Commit

Permalink
Create quarkus hal extension for resteasy and reactive
Browse files Browse the repository at this point in the history
The quarkus-hal is a new extension that supports both resteasy and resteasy reactive.
  • Loading branch information
Sgitario committed May 10, 2022
1 parent 00ef4ce commit 94a2690
Show file tree
Hide file tree
Showing 75 changed files with 1,406 additions and 1,154 deletions.
10 changes: 10 additions & 0 deletions bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,16 @@
<artifactId>quarkus-jsonp-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hal</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hal-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-kotlin-serialization</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public interface Capability {

String JSONB = QUARKUS_PREFIX + "jsonb";

String HAL = QUARKUS_PREFIX + "hal";

String REST = QUARKUS_PREFIX + "rest";
String REST_CLIENT = REST + ".client";
String REST_JACKSON = REST + ".jackson";
Expand Down
13 changes: 13 additions & 0 deletions devtools/bom-descriptor-json/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,19 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hal</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-envers</artifactId>
Expand Down
13 changes: 13 additions & 0 deletions docs/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,19 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hal-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-envers-deployment</artifactId>
Expand Down
141 changes: 141 additions & 0 deletions docs/src/main/asciidoc/resteasy-reactive.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1231,6 +1231,147 @@ public class RecordsResource {

Using this injected bean of type `RestLinksProvider`, you can get the links by type using the method `RestLinksProvider.getTypeLinks` or get the links by a concrete instance using the method `RestLinksProvider.getInstanceLinks`.

==== JSON Hypertext Application Language (HAL) support

The https://tools.ietf.org/id/draft-kelly-json-hal-01.html[HAL] standard is a simple format to represent web links.

To enable the HAL support, add the `quarkus-hal` extension to your project. Also, as HAL needs JSON support, you need to add either the `quarkus-resteasy-reactive-jsonb` or the `quarkus-resteasy-reactive-jackson` extension.

.Table Context object
|===
|GAV|Usage

|`io.quarkus:quarkus-hal`
|https://tools.ietf.org/id/draft-kelly-json-hal-01.html[HAL]

|===

After adding the extensions, we can now annotate the REST resources to produce the media type `application/hal+json` (or use RestMediaType.APPLICATION_HAL_JSON). For example:

[source,java]
----
@Path("/records")
public class RecordsResource {
@GET
@Produces({ MediaType.APPLICATION_JSON, RestMediaType.APPLICATION_HAL_JSON })
@RestLink(rel = "list")
@InjectRestLinks
public List<Record> getAll() {
// ...
}
@GET
@Produces({ MediaType.APPLICATION_JSON, RestMediaType.APPLICATION_HAL_JSON })
@Path("/{id}")
@RestLink(rel = "self")
@InjectRestLinks(RestLinkType.INSTANCE)
public TestRecord get(@PathParam("id") int id) {
// ...
}
}
----

Now, the endpoints `/records` and `/records/{id}` will accept the media type both `json` and `hal+json` to print the records in Hal format.

For example, if we invoke the `/records` endpoint using curl to return a list of records, the HAL format will look like as follows:

[source,bash]
----
& curl -H "Accept:application/hal+json" -i localhost:8080/records
{
"_embedded": {
"items": [
{
"id": 1,
"slug": "first",
"value": "First value",
"_links": {
"self": {
"href": "http://localhost:8081/records/1"
},
"list": {
"href": "http://localhost:8081/records"
}
}
},
{
"id": 2,
"slug": "second",
"value": "Second value",
"_links": {
"self": {
"href": "http://localhost:8081/records/2"
},
"list": {
"href": "http://localhost:8081/records"
}
}
}
]
},
"_links": {
"list": {
"href": "http://localhost:8081/records"
}
}
}
----

When we call a resource `/records/1` that returns only one instance, then the output is:

[source,bash]
----
& curl -H "Accept:application/hal+json" -i localhost:8080/records/1
{
"id": 1,
"slug": "first",
"value": "First value",
"_links": {
"self": {
"href": "http://localhost:8081/records/1"
},
"list": {
"href": "http://localhost:8081/records"
}
}
}
----

Finally, you can also provide additional HAL links programmatically in your resource just by returning either `HalCollectionWrapper` (to return a list of entities) or `HalEntityWrapper` (to return a single object) as described in the following example:

[source,java]
----
@Path("/records")
public class RecordsResource {
@Inject
RestLinksProvider linksProvider;
@GET
@Produces({ MediaType.APPLICATION_JSON, RestMediaType.APPLICATION_HAL_JSON })
@RestLink(rel = "list")
public HalCollectionWrapper getAll() {
List<Record> list = // ...
HalCollectionWrapper halCollection = new HalCollectionWrapper(list, "collectionName", linksProvider.getTypeLinks(Record.class));
halCollection.addLinks(Link.fromPath("/records/1").rel("first-record").build());
return halCollection;
}
@GET
@Produces({ MediaType.APPLICATION_JSON, RestMediaType.APPLICATION_HAL_JSON })
@Path("/{id}")
@RestLink(rel = "self")
@InjectRestLinks(RestLinkType.INSTANCE)
public HalEntityWrapper get(@PathParam("id") int id) {
Record entity = // ...
HalEntityWrapper halEntity = new HalEntityWrapper(entity, linksProvider.getInstanceLinks(entity));
halEntity.addLinks(Link.fromPath("/records/1/parent").rel("parent-record").build());
return halEntity;
}
}
----

== CORS filter

link:https://en.wikipedia.org/wiki/Cross-origin_resource_sharing[Cross-origin resource sharing] (CORS) is a mechanism that
Expand Down
105 changes: 105 additions & 0 deletions docs/src/main/asciidoc/resteasy.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,111 @@ public class CustomJsonbConfig {
}
----

[[links]]
=== JSON Hypertext Application Language (HAL) support

The https://tools.ietf.org/id/draft-kelly-json-hal-01.html[HAL] standard is a simple format to represent web links.

To enable the HAL support, add the `quarkus-hal` extension to your project. Also, as HAL needs JSON support, you need to add either the `quarkus-resteasy-jsonb` or the `quarkus-resteasy-jackson` extension.

.Table Context object
|===
|GAV|Usage

|`io.quarkus:quarkus-hal`
|https://tools.ietf.org/id/draft-kelly-json-hal-01.html[HAL]

|===

After adding the extensions, we can now annotate the REST resources to produce the media type `application/hal+json` (or use RestMediaType.APPLICATION_HAL_JSON). For example:

[source,java]
----
@Path("/records")
public class RecordsResource {
@GET
@Produces({ MediaType.APPLICATION_JSON, "application/hal+json" })
@LinkResource(entityClassName = "org.acme.Record", rel = "list")
public List<TestRecord> getAll() {
// ...
}
@GET
@Path("/first")
@Produces({ MediaType.APPLICATION_JSON, "application/hal+json" })
@LinkResource(rel = "first")
public TestRecord getFirst() {
// ...
}
}
----

Now, the endpoints `/records` and `/records/first` will accept the media type both `json` and `hal+json` to print the records in Hal format.

For example, if we invoke the `/records` endpoint using curl to return a list of records, the HAL format will look like as follows:

[source,bash]
----
& curl -H "Accept:application/hal+json" -i localhost:8080/records
{
"_embedded": {
"items": [
{
"id": 1,
"slug": "first",
"value": "First value",
"_links": {
"list": {
"href": "http://localhost:8081/records"
},
"first": {
"href": "http://localhost:8081/records/first"
}
}
},
{
"id": 2,
"slug": "second",
"value": "Second value",
"_links": {
"list": {
"href": "http://localhost:8081/records"
},
"first": {
"href": "http://localhost:8081/records/first"
}
}
}
]
},
"_links": {
"list": {
"href": "http://localhost:8081/records"
}
}
}
----

When we call a resource `/records/first` that returns only one instance, then the output is:

[source,bash]
----
& curl -H "Accept:application/hal+json" -i localhost:8080/records/first
{
"id": 1,
"slug": "first",
"value": "First value",
"_links": {
"list": {
"href": "http://localhost:8081/records"
},
"first": {
"href": "http://localhost:8081/records/first"
}
}
}
----

== Creating a frontend

Expand Down
60 changes: 60 additions & 0 deletions extensions/hal/deployment/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>quarkus-hal-parent</artifactId>
<groupId>io.quarkus</groupId>
<version>999-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>quarkus-hal-deployment</artifactId>
<name>Quarkus - HAL - Deployment</name>

<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hal</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jackson-spi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jsonb-spi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-internal</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-extension-processor</artifactId>
<version>${project.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>

</project>
Loading

0 comments on commit 94a2690

Please sign in to comment.