Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent scalars from being wrapped #34

Merged
merged 2 commits into from
Dec 4, 2024
Merged
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
2 changes: 1 addition & 1 deletion .github/badges/jacoco.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

### Changed
* 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

## [1.0.0]

Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ ___

hateoflux is a lightweight, reactive-first Java library designed to streamline the creation of hypermedia-driven APIs in Spring WebFlux applications. It addresses the limitations of Spring HATEOAS in reactive environments, offering a more intuitive and maintainable approach to building HAL+JSON compliant APIs.

<br>
<p style="text-align:center">
<a href="https://github.com/kamillionlabs/hateoflux#getting-started">Getting Started</a> |
<a href="https://hateoflux.kamillionlabs.de/docs/cookbook.html">Cookbook</a> |
<a href="https://hateoflux.kamillionlabs.de">Documentation</a> |
<a href="https://github.com/kamillionlabs/hateoflux-demos">Demos</a>
</p>


## Table of Contents

- [Why hateoflux?](#why-hateoflux)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ public static <EmbeddedT> HalEmbeddedWrapper<EmbeddedT> wrap(EmbeddedT resourceT
}
Assert.isTrue(!(resourceToWrap instanceof Iterable<?>), valueIsNotAllowedToBeOfType("Resource to embed",
"collection/iterable"));
Assert.isTrue(!isScalar(resourceToWrap.getClass()), valueIsNotAllowedToBeOfType("Embedded",
"scalar (e.g. String, int, etc.)"));
return new HalEmbeddedWrapper<>(resourceToWrap);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import de.kamillionlabs.hateoflux.model.link.Link;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;

Expand Down Expand Up @@ -65,6 +67,8 @@ public final class HalResourceWrapper<ResourceT, EmbeddedT>
private Map.Entry<String, List<HalEmbeddedWrapper<EmbeddedT>>> embedded;

@JsonIgnore
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
private Boolean isEmbeddedOriginallyAList;

@JsonProperty("_embedded")
Expand Down Expand Up @@ -131,6 +135,8 @@ public static <ResourceT> HalResourceWrapper<ResourceT, Void> wrap(@NonNull Reso
Assert.isTrue(!(resource instanceof Iterable<?>), valueIsNotAllowedToBeOfType("Resource", "collection" +
"/iterable. " +
"Use HalListWrapper instead"));
Assert.isTrue(!isScalar(resource.getClass()), valueIsNotAllowedToBeOfType("Resource",
"scalar (e.g. String, int, etc.)"));
return new HalResourceWrapper<>(resource);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,5 +281,13 @@ protected static String determineRelationNameForObject(Object object) {
}
}

protected static boolean isScalar(Class<?> clazz) {
return clazz.isPrimitive()
|| clazz.equals(String.class)
|| Number.class.isAssignableFrom(clazz)
|| clazz.equals(Boolean.class)
|| clazz.equals(Character.class);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package de.kamillionlabs.hateoflux.model.hal;

import org.junit.jupiter.api.Test;

import java.math.BigDecimal;

import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;

class HalEmbeddedWrapperTest {

@Test
void givenString_whenWrapping_thenThrowsException() {
assertThatThrownBy(() -> HalEmbeddedWrapper.wrap("hello"))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Embedded is not allowed to be of type scalar (e.g. String, int, etc.)");
}

@Test
void givenCharacter_whenWrapping_thenThrowsException() {
assertThatThrownBy(() -> HalEmbeddedWrapper.wrap('h'))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Embedded is not allowed to be of type scalar (e.g. String, int, etc.)");
}

@Test
void givenPrimitiveInt_whenWrapping_thenThrowsException() {
assertThatThrownBy(() -> HalEmbeddedWrapper.wrap(3))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Embedded is not allowed to be of type scalar (e.g. String, int, etc.)");
}

@Test
void givenBigDecimal_whenWrapping_thenThrowsException() {
assertThatThrownBy(() -> HalEmbeddedWrapper.wrap(new BigDecimal("4536433.58")))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Embedded is not allowed to be of type scalar (e.g. String, int, etc.)");
}

@Test
void givenBoolean_whenWrapping_thenThrowsException() {
assertThatThrownBy(() -> HalEmbeddedWrapper.wrap(false))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Embedded is not allowed to be of type scalar (e.g. String, int, etc.)");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.assertj.core.api.AssertionsForClassTypes;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
Expand Down Expand Up @@ -79,4 +80,39 @@ void givenEmptyCollection_whenDetermineRelationNameForObject_thenThrowsException
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Iterable cannot be empty when determining relation names");
}

@Test
void givenString_whenWrapping_thenThrowsException() {
assertThatThrownBy(() -> HalResourceWrapper.wrap("hello"))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Resource is not allowed to be of type scalar (e.g. String, int, etc.)");
}

@Test
void givenCharacter_whenWrapping_thenThrowsException() {
assertThatThrownBy(() -> HalResourceWrapper.wrap('h'))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Resource is not allowed to be of type scalar (e.g. String, int, etc.)");
}

@Test
void givenPrimitiveInt_whenWrapping_thenThrowsException() {
assertThatThrownBy(() -> HalResourceWrapper.wrap(3))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Resource is not allowed to be of type scalar (e.g. String, int, etc.)");
}

@Test
void givenBigDecimal_whenWrapping_thenThrowsException() {
assertThatThrownBy(() -> HalResourceWrapper.wrap(new BigDecimal("4536433.58")))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Resource is not allowed to be of type scalar (e.g. String, int, etc.)");
}

@Test
void givenBoolean_whenWrapping_thenThrowsException() {
assertThatThrownBy(() -> HalResourceWrapper.wrap(false))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Resource is not allowed to be of type scalar (e.g. String, int, etc.)");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,6 @@ public void givenHalListWrapperWithEmbeddingListsAndEmptyOnes_whenSerialized_the
String actualJson = mapper.writeValueAsString(halListWrapper);

// THEN
System.out.println(actualJson);
JSONAssert.assertEquals("""
{
"_embedded": {
Expand Down
Loading