Skip to content

CassandraModule should not have template component for ResultSetMapper<T> #229

Merged
merged 3 commits into from
Jul 31, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,23 @@ private KoraExtensionDependencyGenerator generateResultSetMapper(RoundEnvironmen
if (!(listType instanceof DeclaredType dt)) {
return null;
}
var listTypeName = (ParameterizedTypeName) TypeName.get(listType);
if (listTypeName.rawType.canonicalName().equals("java.util.List")) {
if (CommonUtils.isList(listType)) {
var tn = (ParameterizedTypeName) TypeName.get(listType);
var rowType = dt.getTypeArguments().get(0);
return this.listResultSetMapper(typeMirror, listTypeName, (DeclaredType) rowType);
} else {
return null;
return this.listResultSetMapper(typeMirror, tn, (DeclaredType) rowType);
}
return () -> {
var singleResultSetMapper = this.elements.getTypeElement(CassandraTypes.RESULT_SET_MAPPER.canonicalName()).getEnclosedElements()
.stream()
.filter(e -> e.getKind() == ElementKind.METHOD && e.getModifiers().contains(Modifier.STATIC))
.map(ExecutableElement.class::cast)
.filter(m -> m.getSimpleName().contentEquals("singleResultSetMapper"))
.findFirst()
.orElseThrow();
var tp = (TypeVariable) singleResultSetMapper.getTypeParameters().get(0).asType();
var executableType = (ExecutableType) GenericTypeResolver.resolve(this.types, Map.of(tp, listType), singleResultSetMapper.asType());
return ExtensionResult.fromExecutable(singleResultSetMapper, executableType);
};
}

private KoraExtensionDependencyGenerator listResultSetMapper(DeclaredType typeMirror, ParameterizedTypeName listType, DeclaredType rowTypeMirror) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ru.tinkoff.kora.database.annotation.processor.cassandra.extension;

import ru.tinkoff.kora.database.annotation.processor.cassandra.CassandraTypes;
import ru.tinkoff.kora.kora.app.annotation.processor.extension.ExtensionFactory;
import ru.tinkoff.kora.kora.app.annotation.processor.extension.KoraExtension;

Expand All @@ -9,7 +10,7 @@
public class CassandraTypesExtensionFactory implements ExtensionFactory {
@Override
public Optional<KoraExtension> create(ProcessingEnvironment processingEnvironment) {
var type = processingEnvironment.getElementUtils().getTypeElement("ru.tinkoff.kora.database.jdbc.mapper.result.JdbcRowMapper");
var type = processingEnvironment.getElementUtils().getTypeElement(CassandraTypes.ROW_MAPPER.canonicalName());
if (type == null) {
return Optional.empty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Row;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.mockito.Mockito;
import ru.tinkoff.kora.annotation.processor.common.AbstractAnnotationProcessorTest;
import ru.tinkoff.kora.annotation.processor.common.TestUtils;
import ru.tinkoff.kora.application.graph.TypeRef;
import ru.tinkoff.kora.database.cassandra.mapper.result.CassandraResultSetMapper;
Expand All @@ -14,74 +14,103 @@
import ru.tinkoff.kora.database.common.annotation.processor.cassandra.CassandraEntity.AllNativeTypesEntity;
import ru.tinkoff.kora.database.common.annotation.processor.entity.TestEntityJavaBean;
import ru.tinkoff.kora.database.common.annotation.processor.entity.TestEntityRecord;
import ru.tinkoff.kora.kora.app.annotation.processor.KoraAppProcessor;

import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.function.Predicate;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class CassandraExtensionTest {
public CassandraExtensionTest() throws Exception {}
import static org.mockito.Mockito.*;

public class CassandraExtensionTest extends AbstractAnnotationProcessorTest {
@Test
@SuppressWarnings("unchecked")
void testEntityRecordListResultSetMapper() throws Exception {
var cl = TestUtils.testKoraExtension(new TypeRef<?>[]{
TypeRef.of(CassandraResultSetMapper.class, TypeRef.of(List.class, TestEntityJavaBean.class)),
TypeRef.of(CassandraRowMapper.class, TestEntityJavaBean.class),
TypeRef.of(CassandraResultSetMapper.class, TypeRef.of(List.class, AllNativeTypesEntity.class)),
TypeRef.of(CassandraRowMapper.class, AllNativeTypesEntity.class),
TypeRef.of(CassandraResultSetMapper.class, TypeRef.of(List.class, String.class)),
},
TypeRef.of(CassandraRowColumnMapper.class, TestEntityRecord.UnknownTypeField.class),
TypeRef.of(CassandraEntity.TestEntityFieldCassandraResultColumnMapperNonFinal.class),
TypeRef.of(CassandraRowMapper.class, String.class)
);
}

private final ClassLoader cl = TestUtils.testKoraExtension(new TypeRef<?>[]{
TypeRef.of(CassandraResultSetMapper.class, TypeRef.of(List.class, TestEntityRecord.class)),
TypeRef.of(CassandraRowMapper.class, TestEntityRecord.class),
TypeRef.of(CassandraResultSetMapper.class, TypeRef.of(List.class, TestEntityJavaBean.class)),
TypeRef.of(CassandraRowMapper.class, TestEntityJavaBean.class),
TypeRef.of(CassandraResultSetMapper.class, TypeRef.of(List.class, AllNativeTypesEntity.class)),
TypeRef.of(CassandraRowMapper.class, AllNativeTypesEntity.class),
TypeRef.of(CassandraResultSetMapper.class, TypeRef.of(List.class, String.class)),
},
TypeRef.of(CassandraRowColumnMapper.class, TestEntityRecord.UnknownTypeField.class),
TypeRef.of(CassandraEntity.TestEntityFieldCassandraResultColumnMapperNonFinal.class),
TypeRef.of(CassandraRowMapper.class, String.class)
);
@Test
void testRowMapper() {
compile(List.of(new KoraAppProcessor()), """
@ru.tinkoff.kora.common.KoraApp
public interface TestApp extends ru.tinkoff.kora.database.cassandra.CassandraModule{
@Root
default String root(ru.tinkoff.kora.database.cassandra.mapper.result.CassandraRowMapper<TestRecord> r) {return "";}
}
""", """
public record TestRecord(int value) {}
""");

compileResult.assertSuccess();
assertThat(compileResult.loadClass("$TestRecord_CassandraRowMapper"))
.isNotNull()
.isFinal()
.matches(doesImplement(CassandraRowMapper.class));
}

@Test
@SuppressWarnings("unchecked")
void testEntityRecordListResultSetMapper() throws Exception {
var type = cl.loadClass("ru.tinkoff.kora.database.common.annotation.processor.entity.$TestEntityRecord_ListCassandraResultSetMapper");
assertThat(type)
public void testListResultSetMapper() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
compile(List.of(new KoraAppProcessor()), """
@ru.tinkoff.kora.common.KoraApp
public interface TestApp extends ru.tinkoff.kora.database.cassandra.CassandraModule {

@Root
default String root(ru.tinkoff.kora.database.cassandra.mapper.result.CassandraResultSetMapper<java.util.List<TestRecord>> r) {return "";}
}
""", """
public record TestRecord(int value) {}
""");

compileResult.assertSuccess();
var listMapper = compileResult.loadClass("$TestRecord_ListCassandraResultSetMapper");
assertThat(listMapper)
.isNotNull()
.isFinal()
.matches(doesImplement(CassandraResultSetMapper.class));

var columnDefinition = Mockito.mock(ColumnDefinitions.class);
var rs = Mockito.mock(ResultSet.class);
var unknownFieldMapper = (CassandraRowColumnMapper<TestEntityRecord.UnknownTypeField>) Mockito.mock(CassandraRowColumnMapper.class);
var mappedField2Mapper = Mockito.mock(CassandraEntity.TestEntityFieldCassandraResultColumnMapperNonFinal.class);
var mapper = (CassandraResultSetMapper<List<TestEntityRecord>>) type.getConstructor(CassandraRowColumnMapper.class, CassandraEntity.TestEntityFieldCassandraResultColumnMapperNonFinal.class)
.newInstance(unknownFieldMapper, mappedField2Mapper);
@SuppressWarnings("unchecked")
var mapper = (CassandraResultSetMapper<List<?>>) listMapper.getConstructor().newInstance();

when(rs.getColumnDefinitions()).thenReturn(columnDefinition);
when(columnDefinition.firstIndexOf("field1")).thenReturn(0);
when(columnDefinition.firstIndexOf("field2")).thenReturn(1);
when(columnDefinition.firstIndexOf("field3")).thenReturn(2);
when(columnDefinition.firstIndexOf("unknown_type_field")).thenReturn(3);
when(columnDefinition.firstIndexOf("mapped_field1")).thenReturn(4);
when(columnDefinition.firstIndexOf("mapped_field2")).thenReturn(5);
when(columnDefinition.firstIndexOf("value")).thenReturn(0);

var row = Mockito.mock(Row.class);

when(rs.iterator()).thenReturn(List.of(row).iterator());
when(rs.iterator()).thenReturn(List.of(row, row).iterator());
var result = mapper.apply(rs);
assertThat(result).hasSize(1);
assertThat(result).hasSize(2);

verify(row).getString(0);
verify(row).getInt(1);
verify(row).getInt(2);
verify(unknownFieldMapper).apply(row, 3);
verify(mappedField2Mapper).apply(row, 5);
when(columnDefinition.firstIndexOf("value")).thenReturn(0);
verify(row, times(2)).getInt(0);
}

@Test
void testEntityRecordRowMapper() throws Exception {
assertThat(cl.loadClass("ru.tinkoff.kora.database.common.annotation.processor.entity.$TestEntityRecord_CassandraRowMapper"))
public void testSingleResultSetMapper() {
compile(List.of(new KoraAppProcessor()), """
@ru.tinkoff.kora.common.KoraApp
public interface TestApp extends ru.tinkoff.kora.database.cassandra.CassandraModule {
@Root
default String root(ru.tinkoff.kora.database.cassandra.mapper.result.CassandraResultSetMapper<TestRecord> r) {return "";}
}
""", """
public record TestRecord(int value) {}
""");

compileResult.assertSuccess();
assertThat(compileResult.loadClass("$TestRecord_CassandraRowMapper"))
.isNotNull()
.isFinal()
.matches(doesImplement(CassandraRowMapper.class));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@

public interface CassandraModule extends DataBaseModule {

default <T> CassandraResultSetMapper<T> cassandraSingleResultSetMapper(CassandraRowMapper<T> rowMapper) {
return CassandraResultSetMapper.singleResultSetMapper(rowMapper);
}

default <T> CassandraResultSetMapper<Optional<T>> cassandraOptionalResultSetMapper(CassandraRowMapper<T> rowMapper) {
return CassandraResultSetMapper.optionalResultSetMapper(rowMapper);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package ru.tinkoff.kora.database.r2dbc;

import io.r2dbc.spi.Row;
import io.r2dbc.spi.Statement;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import ru.tinkoff.kora.common.DefaultComponent;
Expand All @@ -11,7 +9,6 @@
import ru.tinkoff.kora.database.r2dbc.mapper.result.R2dbcResultFluxMapper;
import ru.tinkoff.kora.database.r2dbc.mapper.result.R2dbcRowMapper;

import javax.annotation.Nullable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,17 @@ class CassandraTypesExtension(val resolver: Resolver, val kspLogger: KSPLogger,
private fun generateResultSetMapper(resolver: Resolver, rowSetKSType: KSType): (() -> ExtensionResult)? {
val rowSetParam = rowSetKSType.arguments[0].type!!.resolve()
if (!rowSetParam.isList()) {
return null
val resultSetMapperDecl = resolver.getClassDeclarationByName(CassandraTypes.resultSetMapper.canonicalName)!!
val rowMapperDecl = resolver.getClassDeclarationByName(CassandraTypes.rowMapper.canonicalName)!!

val resultSetMapperType = resultSetMapperDecl.asType(listOf(resolver.getTypeArgument(resolver.createKSTypeReferenceFromKSType(rowSetParam), Variance.INVARIANT)))
val rowMapperType = rowMapperDecl.asType(listOf(resolver.getTypeArgument(resolver.createKSTypeReferenceFromKSType(rowSetParam), Variance.INVARIANT)))

val functionDecl = resolver.getFunctionDeclarationsByName(CassandraTypes.resultSetMapper.canonicalName + ".singleResultSetMapper").first()
val functionType = functionDecl.parametrized(resultSetMapperType, listOf(rowMapperType))
return {
ExtensionResult.fromExecutable(functionDecl, functionType)
}
}
val rowType = rowSetParam.arguments[0]
val rowResolvedType = rowType.type!!.resolve()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import ru.tinkoff.kora.database.cassandra.mapper.result.CassandraRowColumnMapper
import ru.tinkoff.kora.database.cassandra.mapper.result.CassandraRowMapper
import ru.tinkoff.kora.database.symbol.processor.entity.EntityWithEmbedded
import ru.tinkoff.kora.database.symbol.processor.entity.TestEntity
import ru.tinkoff.kora.kora.app.ksp.KoraAppProcessorProvider
import ru.tinkoff.kora.ksp.common.AbstractSymbolProcessorTest
import ru.tinkoff.kora.ksp.common.TestUtils
import kotlin.reflect.typeOf

class CassandraExtensionTest {
class CassandraExtensionTest : AbstractSymbolProcessorTest() {
@Test
fun testEntity() {
TestUtils.testKoraExtension(
Expand All @@ -27,4 +29,46 @@ class CassandraExtensionTest {
typeOf<CassandraRowMapper<String>>(),
)
}

@Test
fun testRowMapper() {
compile(listOf(KoraAppProcessorProvider()), """
@ru.tinkoff.kora.common.KoraApp
interface TestApp : ru.tinkoff.kora.database.cassandra.CassandraModule {
@ru.tinkoff.kora.common.annotation.Root
fun root(m: ru.tinkoff.kora.database.cassandra.mapper.result.CassandraRowMapper<TestEntity>) = ""
}
""".trimIndent(), """
data class TestEntity(val value: String)
""".trimIndent())
compileResult.assertSuccess()
}

@Test
fun testListResultSetMapper() {
compile(listOf(KoraAppProcessorProvider()), """
@ru.tinkoff.kora.common.KoraApp
interface TestApp : ru.tinkoff.kora.database.cassandra.CassandraModule {
@ru.tinkoff.kora.common.annotation.Root
fun root(m: ru.tinkoff.kora.database.cassandra.mapper.result.CassandraResultSetMapper<List<TestEntity>>) = ""
}
""".trimIndent(), """
data class TestEntity(val value: String)
""".trimIndent())
compileResult.assertSuccess()
}

@Test
fun testSingleResultSetMapper() {
compile(listOf(KoraAppProcessorProvider()), """
@ru.tinkoff.kora.common.KoraApp
interface TestApp : ru.tinkoff.kora.database.cassandra.CassandraModule {
@ru.tinkoff.kora.common.annotation.Root
fun root(m: ru.tinkoff.kora.database.cassandra.mapper.result.CassandraResultSetMapper<TestEntity>) = ""
}
""".trimIndent(), """
data class TestEntity(val value: String)
""".trimIndent())
compileResult.assertSuccess()
}
}