Skip to content

Commit 09e3c8f

Browse files
authored
Add utility to list all entities registered with DynamicRelations
2 parents a13f918 + 4d0bb27 commit 09e3c8f

11 files changed

Lines changed: 314 additions & 0 deletions

File tree

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package at.drm.util;
2+
3+
import at.drm.dao.RelationDao;
4+
import at.drm.factory.RelationDaoFactory;
5+
import lombok.RequiredArgsConstructor;
6+
import org.springframework.core.ResolvableType;
7+
import org.springframework.stereotype.Component;
8+
import java.lang.reflect.Field;
9+
import java.util.List;
10+
import java.util.Set;
11+
import java.util.stream.Collectors;
12+
13+
@Component
14+
@RequiredArgsConstructor
15+
public class DynamicRelationsUtils {
16+
17+
private final RelationDaoFactory relationDaoFactory;
18+
19+
public List<Class<?>> listRegisteredEntities() {
20+
Set<RelationDao> allDaos = relationDaoFactory.getAllDaos();
21+
22+
return allDaos.stream()
23+
.map(this::extractEntityClassFromDao)
24+
.distinct()
25+
.collect(Collectors.toList());
26+
}
27+
28+
private Class<?> extractEntityClassFromDao(RelationDao relationDao) {
29+
try {
30+
ResolvableType resolvableType = ResolvableType.forClass(relationDao.getClass()).as(RelationDao.class);
31+
ResolvableType generic = resolvableType.getGeneric(0);
32+
Class<?> relationLinkClass = getRelationLinkClass(generic);
33+
34+
Field sourceObjectField = relationLinkClass.getDeclaredField("sourceObject");
35+
return sourceObjectField.getType();
36+
} catch (NoSuchFieldException e) {
37+
throw new RuntimeException(
38+
"Could not find sourceObject field in RelationLink class for DAO: " + relationDao.getClass(), e
39+
);
40+
}
41+
}
42+
43+
private Class<?> getRelationLinkClass(ResolvableType generic) {
44+
Class<?> relationLinkClass = generic.resolve();
45+
46+
if (relationLinkClass == null) {
47+
throw new RuntimeException("Could not resolve RelationLink class for type: " + generic.getClass());
48+
}
49+
50+
return relationLinkClass;
51+
}
52+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package at.drm.testutil.daos;
2+
3+
import at.drm.dao.RelationDao;
4+
import at.drm.testutil.relations.AnotherTestEntityRelationLink;
5+
6+
public abstract class AnotherTestEntityRelationDao implements RelationDao<AnotherTestEntityRelationLink, Long> { }
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package at.drm.testutil.daos;
2+
3+
import at.drm.dao.RelationDao;
4+
import at.drm.testutil.relations.InvalidRelationLink;
5+
6+
public abstract class InvalidRelationDao implements RelationDao<InvalidRelationLink, Long> { }
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package at.drm.testutil.daos;
2+
3+
import at.drm.dao.RelationDao;
4+
import at.drm.testutil.relations.TestEntityRelationLink;
5+
6+
public abstract class TestEntityRelationDao implements RelationDao<TestEntityRelationLink, Long> { }
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package at.drm.testutil.entities;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Data;
5+
import lombok.EqualsAndHashCode;
6+
import lombok.ToString;
7+
8+
@Data
9+
@AllArgsConstructor
10+
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
11+
@ToString(onlyExplicitlyIncluded = true)
12+
public class AnotherTestEntity {
13+
14+
@EqualsAndHashCode.Include
15+
@ToString.Include
16+
private Long id;
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package at.drm.testutil.entities;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Data;
5+
import lombok.EqualsAndHashCode;
6+
import lombok.ToString;
7+
8+
@Data
9+
@AllArgsConstructor
10+
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
11+
@ToString(onlyExplicitlyIncluded = true)
12+
public class SomeTestEntity {
13+
14+
@EqualsAndHashCode.Include
15+
@ToString.Include
16+
private Long id;
17+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package at.drm.testutil.relations;
2+
3+
import at.drm.model.RelationLink;
4+
import at.drm.testutil.entities.AnotherTestEntity;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Data;
7+
import lombok.NoArgsConstructor;
8+
9+
@Data
10+
@NoArgsConstructor
11+
@AllArgsConstructor
12+
public class AnotherTestEntityRelationLink implements RelationLink<AnotherTestEntity> {
13+
private AnotherTestEntity sourceObject;
14+
private Long targetId;
15+
private String targetType;
16+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package at.drm.testutil.relations;
2+
3+
import at.drm.model.RelationLink;
4+
import at.drm.testutil.entities.SomeTestEntity;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Data;
7+
import lombok.NoArgsConstructor;
8+
9+
@Data
10+
@NoArgsConstructor
11+
@AllArgsConstructor
12+
public class InvalidRelationLink implements RelationLink<SomeTestEntity> {
13+
private SomeTestEntity wrongFieldName;
14+
private Long targetId;
15+
private String targetType;
16+
17+
@Override
18+
public SomeTestEntity getSourceObject() {
19+
return wrongFieldName;
20+
}
21+
22+
@Override
23+
public void setSourceObject(SomeTestEntity sourceObject) {
24+
this.wrongFieldName = sourceObject;
25+
}
26+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package at.drm.testutil.relations;
2+
3+
import at.drm.model.RelationLink;
4+
import at.drm.testutil.entities.SomeTestEntity;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Data;
7+
import lombok.NoArgsConstructor;
8+
9+
@Data
10+
@NoArgsConstructor
11+
@AllArgsConstructor
12+
public class TestEntityRelationLink implements RelationLink<SomeTestEntity> {
13+
private SomeTestEntity sourceObject;
14+
private Long targetId;
15+
private String targetType;
16+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package at.drm.util;
2+
3+
import at.drm.factory.RelationDaoFactory;
4+
import at.drm.testutil.daos.AnotherTestEntityRelationDao;
5+
import at.drm.testutil.daos.InvalidRelationDao;
6+
import at.drm.testutil.daos.TestEntityRelationDao;
7+
import at.drm.testutil.entities.AnotherTestEntity;
8+
import at.drm.testutil.entities.SomeTestEntity;
9+
import org.junit.jupiter.api.BeforeEach;
10+
import org.junit.jupiter.api.Test;
11+
import org.junit.jupiter.api.extension.ExtendWith;
12+
import org.mockito.Mock;
13+
import org.mockito.junit.jupiter.MockitoExtension;
14+
15+
import java.util.List;
16+
import java.util.Set;
17+
18+
import static org.junit.jupiter.api.Assertions.*;
19+
import static org.mockito.Mockito.*;
20+
21+
@ExtendWith(MockitoExtension.class)
22+
class DynamicRelationsUtilsTest {
23+
24+
@Mock
25+
private RelationDaoFactory relationDaoFactory;
26+
27+
private DynamicRelationsUtils dynamicRelationsUtils;
28+
29+
@BeforeEach
30+
void setUp() {
31+
dynamicRelationsUtils = new DynamicRelationsUtils(relationDaoFactory);
32+
}
33+
34+
@Test
35+
void testListRegisteredEntities_WithMultipleDaos_ShouldReturnDistinctEntityClasses() {
36+
TestEntityRelationDao dao1 = mock(TestEntityRelationDao.class);
37+
AnotherTestEntityRelationDao dao2 = mock(AnotherTestEntityRelationDao.class);
38+
39+
when(relationDaoFactory.getAllDaos()).thenReturn(Set.of(dao1, dao2));
40+
41+
List<Class<?>> result = dynamicRelationsUtils.listRegisteredEntities();
42+
43+
assertNotNull(result);
44+
assertEquals(2, result.size());
45+
assertTrue(result.contains(SomeTestEntity.class));
46+
assertTrue(result.contains(AnotherTestEntity.class));
47+
verify(relationDaoFactory, times(1)).getAllDaos();
48+
}
49+
50+
@Test
51+
void testListRegisteredEntities_WithDuplicateEntityTypes_ShouldReturnDistinctClasses() {
52+
TestEntityRelationDao dao1 = mock(TestEntityRelationDao.class);
53+
TestEntityRelationDao dao2 = mock(TestEntityRelationDao.class);
54+
55+
when(relationDaoFactory.getAllDaos()).thenReturn(Set.of(dao1, dao2));
56+
57+
List<Class<?>> result = dynamicRelationsUtils.listRegisteredEntities();
58+
59+
assertNotNull(result);
60+
assertEquals(1, result.size());
61+
assertEquals(SomeTestEntity.class, result.getFirst());
62+
verify(relationDaoFactory, times(1)).getAllDaos();
63+
}
64+
65+
@Test
66+
void testListRegisteredEntities_WithEmptyDaoSet_ShouldReturnEmptyList() {
67+
when(relationDaoFactory.getAllDaos()).thenReturn(Set.of());
68+
69+
List<Class<?>> result = dynamicRelationsUtils.listRegisteredEntities();
70+
71+
assertNotNull(result);
72+
assertTrue(result.isEmpty());
73+
verify(relationDaoFactory, times(1)).getAllDaos();
74+
}
75+
76+
@Test
77+
void testExtractEntityClassFromDao_WithValidDao_ShouldReturnCorrectEntityClass() {
78+
TestEntityRelationDao dao = mock(TestEntityRelationDao.class);
79+
when(relationDaoFactory.getAllDaos()).thenReturn(Set.of(dao));
80+
81+
List<Class<?>> result = dynamicRelationsUtils.listRegisteredEntities();
82+
83+
assertNotNull(result);
84+
assertEquals(1, result.size());
85+
assertEquals(SomeTestEntity.class, result.getFirst());
86+
}
87+
88+
@Test
89+
void testExtractEntityClassFromDao_WithInvalidRelationLink_ShouldThrowRuntimeException() {
90+
InvalidRelationDao invalidDao = mock(InvalidRelationDao.class);
91+
when(relationDaoFactory.getAllDaos()).thenReturn(Set.of(invalidDao));
92+
93+
RuntimeException exception = assertThrows(RuntimeException.class, () -> dynamicRelationsUtils.listRegisteredEntities());
94+
95+
assertTrue(exception.getMessage().contains("Could not find sourceObject field"));
96+
assertTrue(exception.getMessage().contains("InvalidRelationDao"));
97+
verify(relationDaoFactory, times(1)).getAllDaos();
98+
}
99+
100+
@Test
101+
void testListRegisteredEntities_WithSingleDao_ShouldReturnSingleEntity() {
102+
TestEntityRelationDao dao = mock(TestEntityRelationDao.class);
103+
when(relationDaoFactory.getAllDaos()).thenReturn(Set.of(dao));
104+
105+
List<Class<?>> result = dynamicRelationsUtils.listRegisteredEntities();
106+
107+
assertNotNull(result);
108+
assertEquals(1, result.size());
109+
assertEquals(SomeTestEntity.class, result.getFirst());
110+
verify(relationDaoFactory, times(1)).getAllDaos();
111+
}
112+
113+
@Test
114+
void testListRegisteredEntities_VerifyStreamProcessing() {
115+
TestEntityRelationDao dao1 = mock(TestEntityRelationDao.class);
116+
AnotherTestEntityRelationDao dao2 = mock(AnotherTestEntityRelationDao.class);
117+
TestEntityRelationDao dao3 = mock(TestEntityRelationDao.class);
118+
119+
when(relationDaoFactory.getAllDaos()).thenReturn(Set.of(dao1, dao2, dao3));
120+
121+
List<Class<?>> result = dynamicRelationsUtils.listRegisteredEntities();
122+
123+
assertNotNull(result);
124+
assertEquals(2, result.size());
125+
assertTrue(result.contains(SomeTestEntity.class));
126+
assertTrue(result.contains(AnotherTestEntity.class));
127+
128+
assertInstanceOf(List.class, result);
129+
verify(relationDaoFactory, times(1)).getAllDaos();
130+
}
131+
}

0 commit comments

Comments
 (0)