fix issues with foreign key exposure and add some tests for it
This commit is contained in:
@@ -5,10 +5,7 @@ import jef.model.annotations.ForeignKey;
|
||||
import jef.serializable.SerializableObject;
|
||||
import jef.util.Util;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
class ForeignKeyExposeInitializer {//TODO testcases for this class
|
||||
|
||||
class ForeignKeyExposeInitializer {
|
||||
static void initForeignKeyExposures(ModelBuilder mb) {
|
||||
var entities = mb.entities();
|
||||
for (int i = 0; i < entities.size(); i++) {
|
||||
@@ -29,43 +26,29 @@ class ForeignKeyExposeInitializer {//TODO testcases for this class
|
||||
}
|
||||
//match with name in annotation
|
||||
var anno = e.getField().getField().getAnnotation(ForeignKey.class);
|
||||
if (anno != null) {
|
||||
if (anno.entity() != SerializableObject.class) { // not a foreign key exposure
|
||||
return false;
|
||||
}
|
||||
var fieldname = anno.getterOrField(); //TODO test
|
||||
if (anno == null) {
|
||||
return false;
|
||||
}
|
||||
if (anno.entity() != SerializableObject.class) { // not a foreign key exposure, ignore
|
||||
return false;
|
||||
}
|
||||
var fieldname = anno.getterOrField();
|
||||
|
||||
//if getter, extract getter field
|
||||
var method = Util.tryGet(() -> entityBuilder.type().orElseThrow().getMethod(anno.getterOrField())); //TODO test
|
||||
if (method.isPresent()) {
|
||||
var res = new OptimizedAsmParser(method.get()).parse();
|
||||
fieldname = res.getAccessedFields().stream().findFirst().orElseThrow().getName();
|
||||
}
|
||||
return f.getField().getName().equals(fieldname);
|
||||
//if getter, extract getter field
|
||||
var method = Util.tryGet(() -> entityBuilder.type().orElseThrow().getMethod(anno.getterOrField()));
|
||||
if (method.isPresent()) {
|
||||
var res = new OptimizedAsmParser(method.get()).parse();
|
||||
fieldname = res.getAccessedFields().stream().findFirst().orElseThrow().getName();
|
||||
}
|
||||
//only allow fields with same prefix
|
||||
if (!e.getField().getName().startsWith(f.getField().getName())) {
|
||||
return false;
|
||||
if (!SerializableObject.class.isAssignableFrom(entityBuilder.getEntity().getField(fieldname).getType())) {
|
||||
throw new ModelException("The getter/field in " + entityBuilder.className() + "::" + f.getField().getField().getName() + " is not a " + SerializableObject.class.getSimpleName() + " (via @ForeignKey in TestClass::testFk)");
|
||||
}
|
||||
//match id field which ends in Id or Fk
|
||||
if (e.getField().getName().equals(f.getField().getName() + "Id") //TODO test
|
||||
|| e.getField().getName().equals(f.getField().getName() + "ID") //TODO test
|
||||
|| e.getField().getName().equals(f.getField().getName() + "Fk") //TODO test
|
||||
|| e.getField().getName().equals(f.getField().getName() + "FK")) { //TODO test
|
||||
return true;
|
||||
}
|
||||
//match id field which ends in the name of the primary key field of the referenced entity
|
||||
var other = mb.getEntity(f.getField().getTypeName()); //TODO test
|
||||
if (other == null || other.getPrimaryKey() == null) {
|
||||
return false;
|
||||
}
|
||||
//assumption: initializing from models only has primary keys with 1 field (no composite keys)
|
||||
var x = other.getPrimaryKey().getFields().get(0).getName(); //TODO test
|
||||
x = x.substring(0, 1).toUpperCase(Locale.ROOT) + x.substring(1);
|
||||
return e.getField().getName().equals(f.getField().getName() + x);
|
||||
}).toList();
|
||||
return f.getField().getName().equals(fieldname);
|
||||
})
|
||||
.toList();
|
||||
idFields.forEach(e -> {
|
||||
e.getField().setExposingForeignKeyOf(f.getField());
|
||||
e.getField().setDatabaseField(idFields.stream().findFirst().orElseThrow() == e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,12 +147,20 @@ class ForeignKeyInitializer {
|
||||
// }
|
||||
|
||||
//find objectId in other entity (1 to 1 relation)
|
||||
//search for exposing foreign key field first
|
||||
var entityIdField = entity.getFields().stream()
|
||||
.filter(oef -> oef.getExposingForeignKeyOf() != null && oef.getExposingForeignKeyOf().getField().equals(f))
|
||||
.findFirst();
|
||||
|
||||
//search for field with name object field name + primary field name
|
||||
var idFieldName = f.getName()
|
||||
+ otherPrimaryField.getName().substring(0, 1).toUpperCase(Locale.ROOT)
|
||||
+ otherPrimaryField.getName().substring(1);
|
||||
var entityIdField = entity.getFields().stream().filter(oef -> oef.getName().equals(idFieldName)).findFirst();
|
||||
if (!entityIdField.isPresent()) {
|
||||
entityIdField = entity.getFields().stream().filter(oef -> oef.getName().equals(idFieldName)).findFirst();
|
||||
}
|
||||
if (entityIdField.isPresent()) {
|
||||
return new FieldSearchResult(entityIdField.get(), entityIdField.get().getField().getAnnotationsByType(ForeignKey.class).length == 0);
|
||||
return new FieldSearchResult(entityIdField.get(), true);//entityIdField.get().getField().getAnnotationsByType(ForeignKey.class).length == 0);
|
||||
}
|
||||
|
||||
var field = new DbField<>(entity, otherPrimaryField.getType(), null, idFieldName);
|
||||
|
||||
@@ -28,15 +28,10 @@ class ForeignKeyProcessorExposeTest {
|
||||
|
||||
//fields ------------------------
|
||||
//TestClass
|
||||
assertEquals(8, mb.getEntity(TestClass.class).getFields().size());
|
||||
assertEquals(3, mb.getEntity(TestClass.class).getFields().size());
|
||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("i") && e.isModelField() && e.isDatabaseField()).count());
|
||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("d") && e.isModelField() && e.isDatabaseField()).count());
|
||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("f") && e.isModelField() && e.isDatabaseField()).count());
|
||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("l") && e.isModelField() && e.isDatabaseField()).count());
|
||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("o") && e.isModelField() && e.isDatabaseField()).count());
|
||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("test") && e.isModelField() && !e.isDatabaseField()).count());
|
||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("testFk") && e.isModelField() && e.isDatabaseField()).count());
|
||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("testI") && !e.isModelField() && e.isDatabaseField()).count());
|
||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("testField") && e.isModelField() && e.isDatabaseField()).count());
|
||||
|
||||
//TestClass2
|
||||
assertEquals(1, mb.getEntity(TestClass2.class).getFields().size());
|
||||
@@ -47,10 +42,13 @@ class ForeignKeyProcessorExposeTest {
|
||||
assertEquals(1, mb.getEntity(TestClass.class).getForeignKeys().size());
|
||||
assertEquals(mb.getEntity(TestClass.class), mb.getEntity(TestClass.class).getForeignKeys().get(0).getEntity());
|
||||
assertEquals(mb.getEntity(TestClass2.class), mb.getEntity(TestClass.class).getForeignKeys().get(0).getReferencedEntity());
|
||||
assertEquals(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("testI")).toList(),
|
||||
assertEquals(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("testField")).toList(),
|
||||
mb.getEntity(TestClass.class).getForeignKeys().get(0).getFields());
|
||||
assertEquals(mb.getEntity(TestClass2.class).getPrimaryKey().getFields(), mb.getEntity(TestClass.class).getForeignKeys().get(0).getReferencedFields());
|
||||
// /keys ------------------------
|
||||
|
||||
//exposing fields
|
||||
assertEquals(mb.getEntity(TestClass.class).getField("test"), mb.getEntity(TestClass.class).getField("testField").getExposingForeignKeyOf());
|
||||
}
|
||||
|
||||
public static class Ctx extends DbContext {
|
||||
@@ -63,13 +61,9 @@ class ForeignKeyProcessorExposeTest {
|
||||
@Getter
|
||||
public static class TestClass extends SerializableObject {
|
||||
public int i = 1;
|
||||
public Object o = new Object();
|
||||
public double d;
|
||||
public float f;
|
||||
public long l;
|
||||
private TestClass2 test;
|
||||
@ForeignKey(getterOrField = "test")
|
||||
public int testFk;
|
||||
public int testField;
|
||||
}
|
||||
|
||||
@Getter
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package jef.model.annotations.processors;
|
||||
|
||||
import jef.DbSet;
|
||||
import jef.model.DbContext;
|
||||
import jef.model.ModelBuilder;
|
||||
import jef.model.annotations.Clazz;
|
||||
import jef.model.annotations.ForeignKey;
|
||||
import jef.model.annotations.Id;
|
||||
import jef.serializable.SerializableObject;
|
||||
import lombok.Getter;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ForeignKeyProcessorExposeViaMethodTest {
|
||||
@Test
|
||||
public void test() {
|
||||
var mb = ModelBuilder.from(Ctx.class);
|
||||
|
||||
assertEquals(2, mb.getEntities().size());
|
||||
assertEquals("objects1", mb.getEntity(TestClass.class).getName());
|
||||
assertEquals("objects2", mb.getEntity(TestClass2.class).getName());
|
||||
assertTrue(mb.getEntity(TestClass.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));
|
||||
assertTrue(mb.getEntity(TestClass2.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass2.class)));
|
||||
assertTrue(mb.getEntity(TestClass.class).getForeignKeys().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));
|
||||
assertTrue(mb.getEntity(TestClass2.class).getForeignKeys().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass2.class)));
|
||||
|
||||
//fields ------------------------
|
||||
//TestClass
|
||||
assertEquals(3, mb.getEntity(TestClass.class).getFields().size());
|
||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("i") && e.isModelField() && e.isDatabaseField()).count());
|
||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("test") && e.isModelField() && !e.isDatabaseField()).count());
|
||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("testField") && e.isModelField() && e.isDatabaseField()).count());
|
||||
|
||||
//TestClass2
|
||||
assertEquals(1, mb.getEntity(TestClass2.class).getFields().size());
|
||||
assertEquals(1, mb.getEntity(TestClass2.class).getFields().stream().filter(e -> e.getName().equals("i") && e.isModelField() && e.isDatabaseField()).count());
|
||||
// /fields ------------------------
|
||||
|
||||
//keys ------------------------
|
||||
assertEquals(1, mb.getEntity(TestClass.class).getForeignKeys().size());
|
||||
assertEquals(mb.getEntity(TestClass.class), mb.getEntity(TestClass.class).getForeignKeys().get(0).getEntity());
|
||||
assertEquals(mb.getEntity(TestClass2.class), mb.getEntity(TestClass.class).getForeignKeys().get(0).getReferencedEntity());
|
||||
assertEquals(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("testField")).toList(),
|
||||
mb.getEntity(TestClass.class).getForeignKeys().get(0).getFields());
|
||||
assertEquals(mb.getEntity(TestClass2.class).getPrimaryKey().getFields(), mb.getEntity(TestClass.class).getForeignKeys().get(0).getReferencedFields());
|
||||
// /keys ------------------------
|
||||
|
||||
//exposing fields
|
||||
assertEquals(mb.getEntity(TestClass.class).getField("test"), mb.getEntity(TestClass.class).getField("testField").getExposingForeignKeyOf());
|
||||
}
|
||||
|
||||
public static class Ctx extends DbContext {
|
||||
@Clazz(TestClass.class)
|
||||
private DbSet<TestClass> objects1;
|
||||
@Clazz(TestClass2.class)
|
||||
private DbSet<TestClass2> objects2;
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class TestClass extends SerializableObject {
|
||||
public int i = 1;
|
||||
private TestClass2 test;
|
||||
@ForeignKey(getterOrField = "getTest")
|
||||
public int testField;
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class TestClass2 extends SerializableObject {
|
||||
@Id
|
||||
public int i = 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package jef.model.annotations.processors;
|
||||
|
||||
import jef.DbSet;
|
||||
import jef.model.DbContext;
|
||||
import jef.model.ModelBuilder;
|
||||
import jef.model.ModelException;
|
||||
import jef.model.annotations.Clazz;
|
||||
import jef.model.annotations.ForeignKey;
|
||||
import jef.model.annotations.Id;
|
||||
import jef.serializable.SerializableObject;
|
||||
import lombok.Getter;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ForeignKeyProcessorInvalidFieldTypeTest {
|
||||
@Test
|
||||
public void test() {
|
||||
assertEquals("The getter/field in TestClass::test is not a SerializableObject (via @ForeignKey in TestClass::testFk)",
|
||||
assertThrowsExactly(ModelException.class, () -> ModelBuilder.from(Ctx.class)).getMessage());
|
||||
}
|
||||
|
||||
public static class Ctx extends DbContext {
|
||||
@Clazz(TestClass.class)
|
||||
private DbSet<TestClass> objects1;
|
||||
@Clazz(TestClass2.class)
|
||||
private DbSet<TestClass2> objects2;
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class TestClass extends SerializableObject {
|
||||
public int i = 1;
|
||||
public Object o = new Object();
|
||||
public double d;
|
||||
public float f;
|
||||
public long l;
|
||||
private TestClass2 test;
|
||||
@ForeignKey(getterOrField = "l")
|
||||
public int testFk;
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class TestClass2 extends SerializableObject {
|
||||
@Id
|
||||
public int i = 1;
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ForeignKeyProcessorInvalidFieldOrGetterTest {
|
||||
class ForeignKeyProcessorNonExistingFieldOrGetterTest {
|
||||
@Test
|
||||
public void test() {
|
||||
assertEquals("Cannot find getter/field TestClass::x (via @ForeignKey in TestClass::ref)",
|
||||
Reference in New Issue
Block a user