diff --git a/src/main/java/li/strolch/persistence/postgresql/PostgreSqlAuditDao.java b/src/main/java/li/strolch/persistence/postgresql/PostgreSqlAuditDao.java index 95120e07e..c56c7cf1b 100644 --- a/src/main/java/li/strolch/persistence/postgresql/PostgreSqlAuditDao.java +++ b/src/main/java/li/strolch/persistence/postgresql/PostgreSqlAuditDao.java @@ -33,13 +33,26 @@ import li.strolch.model.audit.AuditVisitor; import li.strolch.persistence.api.AuditDao; import li.strolch.persistence.api.StrolchPersistenceException; import ch.eitchnet.utils.collections.DateRange; +import ch.eitchnet.utils.helper.StringHelper; /** * @author Robert von Burg */ public class PostgreSqlAuditDao implements AuditDao { - public static final String FIELDS = "id, username, firstname, lastname, date, element_type, element_accessed, new_version, action, access_type"; + public static final String ID = "id"; + public static final String ACCESS_TYPE = "access_type"; + public static final String ACCESS_TYPE_TYPE = "::access_type"; + public static final String ACTION = "action"; + public static final String NEW_VERSION = "new_version"; + public static final String ELEMENT_ACCESSED = "element_accessed"; + public static final String ELEMENT_TYPE = "element_type"; + public static final String DATE = "date"; + public static final String LASTNAME = "lastname"; + public static final String FIRSTNAME = "firstname"; + public static final String USERNAME = "username"; + public static final String FIELDS = StringHelper.commaSeparated(ID, USERNAME, FIRSTNAME, LASTNAME, DATE, + ELEMENT_TYPE, ELEMENT_ACCESSED, NEW_VERSION, ACTION, ACCESS_TYPE); public static final String TABLE_NAME = "audits"; private PostgreSqlStrolchTransaction tx; @@ -53,7 +66,7 @@ public class PostgreSqlAuditDao implements AuditDao { @Override public boolean hasElement(String type, Long id) { - String sql = "select count(*) from " + TABLE_NAME + " where element_type = ? and id = ?"; //$NON-NLS-1$ + String sql = "select count(*) from " + TABLE_NAME + " where " + ELEMENT_TYPE + " = ? and " + ID + " = ?"; //$NON-NLS-1$ try (PreparedStatement statement = this.tx.getConnection().prepareStatement(sql)) { statement.setString(1, type); @@ -78,7 +91,7 @@ public class PostgreSqlAuditDao implements AuditDao { @Override public long querySize(DateRange dateRange) { - String sql = "select count(*) from " + TABLE_NAME + " where date between ? and ?"; //$NON-NLS-1$ + String sql = "select count(*) from " + TABLE_NAME + " where " + DATE + " between ? and ?"; //$NON-NLS-1$ try (PreparedStatement statement = this.tx.getConnection().prepareStatement(sql)) { statement.setDate(1, new Date(dateRange.getFromDate().getTime()), Calendar.getInstance()); @@ -96,7 +109,7 @@ public class PostgreSqlAuditDao implements AuditDao { @Override public long querySize(String type, DateRange dateRange) { - String sql = "select count(*) from " + TABLE_NAME + " where element_type = ? and date between ? and ?"; //$NON-NLS-1$ + String sql = "select count(*) from " + TABLE_NAME + " where " + ELEMENT_TYPE + " = ? and " + DATE + " between ? and ?"; //$NON-NLS-1$ try (PreparedStatement statement = this.tx.getConnection().prepareStatement(sql)) { statement.setString(1, type); @@ -117,11 +130,11 @@ public class PostgreSqlAuditDao implements AuditDao { public Set queryTypes() { Set keySet = new HashSet<>(); - String sql = "select distinct element_type from " + TABLE_NAME; //$NON-NLS-1$ + String sql = "select distinct " + ELEMENT_TYPE + " from " + TABLE_NAME; //$NON-NLS-1$ try (PreparedStatement statement = this.tx.getConnection().prepareStatement(sql)) { try (ResultSet result = statement.executeQuery()) { while (result.next()) { - keySet.add(result.getString("element_type")); //$NON-NLS-1$ + keySet.add(result.getString(ELEMENT_TYPE)); //$NON-NLS-1$ } } } catch (SQLException e) { @@ -134,7 +147,7 @@ public class PostgreSqlAuditDao implements AuditDao { @Override public Audit queryBy(String type, Long id) { - String sql = "select " + FIELDS + " from " + TABLE_NAME + " where element_type = ? and id = ?"; //$NON-NLS-1$ + String sql = "select " + FIELDS + " from " + TABLE_NAME + " where " + ELEMENT_TYPE + " = ? and " + ID + " = ?"; //$NON-NLS-1$ try (PreparedStatement statement = this.tx.getConnection().prepareStatement(sql)) { statement.setString(1, type); @@ -157,7 +170,7 @@ public class PostgreSqlAuditDao implements AuditDao { @Override public List queryAll(String type, DateRange dateRange) { List list = new ArrayList<>(); - String sql = "select " + FIELDS + " from " + TABLE_NAME + " where element_type = ? and date between ? and ?"; //$NON-NLS-1$ + String sql = "select " + FIELDS + " from " + TABLE_NAME + " where " + ELEMENT_TYPE + " = ? and " + DATE + " between ? and ?"; //$NON-NLS-1$ try (PreparedStatement statement = this.tx.getConnection().prepareStatement(sql)) { statement.setString(1, type); @@ -205,7 +218,9 @@ public class PostgreSqlAuditDao implements AuditDao { @Override public void update(Audit audit) { - String sql = "update " + TABLE_NAME + " set id = ?, username = ?, firstname = ?, lastname = ?, date = ?, element_type = ?, element_accessed = ?, new_version = ?, action = ?, access_type = ?::access_type where id = ?"; //$NON-NLS-1$ + String sql = "update " + TABLE_NAME + " set " + ID + " = ?, " + USERNAME + " = ?, " + FIRSTNAME + " = ?, " + + LASTNAME + " = ?, " + DATE + " = ?, " + ELEMENT_TYPE + " = ?, " + ELEMENT_ACCESSED + " = ?, " + + NEW_VERSION + " = ?, " + ACTION + " = ?, " + ACCESS_TYPE + " = ?::access_type where " + ID + " = ?"; //$NON-NLS-1$ try (PreparedStatement preparedStatement = this.tx.getConnection().prepareStatement(sql)) { setAuditFields(audit, preparedStatement); @@ -232,7 +247,7 @@ public class PostgreSqlAuditDao implements AuditDao { @Override public void remove(Audit audit) { - String sql = "delete from " + TABLE_NAME + " where id = ?"; //$NON-NLS-1$ + String sql = "delete from " + TABLE_NAME + " where " + ID + " = ?"; //$NON-NLS-1$ try (PreparedStatement preparedStatement = this.tx.getConnection().prepareStatement(sql)) { preparedStatement.setLong(1, audit.getId()); @@ -259,7 +274,7 @@ public class PostgreSqlAuditDao implements AuditDao { @Override public long removeAll(String type, DateRange dateRange) { - String sql = "delete from " + TABLE_NAME + " where element_type = ? and date between ? and ?"; //$NON-NLS-1$ + String sql = "delete from " + TABLE_NAME + " where " + ELEMENT_TYPE + " = ? and " + DATE + " between ? and ?"; //$NON-NLS-1$ try (PreparedStatement preparedStatement = this.tx.getConnection().prepareStatement(sql)) { preparedStatement.setString(1, type); diff --git a/src/main/java/li/strolch/persistence/postgresql/PostgreSqlAuditQueryVisitor.java b/src/main/java/li/strolch/persistence/postgresql/PostgreSqlAuditQueryVisitor.java index 84290e954..c43d91d48 100644 --- a/src/main/java/li/strolch/persistence/postgresql/PostgreSqlAuditQueryVisitor.java +++ b/src/main/java/li/strolch/persistence/postgresql/PostgreSqlAuditQueryVisitor.java @@ -24,7 +24,6 @@ import li.strolch.model.audit.AccessType; import li.strolch.model.audit.ActionSelection; import li.strolch.model.audit.AuditQuery; import li.strolch.model.audit.AuditQueryVisitor; -import li.strolch.model.audit.DateRangeSelection; import li.strolch.model.audit.ElementSelection; import li.strolch.model.audit.IdentitySelection; import li.strolch.model.query.StringSelection; @@ -63,31 +62,28 @@ public class PostgreSqlAuditQueryVisitor implements AuditQueryVisitor { return sqlAsString; this.sql.append("\nwhere\n"); - this.sql.append(this.indent); this.sql.append(this.sb.toString()); sqlAsString = this.sql.toString(); return sqlAsString; } @Override - public void visit(ElementSelection selection) { - if (selection.isWildcard()) - return; - - if (!selection.isElementsAccessedWildcard()) { - StringSelection sel = selection.getElementAccessedSelection(); - toSql(sel.getMatchMode(), sel.getValues()); - } - - if (!selection.isElementTypesWildcard()) { - StringSelection sel = selection.getElementTypeSelection(); - toSql(sel.getMatchMode(), sel.getValues()); - } + public void visit(AuditQuery auditQuery) { + ensureAnd(); + this.sb.append(this.indent); + this.sb.append(PostgreSqlAuditDao.ELEMENT_TYPE); + this.sb.append(" = ?\n"); + ensureAnd(); + this.values.add(auditQuery.getElementTypeSelection()); + PostgreSqlHelper.toSql(this.indent, this.sb, this.values, PostgreSqlAuditDao.DATE, auditQuery.getDateRange()); } - private void toSql(StringMatchMode mm, String[] values) { - this.sb.append(this.indent); - this.sb.append(PostgreSqlHelper.toSql(this.indent, mm, this.values, values)); + @Override + public void visit(ElementSelection selection) { + if (!selection.isElementsAccessedWildcard()) { + StringSelection sel = selection.getElementAccessedSelection(); + toSql(PostgreSqlAuditDao.ELEMENT_ACCESSED, sel.getMatchMode(), sel.getValues()); + } } @Override @@ -97,59 +93,68 @@ public class PostgreSqlAuditQueryVisitor implements AuditQueryVisitor { if (!selection.isFirstnameWildcard()) { StringSelection sel = selection.getFirstnameSelection(); - toSql(sel.getMatchMode(), sel.getValues()); + toSql(PostgreSqlAuditDao.FIRSTNAME, sel.getMatchMode(), sel.getValues()); } if (!selection.isLastnameWildcard()) { StringSelection sel = selection.getLastnameSelection(); - toSql(sel.getMatchMode(), sel.getValues()); + toSql(PostgreSqlAuditDao.LASTNAME, sel.getMatchMode(), sel.getValues()); } if (!selection.isUsernameWildcard()) { StringSelection sel = selection.getUsernameSelection(); - toSql(sel.getMatchMode(), sel.getValues()); + toSql(PostgreSqlAuditDao.USERNAME, sel.getMatchMode(), sel.getValues()); } } - @Override - public void visit(DateRangeSelection selection) { - - // TODO Auto-generated method stub - - } - @Override public void visit(ActionSelection selection) { if (!selection.isWildcardAction()) { StringSelection sel = selection.getActionSelection(); - toSql(sel.getMatchMode(), sel.getValues()); + toSql(PostgreSqlAuditDao.ACTION, sel.getMatchMode(), sel.getValues()); } if (!selection.isWildcardActionType()) { AccessType[] accessTypes = selection.getAccessTypes(); - String[] query = new String[accessTypes.length]; - for (int i = 0; i < accessTypes.length; i++) { - query[i] = accessTypes[i].name(); + ensureAnd(); + this.sb.append(this.indent); + if (accessTypes.length == 1) { + sb.append(PostgreSqlAuditDao.ACCESS_TYPE + " = ?"); + sb.append(PostgreSqlAuditDao.ACCESS_TYPE_TYPE); + sb.append("\n"); + this.values.add(accessTypes[0].name()); + } else { + sb.append(PostgreSqlAuditDao.ACCESS_TYPE + " in ("); + for (int i = 0; i < accessTypes.length; i++) { + sb.append("?"); + sb.append(PostgreSqlAuditDao.ACCESS_TYPE_TYPE); + values.add(accessTypes[i].name()); + if (i < accessTypes.length - 1) + sb.append(", "); + } + sb.append(" )\n"); + sb.append("\n"); } - - toSql(StringMatchMode.EQUALS_CASE_SENSITIVE, query); } } - @Override - public void visit(AuditQuery auditQuery) { - // TODO Auto-generated method stub - + private void toSql(String column, StringMatchMode mm, String[] values) { + ensureAnd(); + this.sb.append(this.indent); + this.sb.append(PostgreSqlHelper.toSql(column, this.indent, mm, this.values, values)); } - /** - * @param ps - * @throws SQLException - */ public void setValues(PreparedStatement ps) throws SQLException { for (int i = 0; i < this.values.size(); i++) { ps.setObject(i + 1, this.values.get(i)); } } + + private void ensureAnd() { + if (this.sb.length() > 0) { + this.sb.append(this.indent); + this.sb.append("and \n"); + } + } } diff --git a/src/main/java/li/strolch/persistence/postgresql/PostgreSqlHelper.java b/src/main/java/li/strolch/persistence/postgresql/PostgreSqlHelper.java index 9bcec7a27..b552d9e2d 100644 --- a/src/main/java/li/strolch/persistence/postgresql/PostgreSqlHelper.java +++ b/src/main/java/li/strolch/persistence/postgresql/PostgreSqlHelper.java @@ -15,17 +15,47 @@ */ package li.strolch.persistence.postgresql; +import java.sql.Date; import java.util.ArrayList; import java.util.List; import ch.eitchnet.utils.StringMatchMode; +import ch.eitchnet.utils.collections.DateRange; /** * @author Robert von Burg */ public class PostgreSqlHelper { - public static String toSql(String indent, StringMatchMode mm, List values, String... query) { + public static void toSql(String indent, StringBuilder sb, List values, String column, DateRange dateRange) { + + // TODO handle inclusive/exclusive: between is inclusive + + if (dateRange.isDate()) { + sb.append(indent); + sb.append(column); + sb.append(" = ?\n"); + values.add(new Date(dateRange.getFromDate().getTime())); + } else if (dateRange.isBounded()) { + sb.append(indent); + sb.append(column); + sb.append(" between ? and ?\n"); + values.add(new Date(dateRange.getFromDate().getTime())); + values.add(new Date(dateRange.getToDate().getTime())); + } else if (dateRange.isToBounded()) { + sb.append(indent); + sb.append(column); + sb.append(" <= ?\n"); + values.add(new Date(dateRange.getToDate().getTime())); + } else if (dateRange.isFromBounded()) { + sb.append(indent); + sb.append(column); + sb.append(" >= ?\n"); + values.add(new Date(dateRange.getFromDate().getTime())); + } + } + + public static String toSql(String column, String indent, StringMatchMode mm, List values, String... query) { // CS EQ // 1. x x @@ -36,10 +66,10 @@ public class PostgreSqlHelper { StringBuilder sb = new StringBuilder(); if (mm.isCaseSensitve() && mm.isEquals()) { if (query.length == 1) { - sb.append("name = ?\n"); + sb.append(column + " = ?\n"); values.add(query[0]); } else { - sb.append("name in ( "); + sb.append(column + " in ( "); for (int i = 0; i < query.length; i++) { sb.append("?"); values.add(query[i]); @@ -50,10 +80,10 @@ public class PostgreSqlHelper { } } else if (!mm.isCaseSensitve() && mm.isEquals()) { if (query.length == 1) { - sb.append("lower(name) = ?\n"); + sb.append("lower(" + column + ") = ?\n"); values.add(query[0].toLowerCase()); } else { - sb.append("lower(name) in ( "); + sb.append("lower(" + column + ") in ( "); for (int i = 0; i < query.length; i++) { sb.append("?"); values.add(query[i].toLowerCase()); @@ -64,14 +94,14 @@ public class PostgreSqlHelper { } } else if (!mm.isEquals() && mm.isCaseSensitve()) { if (query.length == 1) { - sb.append("name like ?\n"); + sb.append(column + " like ?\n"); values.add("%" + query[0] + "%"); } else { sb.append("(\n"); for (int i = 0; i < query.length; i++) { sb.append(indent); sb.append(" "); - sb.append("name like ?"); + sb.append(column + " like ?"); values.add("%" + query[i] + "%"); if (i < query.length - 1) sb.append(" or"); @@ -81,14 +111,14 @@ public class PostgreSqlHelper { } } else { if (query.length == 1) { - sb.append("lower(name) like ?\n"); + sb.append("lower(" + column + ") like ?\n"); values.add("%" + query[0].toLowerCase() + "%"); } else { sb.append("(\n"); for (int i = 0; i < query.length; i++) { sb.append(indent); sb.append(" "); - sb.append("lower(name) like ?"); + sb.append("lower(" + column + ") like ?"); values.add("%" + query[i].toLowerCase() + "%"); if (i < query.length - 1) sb.append(" or"); @@ -103,35 +133,35 @@ public class PostgreSqlHelper { public static void main(String[] args) { ArrayList values = new ArrayList<>(); - String sql = toSql(" ", StringMatchMode.CONTAINS_CASE_INSENSITIVE, values, "foo", "bar", "fub"); + String sql = toSql("name", " ", StringMatchMode.CONTAINS_CASE_INSENSITIVE, values, "foo", "bar", "fub"); System.out.println(sql); System.out.println(); - sql = toSql(" ", StringMatchMode.CONTAINS_CASE_INSENSITIVE, values, "foo"); + sql = toSql("name", " ", StringMatchMode.CONTAINS_CASE_INSENSITIVE, values, "foo"); System.out.println(sql); System.out.println(); - sql = toSql(" ", StringMatchMode.CONTAINS_CASE_SENSITIVE, values, "foo", "bar", "fub"); + sql = toSql("name", " ", StringMatchMode.CONTAINS_CASE_SENSITIVE, values, "foo", "bar", "fub"); System.out.println(sql); System.out.println(); - sql = toSql(" ", StringMatchMode.CONTAINS_CASE_SENSITIVE, values, "foo"); + sql = toSql("name", " ", StringMatchMode.CONTAINS_CASE_SENSITIVE, values, "foo"); System.out.println(sql); System.out.println(); - sql = toSql(" ", StringMatchMode.EQUALS_CASE_INSENSITIVE, values, "foo", "bar", "fub"); + sql = toSql("name", " ", StringMatchMode.EQUALS_CASE_INSENSITIVE, values, "foo", "bar", "fub"); System.out.println(sql); System.out.println(); - sql = toSql(" ", StringMatchMode.EQUALS_CASE_INSENSITIVE, values, "foo"); + sql = toSql("name", " ", StringMatchMode.EQUALS_CASE_INSENSITIVE, values, "foo"); System.out.println(sql); System.out.println(); - sql = toSql(" ", StringMatchMode.EQUALS_CASE_SENSITIVE, values, "foo", "bar", "fub"); + sql = toSql("name", " ", StringMatchMode.EQUALS_CASE_SENSITIVE, values, "foo", "bar", "fub"); System.out.println(sql); System.out.println(); - sql = toSql(" ", StringMatchMode.EQUALS_CASE_SENSITIVE, values, "foo"); + sql = toSql("name", " ", StringMatchMode.EQUALS_CASE_SENSITIVE, values, "foo"); System.out.println(sql); System.out.println(); } diff --git a/src/main/java/li/strolch/persistence/postgresql/PostgreSqlOrderQueryVisitor.java b/src/main/java/li/strolch/persistence/postgresql/PostgreSqlOrderQueryVisitor.java index ec8f728f0..687ac1331 100644 --- a/src/main/java/li/strolch/persistence/postgresql/PostgreSqlOrderQueryVisitor.java +++ b/src/main/java/li/strolch/persistence/postgresql/PostgreSqlOrderQueryVisitor.java @@ -15,13 +15,10 @@ */ package li.strolch.persistence.postgresql; -import java.sql.Date; - import li.strolch.model.Tags; import li.strolch.model.query.DateSelection; import li.strolch.model.query.OrderQueryVisitor; import li.strolch.model.query.StateSelection; -import ch.eitchnet.utils.collections.DateRange; /** * @author Robert von Burg @@ -45,28 +42,7 @@ public class PostgreSqlOrderQueryVisitor extends PostgreSqlQueryVisitor implemen @Override public void visit(DateSelection selection) { - - // TODO handle inclusive and exclusive date ranges - - DateRange dateRange = selection.getDateRange(); - if (dateRange.isDate()) { - sb.append(indent); - sb.append("date = ?\n"); - values.add(new Date(dateRange.getFromDate().getTime())); - } else if (dateRange.isBounded()) { - sb.append(indent); - sb.append("date between ? and ?\n"); - values.add(new Date(dateRange.getFromDate().getTime())); - values.add(new Date(dateRange.getToDate().getTime())); - } else if (dateRange.isToBounded()) { - sb.append(indent); - sb.append("date < ?\n"); - values.add(new Date(dateRange.getToDate().getTime())); - } else if (dateRange.isFromBounded()) { - sb.append(indent); - sb.append("date > ?\n"); - values.add(new Date(dateRange.getFromDate().getTime())); - } + PostgreSqlHelper.toSql(this.indent, this.sb, this.values, "date", selection.getDateRange()); } @Override diff --git a/src/main/java/li/strolch/persistence/postgresql/PostgreSqlQueryVisitor.java b/src/main/java/li/strolch/persistence/postgresql/PostgreSqlQueryVisitor.java index f5d1e015e..c10debcbe 100644 --- a/src/main/java/li/strolch/persistence/postgresql/PostgreSqlQueryVisitor.java +++ b/src/main/java/li/strolch/persistence/postgresql/PostgreSqlQueryVisitor.java @@ -144,7 +144,7 @@ public abstract class PostgreSqlQueryVisitor implements StrolchElementSelectionV this.sb.append(this.indent); String name = selection.getName(); StringMatchMode mm = selection.getMatchMode(); - this.sb.append(toSql(this.indent, mm, this.values, name)); + this.sb.append(toSql("name", this.indent, mm, this.values, name)); } @Override diff --git a/src/test/java/li/strolch/persistence/postgresql/dao/test/AuditQueryTest.java b/src/test/java/li/strolch/persistence/postgresql/dao/test/AuditQueryTest.java new file mode 100644 index 000000000..a85cc67f0 --- /dev/null +++ b/src/test/java/li/strolch/persistence/postgresql/dao/test/AuditQueryTest.java @@ -0,0 +1,270 @@ +/* + * Copyright 2013 Robert von Burg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package li.strolch.persistence.postgresql.dao.test; + +import static li.strolch.persistence.postgresql.dao.test.CachedDaoTest.CONFIG_SRC; +import static li.strolch.persistence.postgresql.dao.test.CachedDaoTest.DB_PASSWORD; +import static li.strolch.persistence.postgresql.dao.test.CachedDaoTest.DB_STORE_PATH_DIR; +import static li.strolch.persistence.postgresql.dao.test.CachedDaoTest.DB_URL; +import static li.strolch.persistence.postgresql.dao.test.CachedDaoTest.DB_USERNAME; +import static li.strolch.persistence.postgresql.dao.test.CachedDaoTest.RUNTIME_PATH; +import static li.strolch.persistence.postgresql.dao.test.CachedDaoTest.dropSchema; +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.HashSet; +import java.util.List; + +import li.strolch.agent.api.AuditTrail; +import li.strolch.agent.api.StrolchRealm; +import li.strolch.model.ModelGenerator; +import li.strolch.model.Tags; +import li.strolch.model.audit.AccessType; +import li.strolch.model.audit.Audit; +import li.strolch.model.audit.AuditQuery; +import li.strolch.persistence.api.AbstractTransaction; +import li.strolch.persistence.api.StrolchTransaction; +import li.strolch.persistence.postgresql.PostgreSqlAuditQueryVisitor; +import li.strolch.runtime.StrolchConstants; +import li.strolch.testbase.runtime.RuntimeMock; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ch.eitchnet.privilege.model.Certificate; +import ch.eitchnet.utils.StringMatchMode; +import ch.eitchnet.utils.collections.DateRange; + +/** + * @author Robert von Burg + */ +public class AuditQueryTest { + + private static final Logger logger = LoggerFactory.getLogger(AuditQueryTest.class); + private static RuntimeMock runtimeMock; + + private static Date past; + private static Date earlier; + private static Date current; + private static Date later; + private static Date future; + + @BeforeClass + public static void beforeClass() throws SQLException { + + dropSchema(DB_URL, DB_USERNAME, DB_PASSWORD); + + File rootPath = new File(RUNTIME_PATH); + File configSrc = new File(CONFIG_SRC); + runtimeMock = new RuntimeMock(); + runtimeMock.mockRuntime(rootPath, configSrc); + new File(rootPath, DB_STORE_PATH_DIR).mkdir(); + runtimeMock.startContainer(); + + Calendar cal = Calendar.getInstance(); + cal.clear(); + cal.set(2000, 1, 1); + past = cal.getTime(); + cal.set(2000, 4, 1); + earlier = cal.getTime(); + cal.set(2000, 6, 1); + current = cal.getTime(); + cal.set(2000, 8, 1); + later = cal.getTime(); + cal.set(2000, 11, 1); + future = cal.getTime(); + + Certificate cert = runtimeMock.getPrivilegeHandler().authenticate("test", "test".getBytes()); + StrolchRealm realm = runtimeMock.getRealm(StrolchConstants.DEFAULT_REALM); + int i = 0; + try (StrolchTransaction tx = realm.openTx(cert, "test")) { + ((AbstractTransaction) tx).setSuppressAudits(true); + AuditTrail auditTrail = tx.getAuditTrail(); + + Audit randomAudit; + randomAudit = ModelGenerator.randomAudit(); + randomAudit.setId(i++); + randomAudit.setUsername("earlier"); + randomAudit.setDate(earlier); + randomAudit.setAccessType(AccessType.CREATE); + randomAudit.setAction("create"); + randomAudit.setElementAccessed(randomAudit.getAccessType().name()); + auditTrail.add(tx, randomAudit); + + randomAudit = ModelGenerator.randomAudit(); + randomAudit.setId(i++); + randomAudit.setDate(current); + randomAudit.setUsername("current"); + randomAudit.setAccessType(AccessType.READ); + randomAudit.setAction("read"); + randomAudit.setElementAccessed(randomAudit.getAccessType().name()); + auditTrail.add(tx, randomAudit); + + randomAudit = ModelGenerator.randomAudit(); + randomAudit.setId(i++); + randomAudit.setDate(later); + randomAudit.setUsername("later"); + randomAudit.setAccessType(AccessType.UPDATE); + randomAudit.setAction("update"); + randomAudit.setElementAccessed(randomAudit.getAccessType().name()); + auditTrail.add(tx, randomAudit); + + randomAudit = ModelGenerator.randomAudit(); + randomAudit.setId(i++); + randomAudit.setDate(current); + randomAudit.setUsername("current"); + randomAudit.setAccessType(AccessType.DELETE); + randomAudit.setAction("delete"); + randomAudit.setElementAccessed(randomAudit.getAccessType().name()); + auditTrail.add(tx, randomAudit); + + randomAudit = ModelGenerator.randomAudit(); + randomAudit.setId(i++); + randomAudit.setDate(current); + randomAudit.setUsername("current"); + randomAudit.setAccessType(AccessType.CREATE); + randomAudit.setAction("create"); + randomAudit.setElementAccessed(randomAudit.getAccessType().name()); + auditTrail.add(tx, randomAudit); + } + } + + @AfterClass + public static void afterClass() { + runtimeMock.destroyRuntime(); + } + + public Connection openConn() throws SQLException { + String url = "jdbc:postgresql://localhost/testdb"; + String username = "testuser"; + String password = "test"; + Connection connection = DriverManager.getConnection(url, username, password); + connection.setAutoCommit(false); + return connection; + } + + @Test + public void shouldQueryTypeAndDateRange() throws SQLException { + AuditQuery query = new AuditQuery(Tags.AUDIT, new DateRange().from(earlier, true).to(later, true)); + performQuery(query, Arrays.asList("0", "1", "2", "3", "4")); + + query = new AuditQuery(Tags.AUDIT, new DateRange().from(current, true).to(current, true)); + performQuery(query, Arrays.asList("1", "3", "4")); + + query = new AuditQuery(Tags.AUDIT, new DateRange().from(current, true)); + performQuery(query, Arrays.asList("1", "2", "3", "4")); + + query = new AuditQuery(Tags.AUDIT, new DateRange().to(current, true)); + performQuery(query, Arrays.asList("0", "1", "3", "4")); + + query = new AuditQuery(Tags.RESOURCE, new DateRange().from(past, true).to(future, true)); + performQuery(query, Arrays. asList()); + } + + @Test + public void shouldQueryAudits() throws SQLException { + AuditQuery query = new AuditQuery(Tags.AUDIT, new DateRange().from(past, true).to(future, true)); + query.action().accessTypes(AccessType.CREATE, AccessType.READ); + performQuery(query, Arrays.asList("0", "1", "4")); + + query = new AuditQuery(Tags.AUDIT, new DateRange().from(past, true).to(future, true)); + query.action().accessTypes(AccessType.CREATE); + performQuery(query, Arrays.asList("0", "4")); + + query = new AuditQuery(Tags.AUDIT, new DateRange().from(past, true).to(future, true)); + query.action().accessTypes(AccessType.CREATE, AccessType.READ) + .actions(StringMatchMode.EQUALS_CASE_SENSITIVE, "create", "read"); + performQuery(query, Arrays.asList("0", "1", "4")); + + query = new AuditQuery(Tags.AUDIT, new DateRange().from(past, true).to(future, true)); + query.action().accessTypes(AccessType.CREATE, AccessType.READ) + .actions(StringMatchMode.EQUALS_CASE_SENSITIVE, "read"); + performQuery(query, Arrays.asList("1")); + + query = new AuditQuery(Tags.AUDIT, new DateRange().from(past, true).to(future, true)); + query.element().elementsAccessed(StringMatchMode.CONTAINS_CASE_INSENSITIVE, "crea"); + performQuery(query, Arrays.asList("0", "4")); + + query = new AuditQuery(Tags.AUDIT, new DateRange().from(past, true).to(future, true)); + query.element().elementsAccessed(StringMatchMode.CONTAINS_CASE_SENSITIVE, "crea"); + performQuery(query, Arrays. asList()); + + query = new AuditQuery(Tags.AUDIT, new DateRange().from(past, true).to(future, true)); + query.element().elementsAccessed(StringMatchMode.EQUALS_CASE_INSENSITIVE, "create"); + performQuery(query, Arrays.asList("0", "4")); + + query = new AuditQuery(Tags.AUDIT, new DateRange().from(past, true).to(future, true)); + query.identity().usernames(StringMatchMode.EQUALS_CASE_INSENSITIVE, "earlier"); + performQuery(query, Arrays.asList("0")); + + query = new AuditQuery(Tags.AUDIT, new DateRange().from(past, true).to(future, true)); + query.identity().usernames(StringMatchMode.EQUALS_CASE_INSENSITIVE, "earlier", "later"); + performQuery(query, Arrays.asList("0", "2")); + + query = new AuditQuery(Tags.AUDIT, new DateRange().from(past, true).to(future, true)); + query.identity().usernames(StringMatchMode.EQUALS_CASE_INSENSITIVE, "earlier") + .firstnames(StringMatchMode.CONTAINS_CASE_INSENSITIVE, "enn"); + performQuery(query, Arrays.asList("0")); + + query = new AuditQuery(Tags.AUDIT, new DateRange().from(past, true).to(future, true)); + query.identity().usernames(StringMatchMode.EQUALS_CASE_INSENSITIVE, "earlier") + .firstnames(StringMatchMode.CONTAINS_CASE_INSENSITIVE, "enn") + .lastnames(StringMatchMode.CONTAINS_CASE_INSENSITIVE, "kennedy"); + performQuery(query, Arrays.asList("0")); + + query = new AuditQuery(Tags.AUDIT, new DateRange().from(past, true).to(future, true)); + query.identity().firstnames(StringMatchMode.CONTAINS_CASE_INSENSITIVE, "enn") + .lastnames(StringMatchMode.CONTAINS_CASE_INSENSITIVE, "kennedy"); + performQuery(query, Arrays.asList("0", "1", "2", "3", "4")); + } + + private void performQuery(AuditQuery query, List expected) throws SQLException { + PostgreSqlAuditQueryVisitor visitor = new PostgreSqlAuditQueryVisitor("id"); + query.accept(visitor); + List ids = queryIds(visitor); + assertEquals(new HashSet<>(expected), new HashSet<>(ids)); + } + + private List queryIds(PostgreSqlAuditQueryVisitor visitor) throws SQLException { + String sql = visitor.getSql(); + logger.info("\n" + sql); + List ids = new ArrayList<>(); + try (Connection con = openConn()) { + try (PreparedStatement ps = con.prepareStatement(sql)) { + visitor.setValues(ps); + + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + ids.add(rs.getString(1)); + } + } + } + + return ids; + } +} diff --git a/src/test/java/li/strolch/persistence/postgresql/dao/test/CachedDaoTest.java b/src/test/java/li/strolch/persistence/postgresql/dao/test/CachedDaoTest.java index f689524ed..902e86e26 100644 --- a/src/test/java/li/strolch/persistence/postgresql/dao/test/CachedDaoTest.java +++ b/src/test/java/li/strolch/persistence/postgresql/dao/test/CachedDaoTest.java @@ -19,6 +19,7 @@ import java.io.File; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; +import java.text.MessageFormat; import li.strolch.persistence.postgresql.DbSchemaVersionCheck; import li.strolch.testbase.runtime.AbstractModelTest; @@ -26,6 +27,10 @@ import li.strolch.testbase.runtime.RuntimeMock; import org.junit.AfterClass; import org.junit.BeforeClass; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ch.eitchnet.utils.helper.StringHelper; public class CachedDaoTest extends AbstractModelTest { @@ -37,6 +42,8 @@ public class CachedDaoTest extends AbstractModelTest { public static final String DB_USERNAME = "testuser"; //$NON-NLS-1$ public static final String DB_PASSWORD = "test"; //$NON-NLS-1$ + private static final Logger logger = LoggerFactory.getLogger(CachedDaoTest.class); + protected static RuntimeMock runtimeMock; @Override @@ -59,7 +66,9 @@ public class CachedDaoTest extends AbstractModelTest { public static void dropSchema(String dbUrl, String dbUsername, String dbPassword) throws SQLException { String dbVersion = DbSchemaVersionCheck.getExpectedDbVersion(); + logger.info(MessageFormat.format("Dropping schema for expected version {0}", dbVersion)); String sql = DbSchemaVersionCheck.getSql(dbVersion, "drop"); //$NON-NLS-1$ + logger.info(StringHelper.NEW_LINE + sql); try (Connection connection = DriverManager.getConnection(dbUrl, dbUsername, dbPassword)) { connection.prepareStatement(sql).execute(); } diff --git a/src/test/java/li/strolch/persistence/postgresql/dao/test/QueryTest.java b/src/test/java/li/strolch/persistence/postgresql/dao/test/QueryTest.java index 596990f7c..88c9afdfb 100644 --- a/src/test/java/li/strolch/persistence/postgresql/dao/test/QueryTest.java +++ b/src/test/java/li/strolch/persistence/postgresql/dao/test/QueryTest.java @@ -331,7 +331,7 @@ public class QueryTest { PostgreSqlOrderQueryVisitor visitor = new PostgreSqlOrderQueryVisitor("id"); query.accept(visitor); List ids = queryIds(visitor); - assertEquals(expected, ids); + assertEquals(new HashSet<>(expected), new HashSet<>(ids)); } private void performResourceQuery(ResourceQuery query, List expected) throws SQLException {