-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
34c8ba3
commit 3818525
Showing
11 changed files
with
537 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package com.cosium.hal_mock_mvc; | ||
|
||
import static java.util.Objects.requireNonNull; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
|
||
/** | ||
* @author Réda Housni Alaoui | ||
*/ | ||
public class Form { | ||
|
||
private final RequestExecutor requestExecutor; | ||
private final ObjectMapper objectMapper; | ||
private final Template template; | ||
|
||
private final Map<String, FormProperty<?>> propertyByName = new HashMap<>(); | ||
|
||
Form(RequestExecutor requestExecutor, ObjectMapper objectMapper, Template template) { | ||
this.requestExecutor = requireNonNull(requestExecutor); | ||
this.objectMapper = requireNonNull(objectMapper); | ||
this.template = requireNonNull(template); | ||
} | ||
|
||
public Form withStringValue(String propertyName, String value) throws Exception { | ||
FormProperty<?> property = | ||
new FormProperty<>(String.class, propertyName, List.of(value), false); | ||
propertyByName.put(property.name(), assertValid(property)); | ||
return this; | ||
} | ||
|
||
public Form withBooleanValue(String propertyName, Boolean value) throws Exception { | ||
FormProperty<?> property = | ||
new FormProperty<>(Boolean.class, propertyName, List.of(value), false); | ||
propertyByName.put(property.name(), assertValid(property)); | ||
return this; | ||
} | ||
|
||
public Form withIntegerValue(String propertyName, Integer value) throws Exception { | ||
FormProperty<?> property = | ||
new FormProperty<>(Integer.class, propertyName, List.of(value), false); | ||
propertyByName.put(property.name(), assertValid(property)); | ||
return this; | ||
} | ||
|
||
public Form withLongValue(String propertyName, Long value) throws Exception { | ||
FormProperty<?> property = new FormProperty<>(Long.class, propertyName, List.of(value), false); | ||
propertyByName.put(property.name(), assertValid(property)); | ||
return this; | ||
} | ||
|
||
public Form withDoubleValue(String propertyName, Double value) throws Exception { | ||
FormProperty<?> property = | ||
new FormProperty<>(Double.class, propertyName, List.of(value), false); | ||
propertyByName.put(property.name(), assertValid(property)); | ||
return this; | ||
} | ||
|
||
public Form withStringValues(String propertyName, List<String> value) throws Exception { | ||
FormProperty<?> property = new FormProperty<>(String.class, propertyName, value, true); | ||
propertyByName.put(property.name(), assertValid(property)); | ||
return this; | ||
} | ||
|
||
public Form withBooleanValues(String propertyName, List<Boolean> value) throws Exception { | ||
FormProperty<?> property = new FormProperty<>(Boolean.class, propertyName, value, true); | ||
propertyByName.put(property.name(), assertValid(property)); | ||
return this; | ||
} | ||
|
||
public Form withIntValues(String propertyName, List<Integer> value) throws Exception { | ||
FormProperty<?> property = new FormProperty<>(Integer.class, propertyName, value, true); | ||
propertyByName.put(property.name(), assertValid(property)); | ||
return this; | ||
} | ||
|
||
public Form withLongValues(String propertyName, List<Long> value) throws Exception { | ||
FormProperty<?> property = new FormProperty<>(Long.class, propertyName, value, true); | ||
propertyByName.put(property.name(), assertValid(property)); | ||
return this; | ||
} | ||
|
||
public Form withDoubleValues(String propertyName, List<Double> value) throws Exception { | ||
FormProperty<?> property = new FormProperty<>(Double.class, propertyName, value, true); | ||
propertyByName.put(property.name(), assertValid(property)); | ||
return this; | ||
} | ||
|
||
private FormProperty<?> assertValid(FormProperty<?> property) throws Exception { | ||
TemplatePropertyRepresentation representation = requireTemplate(property); | ||
if (representation.readOnly()) { | ||
throw new IllegalArgumentException( | ||
"Cannot set value for read-only property '%s'".formatted(property.name())); | ||
} | ||
return new TemplateProperty(requestExecutor, objectMapper, representation) | ||
.assertValid(property); | ||
} | ||
|
||
private TemplatePropertyRepresentation requireTemplate(FormProperty<?> property) { | ||
return Optional.ofNullable(template.representation().propertyByName().get(property.name())) | ||
.orElseThrow( | ||
() -> | ||
new IllegalArgumentException( | ||
"No property found for name '%s'".formatted(property.name()))); | ||
} | ||
} |
58 changes: 58 additions & 0 deletions
58
core/src/main/java/com/cosium/hal_mock_mvc/FormProperty.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package com.cosium.hal_mock_mvc; | ||
|
||
import static java.util.Objects.requireNonNull; | ||
|
||
import java.util.List; | ||
import java.util.Set; | ||
|
||
/** | ||
* @author Réda Housni Alaoui | ||
*/ | ||
record FormProperty<T>(Class<T> valueType, String name, List<T> values, boolean array) { | ||
|
||
FormProperty { | ||
if (!array && values.size() > 1) { | ||
throw new IllegalArgumentException("Non array property can't hold more than 1 value."); | ||
} | ||
requireNonNull(valueType); | ||
requireNonNull(name); | ||
values = List.copyOf(values); | ||
} | ||
|
||
public boolean isNumberValueType() { | ||
return Set.of(Integer.class, Long.class, Double.class).contains(valueType); | ||
} | ||
|
||
public List<Double> toDoubleValues() { | ||
if (!isNumberValueType()) { | ||
throw new IllegalArgumentException("%s is not a number type".formatted(valueType)); | ||
} | ||
if (Integer.class.equals(valueType)) { | ||
return values.stream() | ||
.map(Integer.class::cast) | ||
.map( | ||
integer -> { | ||
if (integer == null) { | ||
return null; | ||
} | ||
return integer.doubleValue(); | ||
}) | ||
.toList(); | ||
} else if (Long.class.equals(valueType)) { | ||
return values.stream() | ||
.map(Long.class::cast) | ||
.map( | ||
aLong -> { | ||
if (aLong == null) { | ||
return null; | ||
} | ||
return aLong.doubleValue(); | ||
}) | ||
.toList(); | ||
} else if (Double.class.equals(valueType)) { | ||
return values.stream().map(Double.class::cast).toList(); | ||
} else { | ||
throw new IllegalArgumentException("Unexpected value type %s".formatted(valueType)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
92 changes: 92 additions & 0 deletions
92
core/src/main/java/com/cosium/hal_mock_mvc/TemplateOptions.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package com.cosium.hal_mock_mvc; | ||
|
||
import static java.util.Objects.requireNonNull; | ||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; | ||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
|
||
import com.cosium.hal_mock_mvc.template.options.InlineElementRepresentation; | ||
import com.cosium.hal_mock_mvc.template.options.OptionsLinkRepresentation; | ||
import com.cosium.hal_mock_mvc.template.options.OptionsRepresentation; | ||
import com.fasterxml.jackson.core.type.TypeReference; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import java.util.List; | ||
import org.springframework.hateoas.Link; | ||
|
||
/** | ||
* @author Réda Housni Alaoui | ||
*/ | ||
class TemplateOptions { | ||
|
||
private final RequestExecutor requestExecutor; | ||
private final ObjectMapper objectMapper; | ||
private final OptionsRepresentation representation; | ||
|
||
TemplateOptions( | ||
RequestExecutor requestExecutor, | ||
ObjectMapper objectMapper, | ||
OptionsRepresentation representation) { | ||
this.requestExecutor = requireNonNull(requestExecutor); | ||
this.objectMapper = requireNonNull(objectMapper); | ||
this.representation = requireNonNull(representation); | ||
} | ||
|
||
public FormProperty<?> assertValid(FormProperty<?> property) throws Exception { | ||
if (!String.class.equals(property.valueType())) { | ||
throw new IllegalArgumentException( | ||
"Value of type '%s' is not valid because the type should be of type 'String' given property '%s' expects a value from an enumeration of options." | ||
.formatted(property.valueType(), property.name())); | ||
} | ||
|
||
long numberOfValues = property.values().size(); | ||
|
||
Long maxItems = representation.maxItems().orElse(null); | ||
if (maxItems != null && numberOfValues > maxItems) { | ||
throw new IllegalArgumentException( | ||
"%s values passed for property '%s' while maxItems == %s" | ||
.formatted(numberOfValues, property.name(), maxItems)); | ||
} | ||
|
||
long minItems = representation.minItems(); | ||
if (numberOfValues < minItems) { | ||
throw new IllegalArgumentException( | ||
"%s values passed for property '%s' while minItems == %s" | ||
.formatted(numberOfValues, property.name(), minItems)); | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
FormProperty<String> stringProperty = (FormProperty<String>) property; | ||
|
||
String valueField = representation.valueField().orElse("value"); | ||
|
||
List<InlineElementRepresentation> inlineElements = representation.inline().orElse(null); | ||
if (inlineElements != null) { | ||
return new TemplateOptionsInlineElements(valueField, inlineElements) | ||
.assertValuesAreValidOptions(stringProperty); | ||
} | ||
|
||
OptionsLinkRepresentation optionsLink = representation.link().orElse(null); | ||
if (optionsLink == null) { | ||
throw new IllegalArgumentException( | ||
"Missing inline and remote elements from options %s.".formatted(representation)); | ||
} | ||
|
||
return new TemplateOptionsInlineElements(valueField, fetchRemoteElements(optionsLink)) | ||
.assertValuesAreValidOptions(stringProperty); | ||
} | ||
|
||
private List<InlineElementRepresentation> fetchRemoteElements( | ||
OptionsLinkRepresentation optionsLink) throws Exception { | ||
|
||
String optionsHref = Link.of(optionsLink.href()).expand().toUri().toString(); | ||
|
||
String rawOptions = | ||
requestExecutor | ||
.execute(get(optionsHref)) | ||
.andExpect(status().is2xxSuccessful()) | ||
.andReturn() | ||
.getResponse() | ||
.getContentAsString(); | ||
|
||
return objectMapper.readValue(rawOptions, new TypeReference<>() {}); | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
core/src/main/java/com/cosium/hal_mock_mvc/TemplateOptionsInlineElement.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package com.cosium.hal_mock_mvc; | ||
|
||
import static java.util.Objects.requireNonNull; | ||
|
||
import com.cosium.hal_mock_mvc.template.options.InlineElementRepresentation; | ||
import com.cosium.hal_mock_mvc.template.options.MapInlineElementRepresentation; | ||
import com.cosium.hal_mock_mvc.template.options.StringInlineElementRepresentation; | ||
|
||
/** | ||
* @author Réda Housni Alaoui | ||
*/ | ||
class TemplateOptionsInlineElement { | ||
|
||
private final String valueField; | ||
private final InlineElementRepresentation representation; | ||
|
||
TemplateOptionsInlineElement(String valueField, InlineElementRepresentation representation) { | ||
this.valueField = requireNonNull(valueField); | ||
this.representation = requireNonNull(representation); | ||
} | ||
|
||
public boolean matches(String value) { | ||
if (representation instanceof MapInlineElementRepresentation mapInlineElementRepresentation) { | ||
|
||
return value.equals(mapInlineElementRepresentation.map().get(valueField)); | ||
|
||
} else if (representation | ||
instanceof StringInlineElementRepresentation stringInlineElementRepresentation) { | ||
|
||
return value.equals(stringInlineElementRepresentation.value()); | ||
|
||
} else { | ||
throw new IllegalArgumentException("Unexpected type %s".formatted(representation.getClass())); | ||
} | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
core/src/main/java/com/cosium/hal_mock_mvc/TemplateOptionsInlineElements.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package com.cosium.hal_mock_mvc; | ||
|
||
import static java.util.Objects.requireNonNull; | ||
|
||
import com.cosium.hal_mock_mvc.template.options.InlineElementRepresentation; | ||
import java.util.List; | ||
|
||
/** | ||
* @author Réda Housni Alaoui | ||
*/ | ||
class TemplateOptionsInlineElements { | ||
private final String valueField; | ||
private final List<InlineElementRepresentation> representations; | ||
|
||
TemplateOptionsInlineElements( | ||
String valueField, List<InlineElementRepresentation> representations) { | ||
this.valueField = requireNonNull(valueField); | ||
this.representations = List.copyOf(representations); | ||
} | ||
|
||
public FormProperty<String> assertValuesAreValidOptions(FormProperty<String> property) { | ||
|
||
if (representations.isEmpty()) { | ||
throw new IllegalArgumentException( | ||
"Value of property '%s' cannot be non null because the list of available option is empty." | ||
.formatted(property.name())); | ||
} | ||
|
||
List<TemplateOptionsInlineElement> inlineElements = | ||
representations.stream() | ||
.map(representation -> new TemplateOptionsInlineElement(valueField, representation)) | ||
.toList(); | ||
|
||
for (String value : property.values()) { | ||
|
||
if (inlineElements.stream().anyMatch(inlineElement -> inlineElement.matches(value))) { | ||
continue; | ||
} | ||
|
||
throw new IllegalArgumentException( | ||
"Value '%s' didn't match any inline option of property '%s' among %s" | ||
.formatted(value, property.name(), representations)); | ||
} | ||
|
||
return property; | ||
} | ||
} |
Oops, something went wrong.