From 4508137e13e0859d9ed9152008b6f9de65c17d7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Tr=C4=99bski?= Date: Mon, 6 Jun 2016 13:48:16 +0200 Subject: [PATCH] SortBy for Hibernate ORM Commits implements sortBy functionality in ORM for: - Alarm repository - AlarmDefinition repository Change-Id: I4c9141cfd313d457481cf2ab3b5d3db319043210 --- .../hibernate/AlarmDefinitionSqlRepoImpl.java | 49 ++++--- .../hibernate/AlarmSqlRepoImpl.java | 137 ++++++++++++++++-- .../AlarmDefinitionSqlRepositoryImplTest.java | 14 +- .../hibernate/AlarmSqlRepositoryImplTest.java | 98 ++++++++----- 4 files changed, 227 insertions(+), 71 deletions(-) diff --git a/java/src/main/java/monasca/api/infrastructure/persistence/hibernate/AlarmDefinitionSqlRepoImpl.java b/java/src/main/java/monasca/api/infrastructure/persistence/hibernate/AlarmDefinitionSqlRepoImpl.java index f2ab69714..a14ed3e71 100644 --- a/java/src/main/java/monasca/api/infrastructure/persistence/hibernate/AlarmDefinitionSqlRepoImpl.java +++ b/java/src/main/java/monasca/api/infrastructure/persistence/hibernate/AlarmDefinitionSqlRepoImpl.java @@ -51,7 +51,6 @@ import monasca.api.domain.exception.EntityNotFoundException; import monasca.api.domain.model.alarmdefinition.AlarmDefinition; import monasca.api.domain.model.alarmdefinition.AlarmDefinitionRepo; import monasca.api.infrastructure.persistence.SubAlarmDefinitionQueries; -import monasca.api.resource.exception.Exceptions; import monasca.common.hibernate.db.AlarmActionDb; import monasca.common.hibernate.db.AlarmDb; import monasca.common.hibernate.db.AlarmDefinitionDb; @@ -79,11 +78,20 @@ public class AlarmDefinitionSqlRepoImpl private static final String SEVERITY = "SEVERITY"; private static final String MATCH_BY = "MATCH_BY"; private static final String ACTIONS_ENABLED = "ACTIONS_ENABLED"; - private static final String STATE = "STATES"; + private static final String STATE = "STATE"; private static final String NOTIFICATION_ID = "NOTIFICATIONIDS"; private static final Joiner COMMA_JOINER = Joiner.on(','); private static final Splitter COMMA_SPLITTER = Splitter.on(',').omitEmptyStrings().trimResults(); private static final Logger logger = LoggerFactory.getLogger(AlarmDefinitionSqlRepoImpl.class); + private static final String FIND_ALARM_DEF_SQL = "SELECT t.id, t.tenant_id, t.name, " + + "t.description, t.expression, t.severity, t.match_by, " + + "t.actions_enabled, aa.alarm_state AS state, aa.action_id AS notificationIds " + + "FROM (SELECT distinct ad.id, ad.tenant_id, ad.name, ad.description, ad.expression, " + + "ad.severity, ad.match_by, ad.actions_enabled, ad.created_at, ad.updated_at, ad.deleted_at " + + "FROM alarm_definition AS ad LEFT OUTER JOIN sub_alarm_definition AS sad ON ad.id = sad.alarm_definition_id " + + "LEFT OUTER JOIN sub_alarm_definition_dimension AS dim ON sad.id = dim.sub_alarm_definition_id %1$s " + + "WHERE ad.tenant_id = :tenantId AND ad.deleted_at IS NULL %2$s ORDER BY ad.id %3$s) AS t " + + "LEFT OUTER JOIN alarm_action AS aa ON t.id = aa.alarm_definition_id %4$s"; @Inject public AlarmDefinitionSqlRepoImpl(@Named("orm") SessionFactory sessionFactory) { @@ -231,27 +239,13 @@ public class AlarmDefinitionSqlRepoImpl List severities, List sortBy, String offset, int limit) { logger.trace(ORM_LOG_MARKER, "find(...) entering..."); - if (sortBy != null && !sortBy.isEmpty()) { - throw Exceptions.unprocessableEntity( - "Sort_by is not implemented for the hibernate database type"); - } Session session = null; List resultSet = Lists.newArrayList(); - // TODO introduce criteria here, will make code significantly better - String query = - " SELECT t.id, t.tenant_id, t.name, t.description, t.expression, t.severity, t.match_by, " - + "t.actions_enabled, aa.alarm_state AS states, aa.action_id AS notificationIds " - + "FROM (SELECT distinct ad.id, ad.tenant_id, ad.name, ad.description, ad.expression, " - + "ad.severity, ad.match_by, ad.actions_enabled, ad.created_at, ad.updated_at, ad.deleted_at " - + "FROM alarm_definition AS ad LEFT OUTER JOIN sub_alarm_definition AS sad ON ad.id = sad.alarm_definition_id " - + "LEFT OUTER JOIN sub_alarm_definition_dimension AS dim ON sad.id = dim.sub_alarm_definition_id %1$s " - + "WHERE ad.tenant_id = :tenantId AND ad.deleted_at IS NULL %2$s ORDER BY ad.id %3$s) AS t " - + "LEFT OUTER JOIN alarm_action AS aa ON t.id = aa.alarm_definition_id ORDER BY t.id, t.created_at"; - - StringBuilder sbWhere = new StringBuilder(); - StringBuilder limitOffset = new StringBuilder(); + final StringBuilder sbWhere = new StringBuilder(); + final StringBuilder limitOffset = new StringBuilder(); + final StringBuilder orderByPart = new StringBuilder(); if (name != null) { sbWhere.append(" and ad.name = :name"); @@ -280,7 +274,22 @@ public class AlarmDefinitionSqlRepoImpl limitOffset.append(" offset :offset "); } - String sql = String.format(query, SubAlarmDefinitionQueries.buildJoinClauseFor(dimensions), sbWhere, limitOffset); + if (sortBy != null && !sortBy.isEmpty()) { + orderByPart.append(" order by ").append(COMMA_JOINER.join(sortBy)); + if (!sortBy.contains("id")) { + orderByPart.append(",id"); + } + } else { + orderByPart.append(" order by id "); + } + + final String sql = String.format( + FIND_ALARM_DEF_SQL, + SubAlarmDefinitionQueries.buildJoinClauseFor(dimensions), + sbWhere, + limitOffset, + orderByPart + ); try { session = sessionFactory.openSession(); diff --git a/java/src/main/java/monasca/api/infrastructure/persistence/hibernate/AlarmSqlRepoImpl.java b/java/src/main/java/monasca/api/infrastructure/persistence/hibernate/AlarmSqlRepoImpl.java index aea1df15f..ace74a1b1 100644 --- a/java/src/main/java/monasca/api/infrastructure/persistence/hibernate/AlarmSqlRepoImpl.java +++ b/java/src/main/java/monasca/api/infrastructure/persistence/hibernate/AlarmSqlRepoImpl.java @@ -27,7 +27,9 @@ import javax.inject.Inject; import javax.inject.Named; import com.google.common.base.Function; +import com.google.common.base.Joiner; import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -47,7 +49,6 @@ import monasca.api.domain.exception.EntityNotFoundException; import monasca.api.domain.model.alarm.Alarm; import monasca.api.domain.model.alarm.AlarmCount; import monasca.api.domain.model.alarm.AlarmRepo; -import monasca.api.resource.exception.Exceptions; import monasca.common.hibernate.db.AlarmDb; import monasca.common.hibernate.db.SubAlarmDb; import monasca.common.hibernate.type.BinaryId; @@ -65,6 +66,10 @@ public class AlarmSqlRepoImpl implements AlarmRepo { private static final Logger logger = LoggerFactory.getLogger(AlarmSqlRepoImpl.class); + private static final Joiner COMMA_JOINER = Joiner.on(","); + private static final Joiner SPACE_JOINER = Joiner.on(" "); + private static final Splitter SPACE_SPLITTER = Splitter.on(" "); + private static final AlarmSortByFunction ALARM_SORT_BY_FUNCTION = new AlarmSortByFunction(); private static final String FIND_ALARM_BY_ID_SQL = "select distinct ad.id as alarm_definition_id, ad.severity, ad.name as alarm_definition_name, " @@ -99,7 +104,7 @@ public class AlarmSqlRepoImpl + "inner join metric_definition as md on md.id = mdd.metric_definition_id " + "left outer join (select dimension_set_id, name, value " + "from metric_dimension group by dimension_set_id, name, value) as mdg on mdg.dimension_set_id = mdd.metric_dimension_set_id " - + "order by a.id ASC"; + + "%s"; @Inject public AlarmSqlRepoImpl(@Named("orm") SessionFactory sessionFactory) { @@ -171,11 +176,6 @@ public class AlarmSqlRepoImpl final boolean enforceLimit) { logger.trace(ORM_LOG_MARKER, "find(...) entering"); - if (sortBy != null && !sortBy.isEmpty()) { - throw Exceptions.unprocessableEntity( - "Sort_by is not implemented for the hibernate database type"); - } - Preconditions.checkNotNull(tenantId, "TenantId is required"); Session session = null; @@ -184,7 +184,9 @@ public class AlarmSqlRepoImpl try { final Query query; - final String sql = String.format(FIND_ALARMS_SQL, this.getFindAlarmsSubQuery( + + final String sortByClause = ALARM_SORT_BY_FUNCTION.apply(sortBy); + final String alarmsSubQuery = this.getFindAlarmsSubQuery( alarmDefId, metricName, metricDimensions, @@ -193,10 +195,13 @@ public class AlarmSqlRepoImpl lifecycleState, link, stateUpdatedStart, + sortBy, offset, limit, enforceLimit - )); + ); + + final String sql = String.format(FIND_ALARMS_SQL, alarmsSubQuery, sortByClause); try { query = new Function(){ @@ -205,7 +210,8 @@ public class AlarmSqlRepoImpl @Override public Query apply(@Nullable final Session input) { assert input != null; - final Query query = input.createSQLQuery(sql); + final Query query = input.createSQLQuery(sql) + .setReadOnly(true); query.setString("tenantId", tenantId); @@ -283,6 +289,7 @@ public class AlarmSqlRepoImpl final String lifecycleState, final String link, final DateTime stateUpdatedStart, + final List sortBy, final String offset, final int limit, final boolean enforceLimit) { @@ -356,8 +363,6 @@ public class AlarmSqlRepoImpl sbWhere.append(" and a.state_updated_at >= :stateUpdatedStart"); } - sbWhere.append(" order by a.id ASC "); - if (enforceLimit && limit > 0) { sbWhere.append(" limit :limit"); } @@ -616,4 +621,112 @@ public class AlarmSqlRepoImpl // Not Implemented return null; } + + private static class AlarmSortByFunction + implements Function, String> { + + static final Map> SORT_BY_TO_COLUMN_ALIAS = Maps.newHashMapWithExpectedSize(10); + + static { + SORT_BY_TO_COLUMN_ALIAS.put("alarm_id", + Lists.newArrayList("a.id")); + SORT_BY_TO_COLUMN_ALIAS.put("alarm_definition_id", + Lists.newArrayList("ad.id")); + SORT_BY_TO_COLUMN_ALIAS.put("alarm_definition_name", + Lists.newArrayList("ad.name")); + SORT_BY_TO_COLUMN_ALIAS.put("created_timestamp", + Lists.newArrayList("a.created_at")); + SORT_BY_TO_COLUMN_ALIAS.put("updated_timestamp", + Lists.newArrayList("a.updated_at")); + SORT_BY_TO_COLUMN_ALIAS.put("state_updated_timestamp", + Lists.newArrayList("a.state_updated_at")); + SORT_BY_TO_COLUMN_ALIAS.put("state", + Lists.newArrayList("a.state='OK'", "a.state='UNDETERMINED'", "a.state='ALARM'")); + SORT_BY_TO_COLUMN_ALIAS.put("severity", + Lists.newArrayList("ad.severity='LOW'", "ad.severity='MEDIUM'", "ad.severity='HIGH'", "ad.severity='CRITICAL'")); + } + + @Nullable + @Override + public String apply(@Nullable final List input) { + final StringBuilder orderClause = new StringBuilder(" ORDER BY "); + + if (CollectionUtils.isEmpty(input)) { + return orderClause.append("a.id ASC ").toString(); + } + + final List sortByElements = Lists.newArrayListWithExpectedSize(input.size()); + boolean alarmIdUsed = false; + + for (final String sortByElement : input) { + final List split = SPACE_SPLITTER.splitToList(sortByElement); + + final String sortAlias = split.get(0); + final String sortOrder = split.size() >= 2 ? split.get(1).toUpperCase() : ""; + + final List columnAlias = SORT_BY_TO_COLUMN_ALIAS.get(sortAlias); + alarmIdUsed = "alarm_id".equals(sortAlias); + + if (columnAlias != null) { + sortByElements.add(new CaseWhenSortClauseFunction(sortOrder).apply(columnAlias)); + } + + } + + if (!alarmIdUsed) { + sortByElements.add("a.id ASC"); + } + + orderClause.append(COMMA_JOINER.join(sortByElements)); + + return orderClause.toString(); + } + + } + + private static final class CaseWhenSortClauseFunction + implements Function, String> { + + private final String sortOrder; + + CaseWhenSortClauseFunction(final String sortOrder) { + this.sortOrder = sortOrder; + } + + @Nullable + @Override + public String apply(@Nullable final List input) { + assert input != null; + if (input.size() == 1) { + return String.format("%s %s", input.get(0), this.sortOrder).trim(); + } else { + + final List builder = Lists.newArrayList("CASE"); + final boolean ascendingOrder = this.isAscendingOrder(); + + for (int it = 0; it < input.size(); it++) { + final String[] parts = input.get(it).split("="); + + final String columnName = parts[0]; + final String columnValue = parts[1]; + final int orderValue = ascendingOrder ? it : input.size() - it - 1; + + builder.add(String.format("WHEN %s=%s THEN %s", + columnName, columnValue, orderValue)); + + } + + builder.add(String.format("ELSE %s", input.size())); + builder.add("END"); + + return SPACE_JOINER.join(builder); + } + } + + private boolean isAscendingOrder() { + return "".equals(this.sortOrder) || "ASC".equals(this.sortOrder); + } + + } + } diff --git a/java/src/test/java/monasca/api/infrastructure/persistence/hibernate/AlarmDefinitionSqlRepositoryImplTest.java b/java/src/test/java/monasca/api/infrastructure/persistence/hibernate/AlarmDefinitionSqlRepositoryImplTest.java index bff3c1c6a..8458aeb69 100644 --- a/java/src/test/java/monasca/api/infrastructure/persistence/hibernate/AlarmDefinitionSqlRepositoryImplTest.java +++ b/java/src/test/java/monasca/api/infrastructure/persistence/hibernate/AlarmDefinitionSqlRepositoryImplTest.java @@ -58,7 +58,6 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.annotation.Nullable; -import javax.ws.rs.WebApplicationException; @Test(groups = "orm") public class AlarmDefinitionSqlRepositoryImplTest { @@ -397,9 +396,16 @@ public class AlarmDefinitionSqlRepositoryImplTest { } - @Test(groups = "orm", expectedExceptions = WebApplicationException.class) - public void shouldFindThrowException() { - repo.find("bob", null, null, null, Arrays.asList("severity", "state"), null, 1); + public void shouldSortBy() { + // null sorts by will sort by ID + this.checkList(repo.find("bob", null, null, null, null, null, 0), + this.alarmDef_123, this.alarmDef_234); + this.checkList(repo.find("bob", null, null, null, Arrays.asList("severity"), null, 0), + this.alarmDef_123, this.alarmDef_234); + this.checkList(repo.find("bob", null, null, null, Arrays.asList("state", "severity"), null, 0), + this.alarmDef_234, this.alarmDef_123); + this.checkList(repo.find("bob", null, null, null, Arrays.asList("name", "state", "severity"), null, 0), + this.alarmDef_234, this.alarmDef_123); } public void shouldFilterBySeverity() { diff --git a/java/src/test/java/monasca/api/infrastructure/persistence/hibernate/AlarmSqlRepositoryImplTest.java b/java/src/test/java/monasca/api/infrastructure/persistence/hibernate/AlarmSqlRepositoryImplTest.java index 609dee20e..4e8c289a5 100644 --- a/java/src/test/java/monasca/api/infrastructure/persistence/hibernate/AlarmSqlRepositoryImplTest.java +++ b/java/src/test/java/monasca/api/infrastructure/persistence/hibernate/AlarmSqlRepositoryImplTest.java @@ -347,66 +347,61 @@ public class AlarmSqlRepositoryImplTest { @Test(groups = "orm") public void shouldFind() { - checkList(repo.find(TENANT_ID, null, null, null, null, null, null, null, null, null, null, 1, true), alarm1, alarm2); - checkList(repo.find(TENANT_ID, null, null, null, null, null, null, null, null, null, null, 2, true), alarm1, alarm2, compoundAlarm); - checkList(repo.find(TENANT_ID, null, null, null, null, null, null, null, null, null, "1", 1, true), alarm2, compoundAlarm); - checkList(repo.find(TENANT_ID, null, null, null, null, null, null, null, null, null, "2", 1, true), compoundAlarm, alarm3); - checkList(repo.find(TENANT_ID, null, null, null, null, null, null, null, null, null, "3", 1, true), alarm3); + checkUnsortedList(repo.find(TENANT_ID, null, null, null, null, null, null, null, null, null, null, 1, true), alarm1, alarm2); + checkUnsortedList(repo.find(TENANT_ID, null, null, null, null, null, null, null, null, null, null, 2, true), alarm1, alarm2, alarm3); + checkUnsortedList(repo.find(TENANT_ID, null, null, null, null, null, null, null, null, null, "1", 1, true), alarm2, alarm3); + checkUnsortedList(repo.find(TENANT_ID, null, null, null, null, null, null, null, null, null, "2", 1, true), alarm3, compoundAlarm); + checkUnsortedList(repo.find(TENANT_ID, null, null, null, null, null, null, null, null, null, "3", 1, true), compoundAlarm); - checkList(repo.find("Not a tenant id", null, null, null, null, null, null, null, null, null, null, 1, false)); + checkUnsortedList(repo.find("Not a tenant id", null, null, null, null, null, null, null, null, null, null, 1, false)); - checkList(repo.find(TENANT_ID, null, null, null, null, null, null, null, null, null, null, 1, false), alarm1, alarm2, alarm3, compoundAlarm); + checkUnsortedList(repo.find(TENANT_ID, null, null, null, null, null, null, null, null, null, null, 1, false), alarm1, alarm2, alarm3, compoundAlarm); - checkList(repo.find(TENANT_ID, compoundAlarm.getAlarmDefinition().getId(), null, null, null, null, null, null, null, null, null, 1, false), compoundAlarm); + checkUnsortedList(repo.find(TENANT_ID, compoundAlarm.getAlarmDefinition().getId(), null, null, null, null, null, null, null, null, null, 1, false), compoundAlarm); - checkList(repo.find(TENANT_ID, null, "cpu.sys_mem", null, null, null, null, null, null, null, null, 1, false), compoundAlarm); + checkUnsortedList(repo.find(TENANT_ID, null, "cpu.sys_mem", null, null, null, null, null, null, null, null, 1, false), compoundAlarm); - checkList(repo.find(TENANT_ID, null, "cpu.idle_perc", null, null, null, null, null, null, null, null, 1, false), alarm1, alarm2, alarm3, compoundAlarm); + checkUnsortedList(repo.find(TENANT_ID, null, "cpu.idle_perc", null, null, null, null, null, null, null, null, 1, false), alarm1, alarm2, alarm3, compoundAlarm); - checkList(repo.find(TENANT_ID, null, "cpu.idle_perc", ImmutableMap.builder().put("flavor_id", "222").build(), null, null, null, null, + checkUnsortedList(repo.find(TENANT_ID, null, "cpu.idle_perc", ImmutableMap.builder().put("flavor_id", "222").build(), null, null, null, null, null, null, null, 1, false), alarm1, alarm3); - checkList( + checkUnsortedList( repo.find(TENANT_ID, null, "cpu.idle_perc", ImmutableMap.builder().put("service", "monitoring").put("hostname", "roland") .build(), null, null, null, null, null, null, null, 1, false), compoundAlarm); - checkList(repo.find(TENANT_ID, null, null, null, AlarmState.UNDETERMINED, null, null, null, null, null, null, 1, false), alarm2, compoundAlarm); + checkUnsortedList(repo.find(TENANT_ID, null, null, null, AlarmState.UNDETERMINED, null, null, null, null, null, null, 1, false), alarm2, compoundAlarm); - checkList( + checkUnsortedList( repo.find(TENANT_ID, alarm1.getAlarmDefinition().getId(), "cpu.idle_perc", ImmutableMap.builder() .put("service", "monitoring").build(), null, null, null, null, null, null, null, 1, false), alarm1, alarm2); - checkList(repo.find(TENANT_ID, alarm1.getAlarmDefinition().getId(), "cpu.idle_perc", null, null, null, null, null, null, null, null, 1, false), alarm1, + checkUnsortedList(repo.find(TENANT_ID, alarm1.getAlarmDefinition().getId(), "cpu.idle_perc", null, null, null, null, null, null, null, null, 1, false), alarm1, alarm2, alarm3); - checkList( + checkUnsortedList( repo.find(TENANT_ID, compoundAlarm.getAlarmDefinition().getId(), null, null, AlarmState.UNDETERMINED, null, null, null, null, null, null, 1, false), compoundAlarm); - checkList(repo.find(TENANT_ID, null, "cpu.sys_mem", null, AlarmState.UNDETERMINED, null, null, null, null, null, null, 1, false), compoundAlarm); + checkUnsortedList(repo.find(TENANT_ID, null, "cpu.sys_mem", null, AlarmState.UNDETERMINED, null, null, null, null, null, null, 1, false), compoundAlarm); - checkList(repo.find(TENANT_ID, null, "cpu.idle_perc", ImmutableMap.builder().put("service", "monitoring").build(), + checkUnsortedList(repo.find(TENANT_ID, null, "cpu.idle_perc", ImmutableMap.builder().put("service", "monitoring").build(), AlarmState.UNDETERMINED, null, null, null, null, null, null, 1, false), alarm2, compoundAlarm); - checkList( + checkUnsortedList( repo.find(TENANT_ID, alarm1.getAlarmDefinition().getId(), "cpu.idle_perc", ImmutableMap.builder() .put("service", "monitoring").build(), AlarmState.UNDETERMINED, null, null, null, null, null, null, 1, false), alarm2); - checkList(repo.find(TENANT_ID, null, null, null, null, null, null, null, DateTime.now(UTC_TIMEZONE), null, null, 0, false)); + checkUnsortedList(repo.find(TENANT_ID, null, null, null, null, null, null, null, DateTime.now(UTC_TIMEZONE), null, null, 0, false)); -// checkList(repo.find(TENANT_ID, null, null, null, null, null, null, ISO_8601_FORMATTER.parseDateTime("2015-03-15T00:00:00Z"), null, 0, false), +// checkUnsortedList(repo.find(TENANT_ID, null, null, null, null, null, null, ISO_8601_FORMATTER.parseDateTime("2015-03-15T00:00:00Z"), null, 0, false), // compoundAlarm); - checkList(repo.find(TENANT_ID, null, null, null, null, null, null, null, ISO_8601_FORMATTER.parseDateTime("2015-03-14T00:00:00Z"), null, null, 1, false), + checkUnsortedList(repo.find(TENANT_ID, null, null, null, null, null, null, null, ISO_8601_FORMATTER.parseDateTime("2015-03-14T00:00:00Z"), null, null, 1, false), alarm1, alarm2, alarm3, compoundAlarm); } - @Test(groups = "orm", expectedExceptions = WebApplicationException.class) - public void shouldFindThrowException() { - repo.find(TENANT_ID, null, null, null, null, null, null, null, null, Arrays.asList("severity", "state"), null, 1, true); - } - @Test(groups = "orm") public void shouldFindById() { @@ -465,28 +460,57 @@ public class AlarmSqlRepositoryImplTest { repo.update(TENANT_ID, "Not a valid alarm id", AlarmState.UNDETERMINED, null, null); } + @Test(groups = "orm") public void shouldFilterBySeverity() { - checkList(repo.find(TENANT_ID, null, null, null, null, Lists.newArrayList(AlarmSeverity.LOW), null, null, null, null, null, 1, false), + checkUnsortedList(repo.find(TENANT_ID, null, null, null, null, Lists.newArrayList(AlarmSeverity.LOW), null, null, null, null, null, 1, false), alarm1, alarm2, alarm3); - checkList(repo.find(TENANT_ID, null, null, null, null, Lists.newArrayList(AlarmSeverity.HIGH), null, null, null, null, null, 1, false), + checkUnsortedList(repo.find(TENANT_ID, null, null, null, null, Lists.newArrayList(AlarmSeverity.HIGH), null, null, null, null, null, 1, false), compoundAlarm); - checkList(repo.find(TENANT_ID, null, null, null, null, Lists.newArrayList(AlarmSeverity.LOW, AlarmSeverity.HIGH), null, null, null, null, null, 1, false), + checkUnsortedList(repo.find(TENANT_ID, null, null, null, null, Lists.newArrayList(AlarmSeverity.LOW, AlarmSeverity.HIGH), null, null, null, null, null, 1, false), alarm1, alarm2, compoundAlarm, alarm3); // no alarms for those severities - checkList(repo.find(TENANT_ID, null, null, null, null, Lists.newArrayList(AlarmSeverity.CRITICAL), null, null, null, null, null, 1, false)); - checkList(repo.find(TENANT_ID, null, null, null, null, Lists.newArrayList(AlarmSeverity.MEDIUM), null, null, null, null, null, 1, false)); - checkList(repo.find(TENANT_ID, null, null, null, null, Lists.newArrayList(AlarmSeverity.CRITICAL, AlarmSeverity.MEDIUM), null, null, null, null, null, 1, false)); + checkUnsortedList(repo.find(TENANT_ID, null, null, null, null, Lists.newArrayList(AlarmSeverity.CRITICAL), null, null, null, null, null, 1, false)); + checkUnsortedList(repo.find(TENANT_ID, null, null, null, null, Lists.newArrayList(AlarmSeverity.MEDIUM), null, null, null, null, null, 1, false)); + checkUnsortedList(repo.find(TENANT_ID, null, null, null, null, Lists.newArrayList(AlarmSeverity.CRITICAL, AlarmSeverity.MEDIUM), null, null, null, null, null, 1, false)); } - private void checkList(List found, Alarm... expected) { + @Test(groups = "orm") + public void shouldSortBy() { + checkSortedList(repo.find(TENANT_ID, null, null, null, null, null, null, null, null, Lists.newArrayList("state", "severity"), null, 1, false), + alarm1, alarm2, compoundAlarm, alarm3); + checkSortedList(repo.find(TENANT_ID, null, null, null, null, null, null, null, null, Lists.newArrayList("state desc", "severity"), null, 1, false), + alarm3, alarm2, compoundAlarm, alarm1); + checkSortedList(repo.find(TENANT_ID, null, null, null, null, null, null, null, null, Lists.newArrayList("state desc", "severity asc"), null, 1, false), + alarm3, alarm2, compoundAlarm, alarm1); + checkSortedList(repo.find(TENANT_ID, null, null, null, null, null, null, null, null, Lists.newArrayList("state desc", "severity desc"), null, 1, false), + alarm3, compoundAlarm, alarm2, alarm1); + checkSortedList(repo.find(TENANT_ID, null, null, null, null, null, null, null, null, Lists.newArrayList("severity"), null, 1, false), + alarm1, alarm2, alarm3, compoundAlarm); + checkSortedList(repo.find(TENANT_ID, null, null, null, null, null, null, null, null, Lists.newArrayList("severity desc", "alarm_id desc"), null, 1, false), + compoundAlarm, alarm3, alarm2, alarm1); + } + + + + private void checkUnsortedList(List found, Alarm... expected) { + this.checkUnsortedList(found, false, expected); + } + + private void checkSortedList(List found, Alarm... expected) { + this.checkUnsortedList(found, true, expected); + } + + private void checkUnsortedList(List found, boolean sorted, Alarm... expected) { assertEquals(found.size(), expected.length); Alarm actual; + int actualIndex; - for (final Alarm alarm : expected) { + for (int expectedIndex = 0; expectedIndex < expected.length; expectedIndex++) { + final Alarm alarm = expected[expectedIndex]; final Optional alarmOptional = FluentIterable .from(found) .firstMatch(new Predicate() { @@ -499,6 +523,10 @@ public class AlarmSqlRepositoryImplTest { assertTrue(alarmOptional.isPresent()); actual = alarmOptional.get(); + if (sorted) { + actualIndex = found.indexOf(actual); + assertEquals(expectedIndex, actualIndex); + } assertEquals(actual, alarm, String.format("%s not equal to %s", actual, alarm)); }