package com.temenos.interaction.jdbc.producer; /* * #%L * interaction-jdbc-producer * %% * Copyright (C) 2012 - 2013 Temenos Holdings N.V. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.UriInfo; import org.junit.Ignore; import org.junit.Test; import org.springframework.jdbc.BadSqlGrammarException; import org.springframework.jdbc.support.rowset.SqlRowSet; import com.temenos.interaction.core.MultivaluedMapImpl; import com.temenos.interaction.core.command.InteractionContext; import com.temenos.interaction.core.entity.Metadata; import com.temenos.interaction.core.hypermedia.ResourceState; import com.temenos.interaction.jdbc.SqlRelation; import com.temenos.interaction.odataext.odataparser.ODataParser; public class TestIrisFilter extends AbstractJdbcProducerTest { /** * Test access to database using Iris parameters with a $filter term. */ @Test public void testIrisFilterQuery() { // Populate the database. populateTestTable(); // Create the producer JdbcProducer producer = null; try { producer = new JdbcProducer(dataSource); } catch (Exception e) { fail(); } // Build up an InteractionContext MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl<String>(); queryParams.add(ODataParser.FILTER_KEY, VARCHAR_FIELD_NAME + " " + SqlRelation.EQ.getoDataString() + " '" + TEST_VARCHAR_DATA + "2'"); MultivaluedMap<String, String> pathParams = new MultivaluedMapImpl<String>(); InteractionContext ctx = new InteractionContext(mock(UriInfo.class), mock(HttpHeaders.class), pathParams, queryParams, mock(ResourceState.class), mock(Metadata.class)); // Run a query SqlRowSet rs = null; try { rs = producer.query(TEST_TABLE_NAME, null, ctx); } catch (Exception e) { fail(); } // Check the results. Should get all fields of the single row we // filtered for. assertFalse(null == rs); int rowCount = 0; while (rs.next()) { assertEquals(TEST_KEY_DATA + 2, rs.getString(KEY_FIELD_NAME)); assertEquals(TEST_VARCHAR_DATA + 2, rs.getString(VARCHAR_FIELD_NAME)); assertEquals(TEST_INTEGER_DATA + 2, rs.getInt(INTEGER_FIELD_NAME)); rowCount++; } assertEquals(1, rowCount); } /** * Test access to database using Iris parameters with a numeric $filter * term. */ @Test public void testIrisNumericFilterQuery() { // Populate the database. populateTestTable(); // Create the producer JdbcProducer producer = null; try { producer = new JdbcProducer(dataSource); } catch (Exception e) { fail(); } // Build up an InteractionContext MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl<String>(); queryParams.add(ODataParser.FILTER_KEY, INTEGER_FIELD_NAME + " " + SqlRelation.EQ.getoDataString() + " " + (TEST_INTEGER_DATA + 2)); MultivaluedMap<String, String> pathParams = new MultivaluedMapImpl<String>(); InteractionContext ctx = new InteractionContext(mock(UriInfo.class), mock(HttpHeaders.class), pathParams, queryParams, mock(ResourceState.class), mock(Metadata.class)); // Run a query SqlRowSet rs = null; try { rs = producer.query(TEST_TABLE_NAME, null, ctx); } catch (Exception e) { fail(); } // Check the results. Should get all fields of the single row we // filtered for. assertFalse(null == rs); int rowCount = 0; while (rs.next()) { assertEquals(TEST_KEY_DATA + 2, rs.getString(KEY_FIELD_NAME)); assertEquals(TEST_VARCHAR_DATA + 2, rs.getString(VARCHAR_FIELD_NAME)); assertEquals(TEST_INTEGER_DATA + 2, rs.getInt(INTEGER_FIELD_NAME)); rowCount++; } assertEquals(1, rowCount); } /** * Test access to database using Iris parameters with a non numeric value * for a numeric $filter term. */ @Test public void testIrisBadNumericFilterQuery() throws Exception { // Populate the database. populateTestTable(); // Create the producer JdbcProducer producer = null; try { producer = new JdbcProducer(dataSource); } catch (Exception e) { fail(); } // Build up an InteractionContext MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl<String>(); queryParams.add(ODataParser.FILTER_KEY, INTEGER_FIELD_NAME + " " + SqlRelation.EQ.getoDataString() + " " + "bad"); MultivaluedMap<String, String> pathParams = new MultivaluedMapImpl<String>(); InteractionContext ctx = new InteractionContext(mock(UriInfo.class), mock(HttpHeaders.class), pathParams, queryParams, mock(ResourceState.class), mock(Metadata.class)); // Run a query. Should fail and throw. boolean threw = false; try { producer.query(TEST_TABLE_NAME, null, ctx); } catch (SecurityException e) { // Old parser detected bad column names and threw this exception. threw = true; } catch (BadSqlGrammarException e) { // New parser does not know about column names. But bad names will // fail during query. threw = true; } if (!threw) { fail(); } } /** * Test access to database using 'concat' in a $filter term. */ @Test public void testConcatFilterQuery() { // Populate the database. populateTestTable(); // Create the producer JdbcProducer producer = null; try { producer = new JdbcProducer(dataSource); } catch (Exception e) { fail(); } // Build up an InteractionContext MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl<String>(); // Create name by concatenating two strings at runtime queryParams.add(ODataParser.FILTER_KEY, VARCHAR_FIELD_NAME + " " + SqlRelation.EQ.getoDataString() + " " + SqlRelation.CONCAT.getoDataString() + "('" + TEST_VARCHAR_DATA + "', '2')"); MultivaluedMap<String, String> pathParams = new MultivaluedMapImpl<String>(); InteractionContext ctx = new InteractionContext(mock(UriInfo.class), mock(HttpHeaders.class), pathParams, queryParams, mock(ResourceState.class), mock(Metadata.class)); // Run a query SqlRowSet rs = null; try { rs = producer.query(TEST_TABLE_NAME, null, ctx); } catch (Exception e) { fail(); } // Check the results. Should get all fields of the single row we // filtered for. assertFalse(null == rs); int rowCount = 0; while (rs.next()) { assertEquals(TEST_KEY_DATA + 2, rs.getString(KEY_FIELD_NAME)); assertEquals(TEST_VARCHAR_DATA + 2, rs.getString(VARCHAR_FIELD_NAME)); assertEquals(TEST_INTEGER_DATA + 2, rs.getInt(INTEGER_FIELD_NAME)); rowCount++; } assertEquals(1, rowCount); } /** * Test access to database using 'substr' in a $filter term. */ @Test public void testSubstrFilterQuery() { // Populate the database. populateTestTable(); // Create the producer JdbcProducer producer = null; try { producer = new JdbcProducer(dataSource); } catch (Exception e) { fail(); } // Build up an InteractionContext MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl<String>(); // Create name by taking a substring at runtime queryParams.add(ODataParser.FILTER_KEY, VARCHAR_FIELD_NAME + " " + SqlRelation.EQ.getoDataString() + " " + SqlRelation.SUBSTR.getoDataString() + "('" + "JUNK" + TEST_VARCHAR_DATA + "2', '5')"); MultivaluedMap<String, String> pathParams = new MultivaluedMapImpl<String>(); InteractionContext ctx = new InteractionContext(mock(UriInfo.class), mock(HttpHeaders.class), pathParams, queryParams, mock(ResourceState.class), mock(Metadata.class)); // Run a query SqlRowSet rs = null; try { rs = producer.query(TEST_TABLE_NAME, null, ctx); } catch (Exception e) { fail(); } // Check the results. Should get all fields of the single row we // filtered for. assertFalse(null == rs); int rowCount = 0; while (rs.next()) { assertEquals(TEST_KEY_DATA + 2, rs.getString(KEY_FIELD_NAME)); assertEquals(TEST_VARCHAR_DATA + 2, rs.getString(VARCHAR_FIELD_NAME)); assertEquals(TEST_INTEGER_DATA + 2, rs.getInt(INTEGER_FIELD_NAME)); rowCount++; } assertEquals(1, rowCount); } /** * Test access to database using 3 argument version of 'substr' in a $filter * term. */ @Test public void testSubstr3ArgFilterQuery() { // Populate the database. populateTestTable(); // Create the producer JdbcProducer producer = null; try { producer = new JdbcProducer(dataSource); } catch (Exception e) { fail(); } // Build up an InteractionContext MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl<String>(); // Create name by taking a substring at runtime queryParams.add(ODataParser.FILTER_KEY, VARCHAR_FIELD_NAME + " " + SqlRelation.EQ.getoDataString() + " " + SqlRelation.SUBSTR.getoDataString() + "('" + "JUNK" + TEST_VARCHAR_DATA + "2JUNK', '5', '" + (TEST_VARCHAR_DATA.length() + 1) + "')"); MultivaluedMap<String, String> pathParams = new MultivaluedMapImpl<String>(); InteractionContext ctx = new InteractionContext(mock(UriInfo.class), mock(HttpHeaders.class), pathParams, queryParams, mock(ResourceState.class), mock(Metadata.class)); // Run a query SqlRowSet rs = null; try { rs = producer.query(TEST_TABLE_NAME, null, ctx); } catch (Exception e) { fail(); } // Check the results. Should get all fields of the single row we // filtered for. assertFalse(null == rs); int rowCount = 0; while (rs.next()) { assertEquals(TEST_KEY_DATA + 2, rs.getString(KEY_FIELD_NAME)); assertEquals(TEST_VARCHAR_DATA + 2, rs.getString(VARCHAR_FIELD_NAME)); assertEquals(TEST_INTEGER_DATA + 2, rs.getInt(INTEGER_FIELD_NAME)); rowCount++; } assertEquals(1, rowCount); } /** * Test access to database using 'replace' in a $filter term. */ @Test public void testReplaceFilterQuery() { // Populate the database. populateTestTable(); // Create the producer JdbcProducer producer = null; try { producer = new JdbcProducer(dataSource); } catch (Exception e) { fail(); } // Build up an InteractionContext MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl<String>(); // Create name by replacing one string with another at runtime queryParams.add(ODataParser.FILTER_KEY, VARCHAR_FIELD_NAME + " " + SqlRelation.EQ.getoDataString() + " " + SqlRelation.REPLACE.getoDataString() + "('JUNK2', 'JUNK', '" + TEST_VARCHAR_DATA + "')"); MultivaluedMap<String, String> pathParams = new MultivaluedMapImpl<String>(); InteractionContext ctx = new InteractionContext(mock(UriInfo.class), mock(HttpHeaders.class), pathParams, queryParams, mock(ResourceState.class), mock(Metadata.class)); // Run a query SqlRowSet rs = null; try { rs = producer.query(TEST_TABLE_NAME, null, ctx); } catch (Exception e) { fail(); } // Check the results. Should get all fields of the single row we // filtered for. assertFalse(null == rs); int rowCount = 0; while (rs.next()) { assertEquals(TEST_KEY_DATA + 2, rs.getString(KEY_FIELD_NAME)); assertEquals(TEST_VARCHAR_DATA + 2, rs.getString(VARCHAR_FIELD_NAME)); assertEquals(TEST_INTEGER_DATA + 2, rs.getInt(INTEGER_FIELD_NAME)); rowCount++; } assertEquals(1, rowCount); } /** * Test access to database using 'floor' in a $filter term. * * TODO Also write 'ceiling' and 'round' versions. */ @Test // Manual testing of this mechanism against SQL server appears to work. But // this test gets no data. Possibly a difference between H2 emulation and // SQL server. @Ignore public void testFloorFilterQuery() { // Populate the database. populateTestTable(); // Create the producer JdbcProducer producer = null; try { producer = new JdbcProducer(dataSource); } catch (Exception e) { fail(); } // Build up an InteractionContext MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl<String>(); // Create name by using a numeric 'floor' at runtime queryParams.add(ODataParser.FILTER_KEY, VARCHAR_FIELD_NAME + " " + SqlRelation.EQ.getoDataString() + " " + SqlRelation.CONCAT.getoDataString() + "('" + TEST_VARCHAR_DATA + "', " + SqlRelation.FLOOR.getoDataString() + "('2.67')" + ")"); MultivaluedMap<String, String> pathParams = new MultivaluedMapImpl<String>(); InteractionContext ctx = new InteractionContext(mock(UriInfo.class), mock(HttpHeaders.class), pathParams, queryParams, mock(ResourceState.class), mock(Metadata.class)); // Run a query SqlRowSet rs = null; try { rs = producer.query(TEST_TABLE_NAME, null, ctx); } catch (Exception e) { fail(); } // Check the results. Should get all fields of the single row we // filtered for. assertFalse(null == rs); int rowCount = 0; while (rs.next()) { assertEquals(TEST_KEY_DATA + 2, rs.getString(KEY_FIELD_NAME)); assertEquals(TEST_VARCHAR_DATA + 2, rs.getString(VARCHAR_FIELD_NAME)); assertEquals(TEST_INTEGER_DATA + 2, rs.getInt(INTEGER_FIELD_NAME)); rowCount++; } assertEquals(1, rowCount); } /** * Test access to database using 'trim' in a $filter term. */ @Test public void testTrimFilterQuery() { // Populate the database. populateTestTable(); // Create the producer JdbcProducer producer = null; try { producer = new JdbcProducer(dataSource); } catch (Exception e) { fail(); } // Build up an InteractionContext MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl<String>(); // Create name with white space around it. queryParams.add(ODataParser.FILTER_KEY, VARCHAR_FIELD_NAME + " " + SqlRelation.EQ.getoDataString() + " " + SqlRelation.TRIM.getoDataString() + "(' " + TEST_VARCHAR_DATA + "2 ')"); MultivaluedMap<String, String> pathParams = new MultivaluedMapImpl<String>(); InteractionContext ctx = new InteractionContext(mock(UriInfo.class), mock(HttpHeaders.class), pathParams, queryParams, mock(ResourceState.class), mock(Metadata.class)); // Run a query SqlRowSet rs = null; try { rs = producer.query(TEST_TABLE_NAME, null, ctx); } catch (Exception e) { fail(); } // Check the results. Should get all fields of the single row we // filtered for. assertFalse(null == rs); int rowCount = 0; while (rs.next()) { assertEquals(TEST_KEY_DATA + 2, rs.getString(KEY_FIELD_NAME)); assertEquals(TEST_VARCHAR_DATA + 2, rs.getString(VARCHAR_FIELD_NAME)); assertEquals(TEST_INTEGER_DATA + 2, rs.getInt(INTEGER_FIELD_NAME)); rowCount++; } assertEquals(1, rowCount); } /** * Test access to database using 'indexof' in a $filter term. */ @Test public void testIndexOfFilterQuery() { // Populate the database. populateTestTable(); // Create the producer JdbcProducer producer = null; try { producer = new JdbcProducer(dataSource); } catch (Exception e) { fail(); } // Build up an InteractionContext MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl<String>(); // Create name with a number based on 'indexof' (looks like characters // are indexed from 1). queryParams.add(ODataParser.FILTER_KEY, VARCHAR_FIELD_NAME + " " + SqlRelation.EQ.getoDataString() + " " + SqlRelation.CONCAT.getoDataString() + "('" + TEST_VARCHAR_DATA + "', " + SqlRelation.INDEXOF.getoDataString() + "('xtestyyy', 'test'))"); MultivaluedMap<String, String> pathParams = new MultivaluedMapImpl<String>(); InteractionContext ctx = new InteractionContext(mock(UriInfo.class), mock(HttpHeaders.class), pathParams, queryParams, mock(ResourceState.class), mock(Metadata.class)); // Run a query SqlRowSet rs = null; try { rs = producer.query(TEST_TABLE_NAME, null, ctx); } catch (Exception e) { fail(); } // Check the results. Should get all fields of the single row we // filtered for. assertFalse(null == rs); int rowCount = 0; while (rs.next()) { assertEquals(TEST_KEY_DATA + 2, rs.getString(KEY_FIELD_NAME)); assertEquals(TEST_VARCHAR_DATA + 2, rs.getString(VARCHAR_FIELD_NAME)); assertEquals(TEST_INTEGER_DATA + 2, rs.getInt(INTEGER_FIELD_NAME)); rowCount++; } assertEquals(1, rowCount); } }