Skip to content

Commit

Permalink
Repository params with declared mapping should be parsed as simple (#157
Browse files Browse the repository at this point in the history
)

see #155
  • Loading branch information
Squiry authored Jun 16, 2023
1 parent 12d5fa7 commit ef270f9
Show file tree
Hide file tree
Showing 19 changed files with 216 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public TypeSpec generate(TypeElement repositoryElement, TypeSpec.Builder type, M
var parameterMappers = new FieldFactory(this.types, type, constructor, "_parameter_mapper_");
for (var method : queryMethods) {
var methodType = (ExecutableType) this.types.asMemberOf(repositoryType, method);
var parameters = QueryParameterParser.parse(this.types, CassandraTypes.CONNECTION, method, methodType);
var parameters = QueryParameterParser.parse(this.types, CassandraTypes.CONNECTION, CassandraTypes.PARAMETER_COLUMN_MAPPER, method, methodType);
var queryAnnotation = CommonUtils.findDirectAnnotation(method, DbUtils.QUERY_ANNOTATION);
var queryString = CommonUtils.parseAnnotationValueWithoutDefault(queryAnnotation, "value").toString();
var query = QueryWithParameters.parse(filer, queryString, parameters);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.squareup.javapoet.*;
import reactor.core.publisher.Mono;
import ru.tinkoff.kora.annotation.processor.common.CommonUtils;
import ru.tinkoff.kora.annotation.processor.common.MethodUtils;
import ru.tinkoff.kora.annotation.processor.common.FieldFactory;
import ru.tinkoff.kora.annotation.processor.common.Visitors;
import ru.tinkoff.kora.common.Tag;
Expand Down Expand Up @@ -62,7 +61,7 @@ public TypeSpec generate(TypeElement repositoryElement, TypeSpec.Builder type, M
var parameterMappers = new FieldFactory(this.types, type, constructor, "_parameter_mapper_");
for (var method : queryMethods) {
var methodType = (ExecutableType) this.types.asMemberOf(repositoryType, method);
var parameters = QueryParameterParser.parse(this.types, JdbcTypes.CONNECTION, method, methodType);
var parameters = QueryParameterParser.parse(this.types, JdbcTypes.CONNECTION, JdbcTypes.PARAMETER_COLUMN_MAPPER, method, methodType);
var queryAnnotation = CommonUtils.findDirectAnnotation(method, DbUtils.QUERY_ANNOTATION);
var queryString = CommonUtils.parseAnnotationValueWithoutDefault(queryAnnotation, "value").toString();
var query = QueryWithParameters.parse(filer, queryString, parameters);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,45 +19,49 @@

public final class QueryParameterParser {

public static List<QueryParameter> parse(Types types, ClassName connectionType, ExecutableElement method, ExecutableType methodType) {
return parse(types, List.of(connectionType), method, methodType);
public static List<QueryParameter> parse(Types types, ClassName connectionType, ClassName parameterMapper, ExecutableElement method, ExecutableType methodType) {
return parse(types, List.of(connectionType), parameterMapper, method, methodType);
}

public static List<QueryParameter> parse(Types types, List<ClassName> connectionTypes, ExecutableElement method, ExecutableType methodType) {
public static List<QueryParameter> parse(Types types, List<ClassName> connectionTypes, ClassName parameterMapper, ExecutableElement method, ExecutableType methodType) {
var result = new ArrayList<QueryParameter>(method.getParameters().size());
for (int i = 0; i < method.getParameters().size(); i++) {
var parameter = QueryParameterParser.parse(types, method.getParameters().get(i), methodType.getParameterTypes().get(i), connectionTypes);
var parameter = QueryParameterParser.parse(types, method.getParameters().get(i), methodType.getParameterTypes().get(i), connectionTypes, parameterMapper);
result.add(parameter);
}
return result;
}

public static QueryParameter parse(Types types, VariableElement parameter, TypeMirror type, ClassName connectionType) {
return parse(types, parameter, type, List.of(connectionType));
}

public static QueryParameter parse(Types types, VariableElement parameter, TypeMirror type, List<ClassName> connectionTypes) {
public static QueryParameter parse(Types types, VariableElement parameter, TypeMirror type, List<ClassName> connectionTypes, ClassName parameterMapper) {
var name = parameter.getSimpleName().toString();
var typeName = TypeName.get(type);
for (var connectionType : connectionTypes) {
if (connectionType.equals(typeName)) {
return new QueryParameter.ConnectionParameter(name, type, parameter);
}
}
var mapping = CommonUtils.parseMapping(parameter).getMapping(parameterMapper);
var batch = CommonUtils.findDirectAnnotation(parameter, DbUtils.BATCH_ANNOTATION);
if (batch != null) {
if (!(typeName instanceof ParameterizedTypeName ptn && (ptn.rawType.canonicalName().equals("java.util.List")))) {
throw new ProcessingErrorException("@Batch parameter must be a list", parameter);
}
var batchType = ((DeclaredType) type).getTypeArguments().get(0);
var entity = DbEntity.parseEntity(types, batchType);
if (entity != null) {
var param = new QueryParameter.EntityParameter(name, entity, parameter);
return new QueryParameter.BatchParameter(name, type, parameter, param);
final QueryParameter param;
if (mapping != null) {
param = new QueryParameter.SimpleParameter(name, batchType, parameter);
} else {
var param = new QueryParameter.SimpleParameter(name, batchType, parameter);
return new QueryParameter.BatchParameter(name, type, parameter, param);
var entity = DbEntity.parseEntity(types, batchType);
if (entity != null) {
param = new QueryParameter.EntityParameter(name, entity, parameter);
} else {
param = new QueryParameter.SimpleParameter(name, batchType, parameter);
}
}
return new QueryParameter.BatchParameter(name, type, parameter, param);
}
if (mapping != null) {
return new QueryParameter.SimpleParameter(name, type, parameter);
}
var entity = DbEntity.parseEntity(types, type);
if (entity != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import ru.tinkoff.kora.annotation.processor.common.*;
import ru.tinkoff.kora.annotation.processor.common.MethodUtils;
import ru.tinkoff.kora.common.Context;
import ru.tinkoff.kora.common.Context;
import ru.tinkoff.kora.common.Tag;
import ru.tinkoff.kora.database.annotation.processor.DbUtils;
Expand Down Expand Up @@ -65,7 +63,7 @@ public TypeSpec generate(TypeElement repositoryElement, TypeSpec.Builder type, M

for (var method : queryMethods) {
var methodType = (ExecutableType) this.types.asMemberOf(repositoryType, method);
var parameters = QueryParameterParser.parse(this.types, R2dbcTypes.CONNECTION, method, methodType);
var parameters = QueryParameterParser.parse(this.types, R2dbcTypes.CONNECTION, R2dbcTypes.PARAMETER_COLUMN_MAPPER, method, methodType);
var queryAnnotation = CommonUtils.findDirectAnnotation(method, DbUtils.QUERY_ANNOTATION);
var queryString = CommonUtils.parseAnnotationValueWithoutDefault(queryAnnotation, "value").toString();
var query = QueryWithParameters.parse(filer, queryString, parameters);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public TypeSpec generate(TypeElement repositoryElement, TypeSpec.Builder type, M
var parameterMappers = new FieldFactory(this.types, type, constructor, "_parameter_mapper_");
for (var method : queryMethods) {
var methodType = (ExecutableType) this.types.asMemberOf(repositoryType, method);
var parameters = QueryParameterParser.parse(this.types, List.of(VertxTypes.CONNECTION, VertxTypes.SQL_CLIENT), method, methodType);
var parameters = QueryParameterParser.parse(this.types, List.of(VertxTypes.CONNECTION, VertxTypes.SQL_CLIENT), VertxTypes.PARAMETER_COLUMN_MAPPER, method, methodType);
var queryAnnotation = CommonUtils.findDirectAnnotation(method, DbUtils.QUERY_ANNOTATION);
var queryString = CommonUtils.parseAnnotationValueWithoutDefault(queryAnnotation, "value").toString();
var query = QueryWithParameters.parse(filer, queryString, parameters);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import ru.tinkoff.kora.database.common.annotation.processor.entity.TestEntityRecord.TestUnknownType;
import ru.tinkoff.kora.database.common.annotation.processor.entity.TestEntityRecord.UnknownTypeField;

import java.sql.SQLException;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -426,4 +427,24 @@ public interface TestRepository extends CassandraRepository {
assertThat(tag).isNotNull();
assertThat(tag.value()).isEqualTo(new Class<?>[]{compileResult.loadClass("TestRepository")});
}

@Test
public void testRecordParameterMapping() throws ClassNotFoundException, SQLException {
var mapper = Mockito.mock(CassandraParameterColumnMapper.class);
var repository = compileCassandra(List.of(mapper), """
@Repository
public interface TestRepository extends CassandraRepository {
@Query("INSERT INTO test(value) VALUES (:value::jsonb)")
void test(@Tag(TestRepository.class) TestRecord value);
}
""", """
public record TestRecord(String value){}
""");

var mapperConstructorParameter = repository.repositoryClass.getConstructors()[0].getParameters()[1];
assertThat(mapperConstructorParameter.getType()).isEqualTo(CassandraParameterColumnMapper.class);
var tag = mapperConstructorParameter.getAnnotation(Tag.class);
assertThat(tag).isNotNull();
assertThat(tag.value()).isEqualTo(new Class<?>[]{compileResult.loadClass("TestRepository")});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -365,4 +365,24 @@ public interface TestRepository extends JdbcRepository {
assertThat(tag.value()).isEqualTo(new Class<?>[]{compileResult.loadClass("TestRepository")});
}

@Test
public void testRecordParameterMapping() throws ClassNotFoundException, SQLException {
var mapper = Mockito.mock(JdbcParameterColumnMapper.class);
var repository = compileJdbc(List.of(mapper), """
@Repository
public interface TestRepository extends JdbcRepository {
@Query("INSERT INTO test(value) VALUES (:value::jsonb)")
void test(@Tag(TestRepository.class) TestRecord value);
}
""", """
public record TestRecord(String value){}
""");

var mapperConstructorParameter = repository.repositoryClass.getConstructors()[0].getParameters()[1];
assertThat(mapperConstructorParameter.getType()).isEqualTo(JdbcParameterColumnMapper.class);
var tag = mapperConstructorParameter.getAnnotation(Tag.class);
assertThat(tag).isNotNull();
assertThat(tag.value()).isEqualTo(new Class<?>[]{compileResult.loadClass("TestRepository")});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -272,5 +272,25 @@ public interface TestRepository extends R2dbcRepository {
assertThat(tag.value()).isEqualTo(new Class<?>[]{compileResult.loadClass("TestRepository")});
}

@Test
public void testRecordParameterMappingByTag() throws ClassNotFoundException {
var mapper = Mockito.mock(R2dbcParameterColumnMapper.class);
var repository = compileR2dbc(List.of(mapper), """
@Repository
public interface TestRepository extends R2dbcRepository {
@Query("INSERT INTO test(value) VALUES (:value)")
void test(@Tag(TestRepository.class) TestRecord value);
}
""", """
public record TestRecord(String value){}
""");

var mapperConstructorParameter = repository.repositoryClass.getConstructors()[0].getParameters()[1];
assertThat(mapperConstructorParameter.getType()).isEqualTo(R2dbcParameterColumnMapper.class);
var tag = mapperConstructorParameter.getAnnotation(Tag.class);
assertThat(tag).isNotNull();
assertThat(tag.value()).isEqualTo(new Class<?>[]{compileResult.loadClass("TestRepository")});
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -260,4 +260,24 @@ public interface TestRepository extends VertxRepository {
assertThat(tag.value()).isEqualTo(new Class<?>[]{compileResult.loadClass("TestRepository")});
}

@Test
public void testRecordParameterMappingByTag() {
var mapper = Mockito.mock(VertxParameterColumnMapper.class);
var repository = compileVertx(List.of(mapper), """
@Repository
public interface TestRepository extends VertxRepository {
@Query("INSERT INTO test(value) VALUES (:value)")
void test(@Tag(TestRepository.class) TestRecord value);
}
""", """
public record TestRecord(String value){}
""");

var mapperConstructorParameter = repository.repositoryClass.getConstructors()[0].getParameters()[1];
assertThat(mapperConstructorParameter.getType()).isEqualTo(VertxParameterColumnMapper.class);
var tag = mapperConstructorParameter.getAnnotation(Tag.class);
assertThat(tag).isNotNull();
assertThat(tag.value()).isEqualTo(new Class<?>[]{compileResult.loadClass("TestRepository")});
}

}
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
package ru.tinkoff.kora.database.symbol.processor

import com.google.devtools.ksp.getConstructors
import com.google.devtools.ksp.isPublic
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import com.google.devtools.ksp.symbol.KSValueParameter
import ru.tinkoff.kora.common.naming.NameConverter
import ru.tinkoff.kora.common.naming.SnakeCaseNameConverter
import ru.tinkoff.kora.ksp.common.AnnotationUtils.findAnnotation
import ru.tinkoff.kora.ksp.common.exception.ProcessingError
import ru.tinkoff.kora.ksp.common.exception.ProcessingErrorException
import ru.tinkoff.kora.ksp.common.parseAnnotationValue

private val defaultColumnNameConverter = SnakeCaseNameConverter()
Expand All @@ -26,56 +19,3 @@ fun parseColumnName(valueParameter: KSValueParameter, columnsNameConverter: Name
}
return defaultColumnNameConverter.convert(fieldName)
}

fun parseColumnName(valueParameter: KSPropertyDeclaration, columnsNameConverter: NameConverter?): String {
val column = valueParameter.findAnnotation(DbUtils.columnAnnotation)
if (column != null) {
return parseAnnotationValue<String>(column, "value")!!
}
val fieldName = valueParameter.simpleName.asString()
if (columnsNameConverter != null) {
return columnsNameConverter.convert(fieldName)
}
return defaultColumnNameConverter.convert(fieldName)
}


fun findEntityConstructor(declaration: KSClassDeclaration): KSFunctionDeclaration {
val constructors = declaration.getConstructors()
.filter { it.isPublic() }
.toList()
if (constructors.isEmpty()) {
throw ProcessingErrorException(
listOf(
ProcessingError(
"Entity type ${declaration.simpleName.asString()} has no public constructors", declaration
)
)
)
}
if (constructors.size == 1) {
return constructors[0]
}
val entityConstructors = constructors
.filter { c -> c.findAnnotation(DbUtils.entityConstructorAnnotation) != null }
.toList()
if (entityConstructors.isEmpty()) {
throw ProcessingErrorException(
listOf(
ProcessingError(
"Entity type ${declaration.simpleName.asString()} has more than one public constructor and none of them is marked with @EntityConstructor", declaration
)
)
)
}
if (entityConstructors.size != 1) {
throw ProcessingErrorException(
listOf(
ProcessingError(
"Entity type ${declaration.simpleName.asString()} has more than one public constructor and more then one of them is marked with @EntityConstructor", declaration
)
)
)
}
return entityConstructors[0]
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class CassandraRepositoryGenerator(private val resolver: Resolver) : RepositoryG
val parameterMappers = FieldFactory(typeBuilder, constructorBuilder, "_parameter_mapper")
for (method in repositoryType.findQueryMethods()) {
val methodType = method.asMemberOf(repositoryResolvedType)
val parameters = QueryParameterParser.parse(CassandraTypes.connection, method, methodType)
val parameters = QueryParameterParser.parse(CassandraTypes.connection, CassandraTypes.parameterColumnMapper, method, methodType)
val queryAnnotation = method.findAnnotation(DbUtils.queryAnnotation)!!
val queryString = queryAnnotation.findValue<String>("value")!!
val query = QueryWithParameters.parse(queryString, parameters)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class JdbcRepositoryGenerator(private val resolver: Resolver) : RepositoryGenera
val parameterMappers = FieldFactory(typeBuilder, constructorBuilder, "_parameter_mapper_")
for (method in queryMethods) {
val methodType = method.asMemberOf(repositoryResolvedType)
val parameters = QueryParameterParser.parse(JdbcTypes.connection, method, methodType)
val parameters = QueryParameterParser.parse(JdbcTypes.connection, JdbcTypes.jdbcParameterColumnMapper, method, methodType)
val queryAnnotation = method.findAnnotation(DbUtils.queryAnnotation)!!
val queryString = queryAnnotation.findValue<String>("value")!!
val query = QueryWithParameters.parse(queryString, parameters)
Expand Down
Loading

0 comments on commit ef270f9

Please sign in to comment.