diff --git a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/hibernate/IntArrayType.java b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/hibernate/IntArrayType.java index 147ecd7a1..8addb55c7 100644 --- a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/hibernate/IntArrayType.java +++ b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/hibernate/IntArrayType.java @@ -1,10 +1,117 @@ package io.hyperfoil.tools.horreum.hibernate; -import org.hibernate.type.BasicTypeReference; +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.type.CustomType; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.spi.TypeConfiguration; +import org.hibernate.usertype.UserType; -public class IntArrayType { +import java.io.Serializable; +import java.sql.Array; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.IntStream; - public static final BasicTypeReference INT_ARRAY = new BasicTypeReference("int-array", int[].class, 666); +import static java.lang.String.format; +public class IntArrayType implements UserType { + + public static final CustomType INSTANCE = new CustomType<>(new IntArrayType(), new TypeConfiguration()); + @Override + public int getSqlType() { + return SqlTypes.ARRAY; + } + + @Override + public Class returnedClass() { + return int[].class; + } + + @Override + public boolean equals(int[] x, int[] y) { + return Arrays.equals(x, y); + } + + @Override + public int hashCode(int[] x) { + return Objects.hashCode(x); + } + + @Override + public int[] nullSafeGet(ResultSet rs, int position, SharedSessionContractImplementor session, Object owner) + throws SQLException { + if(rs.wasNull()) + return null; + Array array = rs.getArray(position); + if (array == null) { + return null; + } + try { + return (int[]) array.getArray(); + } catch (final Exception ex) { + throw new RuntimeException("Failed to convert ResultSet to int[]: " + ex.getMessage(), ex); + } + } + + @Override + public void nullSafeSet(PreparedStatement ps, int[] value, int index, SharedSessionContractImplementor session) + throws SQLException { + if (value == null) { + ps.setNull(index, Types.ARRAY); + return; + } + try { + Integer[] castArray = IntStream.of(value).boxed().toArray( Integer[]::new ); + Array array = ps.getConnection().createArrayOf("integer", castArray); + ps.setArray(index, array); + } catch (final Exception ex) { + throw new RuntimeException(format("Failed to convert JSON to String: %s", ex.getMessage()), ex); + } + } + + @Override + public int[] deepCopy(int[] value) throws HibernateException { + if (value == null) { + return null; + } + return Arrays.copyOf(value, value.length); + } + + @Override + public boolean isMutable() { + return true; + } + + @Override + public Serializable disassemble(int[] value) throws HibernateException { + return (Serializable) this.deepCopy(value); + } + + @Override + public int[] assemble(Serializable cached, Object owner) throws HibernateException { + String stringArray = cached.toString().replaceAll("[\\[\\]]", ""); + String[] tokens = stringArray.split(","); + + int length = tokens.length; + int[] array = new int[length]; + for (int i = 0; i < tokens.length; i++) { + array[i] = Integer.valueOf(tokens[i]); + } + return array; + } + + @Override + public int[] replace(int[] original, int[] target, Object owner) throws HibernateException { + return original; + } + + public String getName() { + return "int-array"; + } } diff --git a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/AlertingServiceImpl.java b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/AlertingServiceImpl.java index 5fd3bbc7e..b38fd44b2 100644 --- a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/AlertingServiceImpl.java +++ b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/AlertingServiceImpl.java @@ -1032,7 +1032,7 @@ public List findLastDatapoints(LastDatapointsParams para Query query = em.createNativeQuery(FIND_LAST_DATAPOINTS) .unwrap(NativeQuery.class) .setParameter(1, Util.parseFingerprint(params.fingerprint), JsonBinaryType.INSTANCE) - .setParameter(2, params.variables, IntArrayType.INT_ARRAY); + .setParameter(2, params.variables, IntArrayType.INSTANCE); SqlServiceImpl.setResultTransformer(query, Transformers.aliasToBean(DatapointLastTimestamp.class)); //noinspection unchecked return query.getResultList(); diff --git a/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/AlertingServiceTest.java b/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/AlertingServiceTest.java index b3faf0fe4..006448681 100644 --- a/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/AlertingServiceTest.java +++ b/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/AlertingServiceTest.java @@ -8,6 +8,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import java.io.IOException; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; @@ -23,6 +24,7 @@ import io.hyperfoil.tools.horreum.api.alerting.DataPoint; import io.hyperfoil.tools.horreum.api.alerting.RunExpectation; import io.hyperfoil.tools.horreum.api.data.DataSet; +import io.hyperfoil.tools.horreum.api.services.ExperimentService; import io.hyperfoil.tools.horreum.bus.MessageBusChannels; import jakarta.inject.Inject; @@ -739,6 +741,18 @@ public void testRandomOrder(TestInfo info) throws InterruptedException { checkChanges(test); } + @org.junit.jupiter.api.Test + public void testFindLastDatapoints(TestInfo info) throws IOException { + populateDataFromFiles(); + AlertingService.LastDatapointsParams params = new AlertingService.LastDatapointsParams(); + params.fingerprint = mapper.valueToTree("{ \"fingerprint\":\"benchmark_test\"}").asText(); + params.variables = new int[] {1,2,3}; + + List timestamps = + jsonRequest().body(params).post("/api/alerting/datapoint/last") + .then().statusCode(200).extract().body().as(new ParameterizedTypeImpl(List.class, AlertingService.DatapointLastTimestamp.class)); + } + private void checkChanges(Test test) { List list = ChangeDAO.list("variable.testId", test.id); assertEquals(Arrays.asList(1L, 4L, 6L, 7L, 9L), @@ -761,4 +775,4 @@ private void drainQueue(BlockingQueue changeQueue) throws Interrup } } } -} \ No newline at end of file +}