Browse Source

Open parts of AOT api for extensibility.

Make method generation context available for subclassing which allows dedicated tests to be implemented in store modules without having to bootstrap the entire AOT facility. This also demanded to change visibility of fragment metadata.

Along the way fixed json rendering of method metadata by wrapping nested values in json objects. This allows to print out values of eg. list type as json array.

Added test to cover json repository metadata rendering and storage.

See: #3265
pull/3321/head
Christoph Strobl 6 months ago
parent
commit
681d85cd60
No known key found for this signature in database
GPG Key ID: E6054036D0C37A4B
  1. 2
      src/main/java/org/springframework/data/repository/aot/generate/AotQueryMethodGenerationContext.java
  2. 2
      src/main/java/org/springframework/data/repository/aot/generate/AotRepositoryFragmentMetadata.java
  3. 2
      src/main/java/org/springframework/data/repository/aot/generate/AotRepositoryMethod.java
  4. 50
      src/test/java/org/springframework/data/repository/aot/generate/RepositoryContributorUnitTests.java
  5. 2
      src/test/java/org/springframework/data/repository/aot/generate/StubRepositoryInformation.java

2
src/main/java/org/springframework/data/repository/aot/generate/AotQueryMethodGenerationContext.java

@ -50,7 +50,7 @@ public class AotQueryMethodGenerationContext { @@ -50,7 +50,7 @@ public class AotQueryMethodGenerationContext {
private final MethodMetadata targetMethodMetadata;
private final VariableNameFactory variableNameFactory;
AotQueryMethodGenerationContext(RepositoryInformation repositoryInformation, Method method, QueryMethod queryMethod,
protected AotQueryMethodGenerationContext(RepositoryInformation repositoryInformation, Method method, QueryMethod queryMethod,
AotRepositoryFragmentMetadata targetTypeMetadata) {
this.method = method;

2
src/main/java/org/springframework/data/repository/aot/generate/AotRepositoryFragmentMetadata.java

@ -35,7 +35,7 @@ import org.springframework.javapoet.TypeName; @@ -35,7 +35,7 @@ import org.springframework.javapoet.TypeName;
* @author Mark Paluch
* @since 4.0
*/
class AotRepositoryFragmentMetadata {
public class AotRepositoryFragmentMetadata {
private final ClassName className;
private final Map<String, FieldSpec> fields = new HashMap<>(3);

2
src/main/java/org/springframework/data/repository/aot/generate/AotRepositoryMethod.java

@ -54,7 +54,7 @@ record AotRepositoryMethod(String name, String signature, @Nullable QueryMetadat @@ -54,7 +54,7 @@ record AotRepositoryMethod(String name, String signature, @Nullable QueryMetadat
JSONObject query = new JSONObject();
for (Map.Entry<String, Object> entry : queryMetadata.serialize().entrySet()) {
query.put(entry.getKey(), entry.getValue());
query.put(entry.getKey(), JSONObject.wrap(entry.getValue()));
}
return query;

50
src/test/java/org/springframework/data/repository/aot/generate/RepositoryContributorUnitTests.java

@ -24,13 +24,14 @@ import example.UserRepositoryExtension; @@ -24,13 +24,14 @@ import example.UserRepositoryExtension;
import example.UserRepositoryExtensionImpl;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.springframework.aot.test.generate.TestGenerationContext;
import org.springframework.core.test.tools.TestCompiler;
import org.springframework.data.aot.CodeContributionAssert;
@ -95,6 +96,53 @@ class RepositoryContributorUnitTests { @@ -95,6 +96,53 @@ class RepositoryContributorUnitTests {
new CodeContributionAssert(generationContext).contributesReflectionFor(expectedTypeName);
}
@Test // GH-3265
void writesCapturedQueryMetadataToResources() {
DummyModuleAotRepositoryContext aotContext = new DummyModuleAotRepositoryContext(UserRepository.class, null);
RepositoryContributor repositoryContributor = new RepositoryContributor(aotContext) {
@Override
protected @Nullable MethodContributor<? extends QueryMethod> contributeQueryMethod(Method method) {
return MethodContributor
.forQueryMethod(
new QueryMethod(method, getRepositoryInformation(), getProjectionFactory(), DefaultParameters::new))
.withMetadata(new QueryMetadata() {
@Override
public Map<String, Object> serialize() {
return Map.of("filter", "FILTER(%s > $1)".formatted(method.getName()), "project",
Arrays.stream(method.getParameters()).map(Parameter::getName).toList());
}
}).contribute(context -> {
CodeBlock.Builder builder = CodeBlock.builder();
if (!ClassUtils.isVoidType(method.getReturnType())) {
builder.addStatement("return null");
}
return builder.build();
});
}
};
TestGenerationContext generationContext = new TestGenerationContext(UserRepository.class);
repositoryContributor.contribute(generationContext);
generationContext.writeGeneratedContent();
String expectedTypeName = "example.UserRepositoryImpl__Aot";
TestCompiler.forSystem().with(generationContext).compile(compiled -> {
String content = compiled.getResourceFile().getContent();
assertThat(content).containsIgnoringWhitespaces("\"filter\": \"FILTER(doSomething > $1)\"")
.containsIgnoringWhitespaces("\"project\": [\n\"firstname\"\n]");
});
new CodeContributionAssert(generationContext).contributesReflectionFor(expectedTypeName);
}
@Test // GH-3279
void callsMethodContributionForQueryMethod() {

2
src/test/java/org/springframework/data/repository/aot/generate/StubRepositoryInformation.java

@ -108,7 +108,7 @@ class StubRepositoryInformation implements RepositoryInformation { @@ -108,7 +108,7 @@ class StubRepositoryInformation implements RepositoryInformation {
@Override
public boolean isQueryMethod(Method method) {
return false;
return !isBaseClassMethod(method);
}
@Override

Loading…
Cancel
Save