Skip to content

Commit

Permalink
cleaned up and comments added
Browse files Browse the repository at this point in the history
  • Loading branch information
yelouarti committed Dec 17, 2024
1 parent 5357266 commit b8ae345
Show file tree
Hide file tree
Showing 21 changed files with 1,448 additions and 1,575 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [1.1.0-SNAPSHOT]

### Added
* New `ReactiveResponseEntity` classes with its handler and autoconfiguration

### Changed
* HalResourecWrapperis#EmbeddedOriginallyAList is now private
* `HalResourecWrapperis#EmbeddedOriginallyAList` is now private

### Fixed
* Single scalars (e.g. String, Integers, Boolean, etc.) are now not allowed in Wrappers and will result in an exception
Expand Down
19 changes: 19 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,22 @@ repositories {
}

tasks.named('test') {
doFirst {
// Resolve the path to the mockito-agent JAR
def mockitoAgentPath = configurations.mockitoAgent.singleFile.absolutePath
println "Using Mockito Java Agent at: ${mockitoAgentPath}"

// Add the Java agent JVM argument
jvmArgs "-javaagent:${mockitoAgentPath}"
}

useJUnitPlatform()
}

configurations {
mockitoAgent
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-json'
implementation 'org.springframework:spring-webflux'
Expand All @@ -43,8 +56,14 @@ dependencies {

testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'

// Add mockito-core to the mockitoAgent configuration without transitive dependencies
mockitoAgent 'org.mockito:mockito-core:5.14.0', {
transitive = false
}
}


jar {
enabled = true
archiveClassifier = ''
Expand Down
183 changes: 142 additions & 41 deletions src/main/java/de/kamillionlabs/hateoflux/http/HalListResponse.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.kamillionlabs.hateoflux.http;

import de.kamillionlabs.hateoflux.model.hal.HalListWrapper;
import de.kamillionlabs.hateoflux.model.hal.HalResourceWrapper;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand All @@ -14,7 +15,51 @@
import static de.kamillionlabs.hateoflux.utility.ValidationMessageTemplates.valueNotAllowedToBeNull;

/**
* A reactive response representation for HAL resources, wrapping a {@link HalListWrapper}
* along with an HTTP status and optional HTTP headers.
*
* <p>This class extends {@link HttpHeadersModule} to provide fluent methods for adding HTTP headers,
* and implements {@link ReactiveResponseEntity} to allow for automatic serialization by the
* {@link ReactiveResponseEntityHandlerResultHandler}.</p>
*
* <p>The core functionality of this class is to encapsulate a reactive HAL resource, represented as a
* {@link Mono} of {@link HalListWrapper}, and to transform it into a standard {@link ResponseEntity}
* via the {@link #toResponseEntity()} method. This enables seamless integration of HAL-based hypermedia
* resources into the reactive response handling pipeline.</p>
*
* <p>The HTTP status is also reactive, defined as a {@link Mono<HttpStatus>} that defaults to
* {@link #DEFAULT_STATUS}. Headers can be optionally provided or constructed using this builder's methods.</p>
*
* <p>Convenience factory methods such as {@link #ok(Mono)}, {@link #created(Mono)}, and others
* produce commonly used response configurations with specific HTTP statuses.</p>
*
* <p>Example usage:</p>
* <pre>{@code
* Mono<HalListResponse<MyResource, MyEmbedded>> body = ...;
* HalListResponse<MyResource, MyEmbedded> response = HalListResponse.ok(body)
* .withContentType("application/hal+json")
* .withETag("\"123456\"");
* }</pre>
*
* <p>
* <strong>Usage Guidelines:</strong>
* </p>
* <ul>
* <li><strong>{@link HalResourceResponse}</strong>: Use when your API endpoint returns a <b>single</b>
* {@link HalResourceWrapper}/li>
* <li><strong>{@link HalMultiResourceResponse}</strong>: Use when your endpoint returns <b>multiple</b>
* {@link HalResourceWrapper}</li>
* <li><strong>{@link HalListResponse}</strong>: Use when your endpoint returns a single {@link HalListWrapper}</li>
* </ul>
*
* @param <ResourceT>
* the type of the resource represented by the HAL wrapper
* @param <EmbeddedT>
* the type of the embedded resources represented by the HAL wrapper
* @author Younes El Ouarti
* @see ReactiveResponseEntity
* @see ResponseEntity
* @see HttpHeadersModule
*/
public class HalListResponse<ResourceT, EmbeddedT>
extends HttpHeadersModule<HalListResponse<ResourceT, EmbeddedT>>
Expand All @@ -23,9 +68,20 @@ public class HalListResponse<ResourceT, EmbeddedT>
private final Mono<HalListWrapper<ResourceT, EmbeddedT>> body;
private final Mono<HttpStatus> status;

private HalListResponse(Mono<HalListWrapper<ResourceT, EmbeddedT>> body,
Mono<HttpStatus> httpStatus,
MultiValueMap<String, String> headers) {
/**
* Constructs a new {@link HalListResponse} with the given body, HTTP status, and headers.
*
* @param body
* a {@link Mono} of {@link HalListResponse} representing the HAL resource body
* @param httpStatus
* a {@link Mono} of {@link HttpStatus} to be associated with the response; defaults to
* {@link #DEFAULT_STATUS} if empty
* @param headers
* an optional set of HTTP headers
*/
public HalListResponse(Mono<HalListWrapper<ResourceT, EmbeddedT>> body,
Mono<HttpStatus> httpStatus,
MultiValueMap<String, String> headers) {
this.status = httpStatus.defaultIfEmpty(DEFAULT_STATUS);
this.body = body;
this.headers = Optional.ofNullable(headers)
Expand All @@ -44,32 +100,71 @@ public Mono<ResponseEntity<?>> toResponseEntity() {

// Static Factory Method

/**
* Creates a {@link HalListResponse} with the given HAL resource body and HTTP status provided as a
* {@link Mono}.
*
* @param body
* a {@link Mono} of {@link HalListWrapper} representing the HAL resource body
* @param httpStatus
* a {@link Mono} of {@link HttpStatus}
* @param <ResourceT>
* the resource type
* @param <EmbeddedT>
* the embedded resource type
* @return the created {@link HalListResponse}
*/
public static <ResourceT, EmbeddedT> HalListResponse<ResourceT, EmbeddedT> of(
@NonNull Mono<HalListWrapper<ResourceT, EmbeddedT>> body,
@NonNull Mono<HttpStatus> httpStatus) {
return new HalListResponse<>(body, httpStatus, null);
}

/**
* Creates a {@link HalListResponse} with an empty body and the given HTTP status provided as a {@link Mono}.
*
* @param httpStatus
* a {@link Mono} of {@link HttpStatus}
* @param <ResourceT>
* the resource type
* @param <EmbeddedT>
* the embedded resource type
* @return the created {@link HalListResponse}
*/
public static <ResourceT, EmbeddedT> HalListResponse<ResourceT, EmbeddedT> of(
@NonNull Mono<HttpStatus> httpStatus) {
return new HalListResponse<>(Mono.empty(), httpStatus, null);
}

/**
* Creates a {@link HalListResponse} with an empty body and the given {@link HttpStatus}.
*
* @param httpStatus
* the {@link HttpStatus} to use
* @param <ResourceT>
* the resource type
* @param <EmbeddedT>
* the embedded resource type
* @return the created {@link HalListResponse}
*/
public static <ResourceT, EmbeddedT> HalListResponse<ResourceT, EmbeddedT> of(
@NonNull HttpStatus httpStatus) {
return new HalListResponse<>(Mono.empty(), Mono.just(httpStatus), null);
}

/**
* Creates a {@link HalListResponse} with {@link HttpStatus#OK} and the given body.
* Creates a {@link HalListResponse} with a body and {@link HttpStatus#OK}.
*
* @param body
* the {@link Mono} of {@link HalListWrapper} representing the body; must not be null
* @param <ResourceT>
* resource type
* the resource type
* @param <EmbeddedT>
* embedded type
* @param body
* the HAL wrapper
* @return the created response
* the embedded resource type
* @return a {@link HalListResponse} with {@code OK} status and the given body
*
* @throws IllegalArgumentException
* if {@code body} is null
*/
public static <ResourceT, EmbeddedT> HalListResponse<ResourceT, EmbeddedT> ok(
@NonNull Mono<HalListWrapper<ResourceT, EmbeddedT>> body) {
Expand All @@ -78,15 +173,18 @@ public static <ResourceT, EmbeddedT> HalListResponse<ResourceT, EmbeddedT> ok(
}

/**
* Creates a {@link HalListResponse} with {@link HttpStatus#CREATED} and the given body.
* Creates a {@link HalListResponse} with a body and {@link HttpStatus#CREATED}.
*
* @param body
* the {@link Mono} of {@link HalListWrapper} representing the body; must not be null
* @param <ResourceT>
* resource type
* the resource type
* @param <EmbeddedT>
* embedded type
* @param body
* the HAL wrapper
* @return the created response
* the embedded resource type
* @return a {@link HalListResponse} with {@code CREATED} status and the given body
*
* @throws IllegalArgumentException
* if {@code body} is null
*/
public static <ResourceT, EmbeddedT> HalListResponse<ResourceT, EmbeddedT> created(
@NonNull Mono<HalListWrapper<ResourceT, EmbeddedT>> body) {
Expand All @@ -95,15 +193,18 @@ public static <ResourceT, EmbeddedT> HalListResponse<ResourceT, EmbeddedT> creat
}

/**
* Creates a {@link HalListResponse} with {@link HttpStatus#ACCEPTED} and the given body.
* Creates a {@link HalListResponse} with a body and {@link HttpStatus#ACCEPTED}.
*
* @param body
* the {@link Mono} of {@link HalListWrapper} representing the body; must not be null
* @param <ResourceT>
* resource type
* the resource type
* @param <EmbeddedT>
* embedded type
* @param body
* the HAL wrapper
* @return the created response
* the embedded resource type
* @return a {@link HalListResponse} with {@code ACCEPTED} status and the given body
*
* @throws IllegalArgumentException
* if {@code body} is null
*/
public static <ResourceT, EmbeddedT> HalListResponse<ResourceT, EmbeddedT> accepted(
@NonNull Mono<HalListWrapper<ResourceT, EmbeddedT>> body) {
Expand All @@ -112,65 +213,65 @@ public static <ResourceT, EmbeddedT> HalListResponse<ResourceT, EmbeddedT> accep
}

/**
* Creates a {@link HalListResponse} with {@link HttpStatus#NO_CONTENT}.
* Creates a {@link HalListResponse} with no body and {@link HttpStatus#NO_CONTENT}.
*
* @param <ResourceT>
* resource type
* the resource type
* @param <EmbeddedT>
* embedded type
* @return the created response
* the embedded resource type
* @return a {@link HalListResponse} with {@code NO_CONTENT} status
*/
public static <ResourceT, EmbeddedT> HalListResponse<ResourceT, EmbeddedT> noContent() {
return new HalListResponse<>(Mono.empty(), Mono.just(HttpStatus.NO_CONTENT), null);
}

/**
* Creates a {@link HalListResponse} with {@link HttpStatus#BAD_REQUEST}.
* Creates a {@link HalListResponse} with no body and {@link HttpStatus#BAD_REQUEST}.
*
* @param <ResourceT>
* resource type
* the resource type
* @param <EmbeddedT>
* embedded type
* @return the created response
* the embedded resource type
* @return a {@link HalListResponse} with {@code BAD_REQUEST} status
*/
public static <ResourceT, EmbeddedT> HalListResponse<ResourceT, EmbeddedT> badRequest() {
return new HalListResponse<>(Mono.empty(), Mono.just(HttpStatus.BAD_REQUEST), null);
}

/**
* Creates a {@link HalListResponse} with {@link HttpStatus#NOT_FOUND}.
* Creates a {@link HalListResponse} with no body and {@link HttpStatus#NOT_FOUND}.
*
* @param <ResourceT>
* resource type
* the resource type
* @param <EmbeddedT>
* embedded type
* @return the created response
* the embedded resource type
* @return a {@link HalListResponse} with {@code NOT_FOUND} status
*/
public static <ResourceT, EmbeddedT> HalListResponse<ResourceT, EmbeddedT> notFound() {
return new HalListResponse<>(Mono.empty(), Mono.just(HttpStatus.NOT_FOUND), null);
}

/**
* Creates a {@link HalListResponse} with {@link HttpStatus#FORBIDDEN}.
* Creates a {@link HalListResponse} with no body and {@link HttpStatus#FORBIDDEN}.
*
* @param <ResourceT>
* resource type
* the resource type
* @param <EmbeddedT>
* embedded type
* @return the created response
* the embedded resource type
* @return a {@link HalListResponse} with {@code FORBIDDEN} status
*/
public static <ResourceT, EmbeddedT> HalListResponse<ResourceT, EmbeddedT> forbidden() {
return new HalListResponse<>(Mono.empty(), Mono.just(HttpStatus.FORBIDDEN), null);
}

/**
* Creates a {@link HalListResponse} with {@link HttpStatus#UNAUTHORIZED}.
* Creates a {@link HalListResponse} with no body and {@link HttpStatus#UNAUTHORIZED}.
*
* @param <ResourceT>
* resource type
* the resource type
* @param <EmbeddedT>
* embedded type
* @return the created response
* the embedded resource type
* @return a {@link HalListResponse} with {@code UNAUTHORIZED} status
*/
public static <ResourceT, EmbeddedT> HalListResponse<ResourceT, EmbeddedT> unauthorized() {
return new HalListResponse<>(Mono.empty(), Mono.just(HttpStatus.UNAUTHORIZED), null);
Expand Down
Loading

0 comments on commit b8ae345

Please sign in to comment.