Skip to content

Commit c70bcf6

Browse files
authored
chore: add support for simple path resolver (#41)
Add support for a simple relection-based path resolver that aims to replace the required features of [go-funk](https://github.com/thoas/go-funk) Since go-funk is a feature rich library (based on lodash) we are unlikely to need all the features offered. This implementation aims to provide the basic path based attribute resolution (which appears to cover the majority of our current use case). It stops short of supporting complex paths e.g. containing collections or array indexing etc. fixes: #40
1 parent f4eeee4 commit c70bcf6

File tree

2 files changed

+106
-0
lines changed

2 files changed

+106
-0
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package io.cloudquery.helper;
2+
3+
import java.lang.reflect.Field;
4+
5+
/**
6+
* A simple reflection-based path resolver.
7+
*/
8+
public class ReflectionPathResolver {
9+
10+
public static class PathResolverException extends Exception {
11+
public PathResolverException(String message, Throwable ex) {
12+
super(message, ex);
13+
}
14+
}
15+
16+
/**
17+
* Resolve a path of an object using reflection.
18+
* <p>
19+
* e.g. if we have a class:
20+
* <pre>
21+
* class TestClass {
22+
* private String name;
23+
* private TestClass child;
24+
* }
25+
* </pre>
26+
* <p>
27+
* Then the following are valid paths to retrieve the associated values:
28+
* <p>
29+
* `name`
30+
* `child.name`
31+
* </p>
32+
* NOTE: this implementation is currently very simplistic and only supports simple field and nested field resolution.
33+
* It does not support collection resolution - unlike the Go SDK which uses <a href="https://github.com/thoas/go-funk#get">go-funk</a>.
34+
*
35+
* @param object The object to resolve the path on
36+
* @param path The path to resolve
37+
* @return The value of the property at the resolved path
38+
* @throws PathResolverException If the path cannot be resolved
39+
*/
40+
public static Object resolve(Object object, String path) throws PathResolverException {
41+
Object current = object;
42+
43+
for (String currentPath : path.split("\\.")) {
44+
try {
45+
Field currentField = object.getClass().getDeclaredField(currentPath);
46+
if (!currentField.canAccess(current)) {
47+
currentField.setAccessible(true);
48+
}
49+
object = currentField.get(object);
50+
} catch (NoSuchFieldException | IllegalAccessException e) {
51+
throw new PathResolverException("Unable to resolve path " + currentPath, e);
52+
}
53+
}
54+
55+
return object;
56+
}
57+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package io.cloudquery.helper;
2+
3+
import lombok.Builder;
4+
import org.junit.jupiter.api.Test;
5+
6+
import java.util.List;
7+
8+
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
import static org.junit.jupiter.api.Assertions.assertThrows;
10+
11+
class ReflectionPathResolverTest {
12+
@Builder
13+
private static class TestClass {
14+
private String name;
15+
16+
@Builder.Default
17+
private List<Integer> numbers = List.of(1, 2, 3);
18+
19+
private TestClass singleChild;
20+
21+
private List<TestClass> multipleChildren;
22+
}
23+
24+
private static final TestClass TEST_DATA = TestClass.builder().name("root").
25+
singleChild(TestClass.builder().name("single-child1").build()).
26+
multipleChildren(
27+
List.of(
28+
TestClass.builder().name("multi-child1").build(),
29+
TestClass.builder().name("multi-child2").build()
30+
)
31+
).
32+
build();
33+
34+
@Test
35+
public void shouldResolveSimpleFields() throws ReflectionPathResolver.PathResolverException {
36+
assertEquals("root", ReflectionPathResolver.resolve(TEST_DATA, "name"));
37+
assertEquals(List.of(1, 2, 3), ReflectionPathResolver.resolve(TEST_DATA, "numbers"));
38+
}
39+
40+
@Test
41+
public void shouldResolveNestedField() throws ReflectionPathResolver.PathResolverException {
42+
assertEquals("single-child1", ReflectionPathResolver.resolve(TEST_DATA, "singleChild.name"));
43+
}
44+
45+
@Test
46+
public void shouldThrowAnErrorIfWeEncounterACollection() {
47+
assertThrows(ReflectionPathResolver.PathResolverException.class, () -> ReflectionPathResolver.resolve(TEST_DATA, "multiplChildren.name"));
48+
}
49+
}

0 commit comments

Comments
 (0)