-
Notifications
You must be signed in to change notification settings - Fork 559
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support to receive aggregate references as request parameters.
We now support using AggregateReference as type to bind request parameters taking URIs pointing to related aggregates. The default resolution will try to resolve the entire URI via UriToEntityConverter but one can also provide a function that can extract any part of the URI to be then resolved into either an identifier, aggregate instance or jMolecules Association against the ConversionService. Fixes #2239.
- Loading branch information
Showing
21 changed files
with
1,025 additions
and
129 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
100 changes: 100 additions & 0 deletions
100
...g-data-rest-core/src/main/java/org/springframework/data/rest/core/AggregateReference.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,100 @@ | ||
/* | ||
* Copyright 2023 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.springframework.data.rest.core; | ||
|
||
import java.net.URI; | ||
import java.util.function.Function; | ||
|
||
import org.springframework.lang.Nullable; | ||
import org.springframework.web.util.UriComponents; | ||
|
||
/** | ||
* Represents a reference to an aggregate backed by a URI. It can be resolved into an aggregate identifier or the | ||
* aggregate instance itself. | ||
* | ||
* @author Oliver Drotbohm | ||
* @since 4.1 | ||
*/ | ||
public interface AggregateReference<T, ID> { | ||
|
||
/** | ||
* Returns the source {@link URI}. | ||
* | ||
* @return will never be {@literal null}. | ||
*/ | ||
URI getUri(); | ||
|
||
/** | ||
* Creates a new {@link AggregateReference} resolving the identifier source value from the given | ||
* {@link UriComponents}. | ||
* | ||
* @param extractor must not be {@literal null}. | ||
* @return will never be {@literal null}. | ||
*/ | ||
AggregateReference<T, ID> withIdSource(Function<UriComponents, Object> extractor); | ||
|
||
/** | ||
* Resolves the underlying URI into a full aggregate, potentially applying the configured identifier extractor. | ||
* | ||
* @return can be {@literal null}. | ||
* @see #withIdSource(Function) | ||
*/ | ||
@Nullable | ||
T resolveAggregate(); | ||
|
||
/** | ||
* Resolves the underlying URI into an aggregate identifier, potentially applying the registered identifier extractor. | ||
* | ||
* @return can be {@literal null}. | ||
* @see #withIdSource(Function) | ||
*/ | ||
@Nullable | ||
ID resolveId(); | ||
|
||
/** | ||
* Resolves the underlying URI into a full aggregate, potentially applying the configured identifier extractor. | ||
* | ||
* @return will never be {@literal null}. | ||
* @throws IllegalStateException in case the value resolved is {@literal null}. | ||
*/ | ||
default T resolveRequiredAggregate() { | ||
|
||
T result = resolveAggregate(); | ||
|
||
if (result == null) { | ||
throw new IllegalStateException("Resolving the aggregate resulted in null"); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
/** | ||
* Resolves the underlying URI into an aggregate identifier, potentially applying the registered identifier extractor. | ||
* | ||
* @return will never be {@literal null}. | ||
* @throws IllegalStateException in case the value resolved is {@literal null}. | ||
*/ | ||
default ID resolveRequiredId() { | ||
|
||
ID result = resolveId(); | ||
|
||
if (result == null) { | ||
throw new IllegalStateException("Resolving the aggregate identifier resulted in null"); | ||
} | ||
|
||
return result; | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
...-core/src/main/java/org/springframework/data/rest/core/AssociationAggregateReference.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,59 @@ | ||
/* | ||
* Copyright 2023 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.springframework.data.rest.core; | ||
|
||
import java.util.function.Function; | ||
|
||
import org.jmolecules.ddd.types.AggregateRoot; | ||
import org.jmolecules.ddd.types.Association; | ||
import org.jmolecules.ddd.types.Identifier; | ||
import org.springframework.lang.Nullable; | ||
import org.springframework.web.util.UriComponents; | ||
|
||
/** | ||
* An {@link AggregateReference} that can also resolve into jMolecules {@link Association} instances. | ||
* | ||
* @author Oliver Drotbohm | ||
* @since 4.1 | ||
*/ | ||
public interface AssociationAggregateReference<T extends AggregateRoot<T, ID>, ID extends Identifier> | ||
extends AggregateReference<T, ID> { | ||
|
||
/** | ||
* Resolves the underlying URI into an {@link Association}, potentially applying the configured identifier extractor. | ||
* | ||
* @return can be {@literal null}. | ||
* @see #withIdSource(Function) | ||
*/ | ||
@Nullable | ||
default Association<T, ID> resolveAssociation() { | ||
return Association.forId(resolveId()); | ||
} | ||
|
||
/** | ||
* Resolves the underlying URI into an {@link Association}, potentially applying the configured identifier extractor. | ||
* | ||
* @return will never be {@literal null}. | ||
* @throws IllegalStateException in case the value resolved is {@literal null}. | ||
*/ | ||
@SuppressWarnings("null") | ||
default Association<T, ID> resolveRequiredAssociation() { | ||
return Association.forId(resolveRequiredId()); | ||
} | ||
|
||
@Override | ||
AssociationAggregateReference<T, ID> withIdSource(Function<UriComponents, Object> extractor); | ||
} |
114 changes: 114 additions & 0 deletions
114
...st-core/src/main/java/org/springframework/data/rest/core/ResolvingAggregateReference.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,114 @@ | ||
/* | ||
* Copyright 2023 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.springframework.data.rest.core; | ||
|
||
import java.net.URI; | ||
import java.util.function.Function; | ||
|
||
import org.springframework.lang.Nullable; | ||
import org.springframework.util.Assert; | ||
import org.springframework.web.util.UriComponents; | ||
import org.springframework.web.util.UriComponentsBuilder; | ||
|
||
/** | ||
* An {@link AggregateReference} implementation that resolves the source URI given a {@link Function} or into a fixed | ||
* value. | ||
* | ||
* @author Oliver Drotbohm | ||
* @since 4.1 | ||
*/ | ||
public class ResolvingAggregateReference<T, ID> implements AggregateReference<T, ID> { | ||
|
||
private static final Function<URI, UriComponents> STARTER = it -> UriComponentsBuilder.fromUri(it).build(); | ||
|
||
private final URI source; | ||
private final Function<URI, ? extends Object> extractor; | ||
private final Function<Object, ? extends T> aggregateResolver; | ||
private final Function<Object, ? extends ID> identifierResolver; | ||
|
||
/** | ||
* Creates a new {@link ResolvingAggregateReference} for the given {@link URI} to eventually resolve the final value | ||
* against the given resolver function. | ||
* | ||
* @param source must not be {@literal null}. | ||
* @param resolver must not be {@literal null}. | ||
*/ | ||
public ResolvingAggregateReference(URI source, Function<Object, ? extends T> aggregateResolver, | ||
Function<Object, ? extends ID> identifierResolver) { | ||
|
||
this(source, aggregateResolver, identifierResolver, it -> it); | ||
} | ||
|
||
protected ResolvingAggregateReference(URI source, Function<Object, ? extends T> aggregateResolver, | ||
Function<Object, ? extends ID> identifierResolver, Function<URI, ? extends Object> extractor) { | ||
|
||
Assert.notNull(source, "Source URI must not be null!"); | ||
Assert.notNull(aggregateResolver, "Aggregate resolver must not be null!"); | ||
Assert.notNull(identifierResolver, "Identifier resolver must not be null!"); | ||
|
||
this.source = source; | ||
this.aggregateResolver = aggregateResolver; | ||
this.identifierResolver = identifierResolver; | ||
this.extractor = extractor; | ||
} | ||
|
||
/** | ||
* Creates a new {@link ResolvingAggregateReference} for the given {@link URI} resolving in the given fixed value. | ||
* Primarily for testing purposes. | ||
* | ||
* @param source must not be {@literal null}. | ||
* @param value can be {@literal null}. | ||
*/ | ||
public ResolvingAggregateReference(URI source, @Nullable T value, ID identifier) { | ||
this(source, __ -> value, __ -> identifier, it -> it); | ||
} | ||
|
||
/* | ||
* (non-Javadoc) | ||
* @see org.springframework.data.rest.core.Foo#getURI() | ||
*/ | ||
@Override | ||
public URI getUri() { | ||
return source; | ||
} | ||
|
||
/* | ||
* (non-Javadoc) | ||
* @see org.springframework.data.rest.core.AggregateReference#resolveId() | ||
*/ | ||
@Override | ||
public ID resolveId() { | ||
return extractor.andThen(identifierResolver).apply(source); | ||
} | ||
|
||
/* | ||
* (non-Javadoc) | ||
* @see org.springframework.data.rest.core.AggregateReference#resolveAggregate() | ||
*/ | ||
@Override | ||
public T resolveAggregate() { | ||
return extractor.andThen(aggregateResolver).apply(source); | ||
} | ||
|
||
/* | ||
* (non-Javadoc) | ||
* @see org.springframework.data.rest.core.AggregateReference#withExtractor(java.util.function.Function) | ||
*/ | ||
@Override | ||
public AggregateReference<T, ID> withIdSource(Function<UriComponents, Object> extractor) { | ||
return new ResolvingAggregateReference<>(source, aggregateResolver, identifierResolver, STARTER.andThen(extractor)); | ||
} | ||
} |
Oops, something went wrong.