fix issues with foreign key exposure and add some tests for it

This commit is contained in:
wea_ondara
2022-09-10 11:19:06 +02:00
parent 967d099d81
commit de17b8985a
6 changed files with 160 additions and 52 deletions

View File

@@ -5,10 +5,7 @@ import jef.model.annotations.ForeignKey;
import jef.serializable.SerializableObject; import jef.serializable.SerializableObject;
import jef.util.Util; import jef.util.Util;
import java.util.Locale; class ForeignKeyExposeInitializer {
class ForeignKeyExposeInitializer {//TODO testcases for this class
static void initForeignKeyExposures(ModelBuilder mb) { static void initForeignKeyExposures(ModelBuilder mb) {
var entities = mb.entities(); var entities = mb.entities();
for (int i = 0; i < entities.size(); i++) { for (int i = 0; i < entities.size(); i++) {
@@ -29,43 +26,29 @@ class ForeignKeyExposeInitializer {//TODO testcases for this class
} }
//match with name in annotation //match with name in annotation
var anno = e.getField().getField().getAnnotation(ForeignKey.class); var anno = e.getField().getField().getAnnotation(ForeignKey.class);
if (anno != null) { if (anno == null) {
if (anno.entity() != SerializableObject.class) { // not a foreign key exposure return false;
return false; }
} if (anno.entity() != SerializableObject.class) { // not a foreign key exposure, ignore
var fieldname = anno.getterOrField(); //TODO test return false;
}
var fieldname = anno.getterOrField();
//if getter, extract getter field //if getter, extract getter field
var method = Util.tryGet(() -> entityBuilder.type().orElseThrow().getMethod(anno.getterOrField())); //TODO test var method = Util.tryGet(() -> entityBuilder.type().orElseThrow().getMethod(anno.getterOrField()));
if (method.isPresent()) { if (method.isPresent()) {
var res = new OptimizedAsmParser(method.get()).parse(); var res = new OptimizedAsmParser(method.get()).parse();
fieldname = res.getAccessedFields().stream().findFirst().orElseThrow().getName(); fieldname = res.getAccessedFields().stream().findFirst().orElseThrow().getName();
}
return f.getField().getName().equals(fieldname);
} }
//only allow fields with same prefix if (!SerializableObject.class.isAssignableFrom(entityBuilder.getEntity().getField(fieldname).getType())) {
if (!e.getField().getName().startsWith(f.getField().getName())) { throw new ModelException("The getter/field in " + entityBuilder.className() + "::" + f.getField().getField().getName() + " is not a " + SerializableObject.class.getSimpleName() + " (via @ForeignKey in TestClass::testFk)");
return false;
} }
//match id field which ends in Id or Fk return f.getField().getName().equals(fieldname);
if (e.getField().getName().equals(f.getField().getName() + "Id") //TODO test })
|| e.getField().getName().equals(f.getField().getName() + "ID") //TODO test .toList();
|| 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();
idFields.forEach(e -> { idFields.forEach(e -> {
e.getField().setExposingForeignKeyOf(f.getField()); e.getField().setExposingForeignKeyOf(f.getField());
e.getField().setDatabaseField(idFields.stream().findFirst().orElseThrow() == e);
}); });
} }
} }

View File

@@ -147,12 +147,20 @@ class ForeignKeyInitializer {
// } // }
//find objectId in other entity (1 to 1 relation) //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() var idFieldName = f.getName()
+ otherPrimaryField.getName().substring(0, 1).toUpperCase(Locale.ROOT) + otherPrimaryField.getName().substring(0, 1).toUpperCase(Locale.ROOT)
+ otherPrimaryField.getName().substring(1); + 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()) { 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); var field = new DbField<>(entity, otherPrimaryField.getType(), null, idFieldName);

View File

@@ -28,15 +28,10 @@ class ForeignKeyProcessorExposeTest {
//fields ------------------------ //fields ------------------------
//TestClass //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("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("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("testField") && e.isModelField() && e.isDatabaseField()).count());
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("testI") && !e.isModelField() && e.isDatabaseField()).count());
//TestClass2 //TestClass2
assertEquals(1, mb.getEntity(TestClass2.class).getFields().size()); assertEquals(1, mb.getEntity(TestClass2.class).getFields().size());
@@ -47,10 +42,13 @@ class ForeignKeyProcessorExposeTest {
assertEquals(1, mb.getEntity(TestClass.class).getForeignKeys().size()); assertEquals(1, mb.getEntity(TestClass.class).getForeignKeys().size());
assertEquals(mb.getEntity(TestClass.class), mb.getEntity(TestClass.class).getForeignKeys().get(0).getEntity()); 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(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()); mb.getEntity(TestClass.class).getForeignKeys().get(0).getFields());
assertEquals(mb.getEntity(TestClass2.class).getPrimaryKey().getFields(), mb.getEntity(TestClass.class).getForeignKeys().get(0).getReferencedFields()); assertEquals(mb.getEntity(TestClass2.class).getPrimaryKey().getFields(), mb.getEntity(TestClass.class).getForeignKeys().get(0).getReferencedFields());
// /keys ------------------------ // /keys ------------------------
//exposing fields
assertEquals(mb.getEntity(TestClass.class).getField("test"), mb.getEntity(TestClass.class).getField("testField").getExposingForeignKeyOf());
} }
public static class Ctx extends DbContext { public static class Ctx extends DbContext {
@@ -63,13 +61,9 @@ class ForeignKeyProcessorExposeTest {
@Getter @Getter
public static class TestClass extends SerializableObject { public static class TestClass extends SerializableObject {
public int i = 1; public int i = 1;
public Object o = new Object();
public double d;
public float f;
public long l;
private TestClass2 test; private TestClass2 test;
@ForeignKey(getterOrField = "test") @ForeignKey(getterOrField = "test")
public int testFk; public int testField;
} }
@Getter @Getter

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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.assertThrowsExactly;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
class ForeignKeyProcessorInvalidFieldOrGetterTest { class ForeignKeyProcessorNonExistingFieldOrGetterTest {
@Test @Test
public void test() { public void test() {
assertEquals("Cannot find getter/field TestClass::x (via @ForeignKey in TestClass::ref)", assertEquals("Cannot find getter/field TestClass::x (via @ForeignKey in TestClass::ref)",