Skip to content

Commit

Permalink
feat: add possibility to use dynamic measurement in mapping from/to P…
Browse files Browse the repository at this point in the history
…OJO (#269)
  • Loading branch information
bednar authored Oct 12, 2021
1 parent e79907f commit f9f6aa0
Show file tree
Hide file tree
Showing 12 changed files with 122 additions and 39 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## 3.4.0 [unreleased]

### Features
1. [#269](https://github.com/influxdata/influxdb-client-java/pull/269): Add possibility to use dynamic `measurement` in mapping from/to `POJO`

### CI
1. [#267](https://github.com/influxdata/influxdb-client-java/pull/267): Add JDK 17 (LTS) to CI pipeline instead of JDK 16

Expand Down
13 changes: 12 additions & 1 deletion client-core/src/main/java/com/influxdb/annotations/Column.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,26 @@


/**
* The annotation is used to customize bidirectional mapping between POJO and Flux query result or lineprotocol.
* The annotation to customize bidirectional mapping between POJO and Flux query result or LineProtocol.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {

String name() default "";

/**
* @return {@link Boolean#TRUE} it an annotated field is Tag
*/
boolean tag() default false;

/**
* @return {@link Boolean#TRUE} it an annotated field is Measurement
*/
boolean measurement() default false;

/**
* @return {@link Boolean#TRUE} it an annotated field is Timestamp
*/
boolean timestamp () default false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ public <T> T toPOJO(@Nonnull final FluxRecord record, @Nonnull final Class<T> cl
col = columnName;
} else if (recordValues.containsKey("_" + columnName)) {
col = "_" + columnName;
} else if (anno != null && anno.measurement()) {
col = "_measurement";
} else {
String columnNameInSnakeCase = camelCaseToSnakeCase(columnName);
if (recordValues.containsKey(columnNameInSnakeCase)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,20 @@ public void camelCaseToSnakeCase() {
Assertions.assertThat(bean.someValue).isEqualTo(20);
}

public static class BigDecimalBean
{
@Test
public void pojoWithMeasurement() {
FluxRecord record = new FluxRecord(0);
record.getValues().put("_measurement", "mem");
record.getValues().put("value", 20);
record.getValues().put("tag", "a");

BeanWithMeasurement bean = mapper.toPOJO(record, BeanWithMeasurement.class);
Assertions.assertThat(bean.customField).isEqualTo("mem");
Assertions.assertThat(bean.tag).isEqualTo("a");
Assertions.assertThat(bean.value).isEqualByComparingTo(new BigDecimal(20));
}

public static class BigDecimalBean {
@Column(name = "value1")
BigDecimal value1;

Expand Down Expand Up @@ -146,4 +158,15 @@ public enum TagEnum {
tagA,
tagB
}

public static class BeanWithMeasurement {
@Column(measurement = true)
String customField;

@Column(name = "tag", tag = true)
String tag;

@Column(name = "value")
BigDecimal value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ internal class ITQueryKotlinApi : AbstractITInfluxDBClientKotlin() {
@BeforeEach
fun `Write testing data`(): Unit = runBlocking {

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

organization = client.organizationsApi
.findOrganizations().stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ void setUp() throws Exception {
influxDB_URL = getInfluxDb2Url();
LOG.log(Level.FINEST, "InfluxDB URL: {0}", influxDB_URL);

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

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

influxDBClient.close();

try {
this.influxDBClient = InfluxDBClientReactiveFactory.create(influxDB_URL, "my-user", "my-password".toCharArray());
} catch (Exception e) {
Assertions.fail("Can't authorize via password", e);
}

this.influxDBClient = InfluxDBClientReactiveFactory.create(influxDB_URL, "my-token".toCharArray());
}

@AfterEach
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ void setUp() throws Exception {

super.setUp();

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

bucket = client.getBucketsApi()
.createBucket(generateName("h2o"), null, organization);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class ITQueryScalaApiQuery extends AbstractITQueryScalaApi with Matchers {

super.setUp()

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import java.lang.reflect.Field;
import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
Expand Down Expand Up @@ -54,32 +55,22 @@ <M> Point toPoint(@Nonnull final M measurement, @Nonnull final WritePrecision pr
Class<?> measurementType = measurement.getClass();
cacheMeasurementClass(measurementType);

if (measurementType.getAnnotation(Measurement.class) == null) {
String message = String
.format("Measurement type '%s' does not have a @Measurement annotation.", measurementType);

throw new InfluxException(message);
}

Point point = Point.measurement(getMeasurementName(measurementType));

CLASS_FIELD_CACHE.get(measurementType.getName()).forEach((name, field) -> {
Point point = Point.measurement(getMeasurementName(measurement, measurementType));

for (Map.Entry<String, Field> entry : CLASS_FIELD_CACHE.get(measurementType.getName()).entrySet()) {
String name = entry.getKey();
Field field = entry.getValue();
Column column = field.getAnnotation(Column.class);

Object value;
try {
field.setAccessible(true);
value = field.get(measurement);
} catch (IllegalAccessException e) {

throw new InfluxException(e);
if (column.measurement()) {
continue;
}

Object value = getObject(measurement, field);

if (value == null) {
Object[] params = {field.getName(), measurement};
LOG.log(Level.FINEST, "Field {0} of {1} has null value", params);
return;
continue;
}

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

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

return point;
}

@Nonnull
private String getMeasurementName(@Nonnull final Class<?> measurementType) {
return measurementType.getAnnotation(Measurement.class).name();
private <M> String getMeasurementName(@Nonnull final M measurement, @Nonnull final Class<?> measurementType) {

// from @Measurement annotation for class
Measurement measurementAnnotation = measurementType.getAnnotation(Measurement.class);
if (measurementAnnotation != null) {
return measurementAnnotation.name();
}

// from Field with @Column(measurement = true)
Field measurementField = CLASS_FIELD_CACHE.get(measurementType.getName())
.values()
.stream()
.filter(field -> field.getAnnotation(Column.class).measurement())
.findFirst()
.orElse(null);

if (measurementField == null) {
String message = String
.format("Unable to determine Measurement for '%s'. Does it have a @Measurement annotation or "
+ "field with @Column(measurement = true) annotation?", measurementType);

throw new InfluxException(message);
}

return getObject(measurement, measurementField).toString();
}

private <M> Object getObject(@Nonnull final M measurement, @Nonnull final Field field) {
Object value;
try {
field.setAccessible(true);
value = field.get(measurement);
} catch (IllegalAccessException e) {

throw new InfluxException(e);
}
return value;
}

private boolean isNumber(@Nonnull final Class<?> fieldType) {
Expand Down
1 change: 1 addition & 0 deletions client/src/test/java/com/influxdb/client/ITUsersApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ void createNewUserAndSetPassword() throws Exception {

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

Assertions.assertThatThrownBy(() -> usersApi.updateUserPassword("020f755c3c082000", "", "new-password"))
Expand Down
4 changes: 3 additions & 1 deletion client/src/test/java/com/influxdb/client/WriteApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,9 @@ void writeMeasurementWhichIsNotMappableToPoint() {
Assertions.assertThat(listener.getValue()).isNotNull();
Assertions.assertThat(listener.getValue().getThrowable())
.isInstanceOf(InfluxException.class)
.hasMessage("Measurement type 'class java.lang.Integer' does not have a @Measurement annotation.");
.hasMessage("Unable to determine Measurement for 'class java.lang.Integer'. "
+ "Does it have a @Measurement annotation or "
+ "field with @Column(measurement = true) annotation?");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,17 @@ void enumTag() {
Assertions.assertThat(lineProtocol).isEqualTo("pojo,tag=tagA num=5i");
}

@Test
void pojoMeasurement() {
PojoMeasurement pojo = new PojoMeasurement();
pojo.tag = "a";
pojo.value = 5;
pojo.customField = "mem";

String lineProtocol = mapper.toPoint(pojo, WritePrecision.S).toLineProtocol();
Assertions.assertThat(lineProtocol).isEqualTo("mem,tag=a value=5i");
}

@Measurement(name = "pojo")
private static class Pojo {

Expand Down Expand Up @@ -155,4 +166,15 @@ private enum TagEnum {
tagA,
tagB
}

public static class PojoMeasurement {
@Column(measurement = true)
String customField;

@Column(name = "tag", tag = true)
private String tag;

@Column(name = "value")
private Integer value;
}
}

0 comments on commit f9f6aa0

Please sign in to comment.