Skip to content

Commit f9f6aa0

Browse files
authored
feat: add possibility to use dynamic measurement in mapping from/to POJO (#269)
1 parent e79907f commit f9f6aa0

12 files changed

Lines changed: 122 additions & 39 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
## 3.4.0 [unreleased]
22

3+
### Features
4+
1. [#269](https://github.com/influxdata/influxdb-client-java/pull/269): Add possibility to use dynamic `measurement` in mapping from/to `POJO`
5+
36
### CI
47
1. [#267](https://github.com/influxdata/influxdb-client-java/pull/267): Add JDK 17 (LTS) to CI pipeline instead of JDK 16
58

client-core/src/main/java/com/influxdb/annotations/Column.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,26 @@
2828

2929

3030
/**
31-
* The annotation is used to customize bidirectional mapping between POJO and Flux query result or lineprotocol.
31+
* The annotation to customize bidirectional mapping between POJO and Flux query result or LineProtocol.
3232
*/
3333
@Retention(RetentionPolicy.RUNTIME)
3434
@Target(ElementType.FIELD)
3535
public @interface Column {
3636

3737
String name() default "";
3838

39+
/**
40+
* @return {@link Boolean#TRUE} it an annotated field is Tag
41+
*/
3942
boolean tag() default false;
4043

44+
/**
45+
* @return {@link Boolean#TRUE} it an annotated field is Measurement
46+
*/
47+
boolean measurement() default false;
48+
49+
/**
50+
* @return {@link Boolean#TRUE} it an annotated field is Timestamp
51+
*/
4152
boolean timestamp () default false;
4253
}

client-core/src/main/java/com/influxdb/query/internal/FluxResultMapper.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ public <T> T toPOJO(@Nonnull final FluxRecord record, @Nonnull final Class<T> cl
7575
col = columnName;
7676
} else if (recordValues.containsKey("_" + columnName)) {
7777
col = "_" + columnName;
78+
} else if (anno != null && anno.measurement()) {
79+
col = "_measurement";
7880
} else {
7981
String columnNameInSnakeCase = camelCaseToSnakeCase(columnName);
8082
if (recordValues.containsKey(columnNameInSnakeCase)) {

client-core/src/test/java/com/influxdb/query/internal/FluxResultMapperTest.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,20 @@ public void camelCaseToSnakeCase() {
104104
Assertions.assertThat(bean.someValue).isEqualTo(20);
105105
}
106106

107-
public static class BigDecimalBean
108-
{
107+
@Test
108+
public void pojoWithMeasurement() {
109+
FluxRecord record = new FluxRecord(0);
110+
record.getValues().put("_measurement", "mem");
111+
record.getValues().put("value", 20);
112+
record.getValues().put("tag", "a");
113+
114+
BeanWithMeasurement bean = mapper.toPOJO(record, BeanWithMeasurement.class);
115+
Assertions.assertThat(bean.customField).isEqualTo("mem");
116+
Assertions.assertThat(bean.tag).isEqualTo("a");
117+
Assertions.assertThat(bean.value).isEqualByComparingTo(new BigDecimal(20));
118+
}
119+
120+
public static class BigDecimalBean {
109121
@Column(name = "value1")
110122
BigDecimal value1;
111123

@@ -146,4 +158,15 @@ public enum TagEnum {
146158
tagA,
147159
tagB
148160
}
161+
162+
public static class BeanWithMeasurement {
163+
@Column(measurement = true)
164+
String customField;
165+
166+
@Column(name = "tag", tag = true)
167+
String tag;
168+
169+
@Column(name = "value")
170+
BigDecimal value;
171+
}
149172
}

client-kotlin/src/test/kotlin/com/influxdb/client/kotlin/ITQueryKotlinApi.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,7 @@ internal class ITQueryKotlinApi : AbstractITInfluxDBClientKotlin() {
6767
@BeforeEach
6868
fun `Write testing data`(): Unit = runBlocking {
6969

70-
val client = InfluxDBClientFactory.create(influxDb2Url, "my-user",
71-
"my-password".toCharArray())
70+
val client = InfluxDBClientFactory.create(influxDb2Url, "my-token".toCharArray())
7271

7372
organization = client.organizationsApi
7473
.findOrganizations().stream()

client-reactive/src/test/java/com/influxdb/client/reactive/AbstractITInfluxDBClientTest.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ void setUp() throws Exception {
5050
influxDB_URL = getInfluxDb2Url();
5151
LOG.log(Level.FINEST, "InfluxDB URL: {0}", influxDB_URL);
5252

53-
InfluxDBClient influxDBClient = InfluxDBClientFactory.create(influxDB_URL, "my-user", "my-password".toCharArray());
53+
InfluxDBClient influxDBClient = InfluxDBClientFactory.create(influxDB_URL, "my-token".toCharArray());
5454

5555
organization = influxDBClient.getOrganizationsApi()
5656
.findOrganizations().stream()
@@ -60,12 +60,7 @@ void setUp() throws Exception {
6060

6161
influxDBClient.close();
6262

63-
try {
64-
this.influxDBClient = InfluxDBClientReactiveFactory.create(influxDB_URL, "my-user", "my-password".toCharArray());
65-
} catch (Exception e) {
66-
Assertions.fail("Can't authorize via password", e);
67-
}
68-
63+
this.influxDBClient = InfluxDBClientReactiveFactory.create(influxDB_URL, "my-token".toCharArray());
6964
}
7065

7166
@AfterEach

client-reactive/src/test/java/com/influxdb/client/reactive/ITWriteQueryReactiveApi.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,7 @@ void setUp() throws Exception {
6868

6969
super.setUp();
7070

71-
InfluxDBClient client = InfluxDBClientFactory.create(influxDB_URL, "my-user",
72-
"my-password".toCharArray());
71+
InfluxDBClient client = InfluxDBClientFactory.create(influxDB_URL, "my-token".toCharArray());
7372

7473
bucket = client.getBucketsApi()
7574
.createBucket(generateName("h2o"), null, organization);

client-scala/src/test/scala/com/influxdb/client/scala/ITQueryScalaApiQuery.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class ITQueryScalaApiQuery extends AbstractITQueryScalaApi with Matchers {
5656

5757
super.setUp()
5858

59-
val client = InfluxDBClientFactory.create(influxDBUtils.getUrl, "my-user", "my-password".toCharArray)
59+
val client = InfluxDBClientFactory.create(influxDBUtils.getUrl, "my-token".toCharArray)
6060

6161
organization = client.getOrganizationsApi
6262
.findOrganizations()
@@ -387,7 +387,7 @@ class ITQueryScalaApiQuery extends AbstractITQueryScalaApi with Matchers {
387387
// Prepare data
388388
//
389389
val records = Range(0, 10000).map(n => s"buffer field=$n $n").mkString("\n")
390-
val client = InfluxDBClientFactory.create(influxDBUtils.getUrl, "my-user", "my-password".toCharArray)
390+
val client = InfluxDBClientFactory.create(influxDBUtils.getUrl, "my-token".toCharArray)
391391
client.getWriteApiBlocking.writeRecord(bucket.getName, organization.getId, WritePrecision.NS, records)
392392
client.close()
393393

client/src/main/java/com/influxdb/client/internal/MeasurementMapper.java

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
import java.lang.reflect.Field;
2525
import java.time.Instant;
26+
import java.util.Map;
2627
import java.util.concurrent.ConcurrentHashMap;
2728
import java.util.concurrent.ConcurrentMap;
2829
import java.util.logging.Level;
@@ -54,32 +55,22 @@ <M> Point toPoint(@Nonnull final M measurement, @Nonnull final WritePrecision pr
5455
Class<?> measurementType = measurement.getClass();
5556
cacheMeasurementClass(measurementType);
5657

57-
if (measurementType.getAnnotation(Measurement.class) == null) {
58-
String message = String
59-
.format("Measurement type '%s' does not have a @Measurement annotation.", measurementType);
60-
61-
throw new InfluxException(message);
62-
}
63-
64-
Point point = Point.measurement(getMeasurementName(measurementType));
65-
66-
CLASS_FIELD_CACHE.get(measurementType.getName()).forEach((name, field) -> {
58+
Point point = Point.measurement(getMeasurementName(measurement, measurementType));
6759

60+
for (Map.Entry<String, Field> entry : CLASS_FIELD_CACHE.get(measurementType.getName()).entrySet()) {
61+
String name = entry.getKey();
62+
Field field = entry.getValue();
6863
Column column = field.getAnnotation(Column.class);
69-
70-
Object value;
71-
try {
72-
field.setAccessible(true);
73-
value = field.get(measurement);
74-
} catch (IllegalAccessException e) {
75-
76-
throw new InfluxException(e);
64+
if (column.measurement()) {
65+
continue;
7766
}
7867

68+
Object value = getObject(measurement, field);
69+
7970
if (value == null) {
8071
Object[] params = {field.getName(), measurement};
8172
LOG.log(Level.FINEST, "Field {0} of {1} has null value", params);
82-
return;
73+
continue;
8374
}
8475

8576
Class<?> fieldType = field.getType();
@@ -97,16 +88,51 @@ <M> Point toPoint(@Nonnull final M measurement, @Nonnull final WritePrecision pr
9788
} else {
9889
point.addField(name, value.toString());
9990
}
100-
});
91+
}
10192

10293
LOG.log(Level.FINEST, "Mapped measurement: {0} to Point: {1}", new Object[]{measurement, point});
10394

10495
return point;
10596
}
10697

10798
@Nonnull
108-
private String getMeasurementName(@Nonnull final Class<?> measurementType) {
109-
return measurementType.getAnnotation(Measurement.class).name();
99+
private <M> String getMeasurementName(@Nonnull final M measurement, @Nonnull final Class<?> measurementType) {
100+
101+
// from @Measurement annotation for class
102+
Measurement measurementAnnotation = measurementType.getAnnotation(Measurement.class);
103+
if (measurementAnnotation != null) {
104+
return measurementAnnotation.name();
105+
}
106+
107+
// from Field with @Column(measurement = true)
108+
Field measurementField = CLASS_FIELD_CACHE.get(measurementType.getName())
109+
.values()
110+
.stream()
111+
.filter(field -> field.getAnnotation(Column.class).measurement())
112+
.findFirst()
113+
.orElse(null);
114+
115+
if (measurementField == null) {
116+
String message = String
117+
.format("Unable to determine Measurement for '%s'. Does it have a @Measurement annotation or "
118+
+ "field with @Column(measurement = true) annotation?", measurementType);
119+
120+
throw new InfluxException(message);
121+
}
122+
123+
return getObject(measurement, measurementField).toString();
124+
}
125+
126+
private <M> Object getObject(@Nonnull final M measurement, @Nonnull final Field field) {
127+
Object value;
128+
try {
129+
field.setAccessible(true);
130+
value = field.get(measurement);
131+
} catch (IllegalAccessException e) {
132+
133+
throw new InfluxException(e);
134+
}
135+
return value;
110136
}
111137

112138
private boolean isNumber(@Nonnull final Class<?> fieldType) {

client/src/test/java/com/influxdb/client/ITUsersApi.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ void createNewUserAndSetPassword() throws Exception {
197197

198198
@Test
199199
@Tag("basic_auth")
200+
@Disabled("TODO not implemented set password https://github.com/influxdata/influxdb/pull/15981")
200201
void updatePasswordNotFound() {
201202

202203
Assertions.assertThatThrownBy(() -> usersApi.updateUserPassword("020f755c3c082000", "", "new-password"))

0 commit comments

Comments
 (0)