diff --git a/README.md b/README.md index 86bff6cd7..12c91693c 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,8 @@ You can enable and disable features through configuration at runtime with dedica the exact same web console. -Test it yourself [![Live Demo](https://img.shields.io/badge/ff4jdemo-online-green.svg)](http://cannys.com/ff4j-demo/) +Test it yourself +[![Live Demo](https://img.shields.io/badge/ff4jdemo-online-green.svg)](http://cannys.com/ff4j-demo/) More information at [ff4j.org](http://ff4j.org) or [reference guide](https://github.com/clun/ff4j-extra/raw/master/ff4j-reference-guide-1.3.pdf). diff --git a/ff4j-core/src/main/java/org/ff4j/audit/repository/InMemoryEventRepository.java b/ff4j-core/src/main/java/org/ff4j/audit/repository/InMemoryEventRepository.java index fd17c6863..ad58648ac 100644 --- a/ff4j-core/src/main/java/org/ff4j/audit/repository/InMemoryEventRepository.java +++ b/ff4j-core/src/main/java/org/ff4j/audit/repository/InMemoryEventRepository.java @@ -116,24 +116,22 @@ public PieChart getFeatureHitsPie(String featureId, long startTime, long endTime int nbDisable = 0; int nbFlip = 0; int notFlip = 0; - if (null != qEvents) { - for (Event evt : qEvents) { - if (evt.getTimestamp() > startTime && evt.getTimestamp() < endTime) { - switch (evt.getType()) { - case FEATURE_CHECK_ON: - nbFlip++; - break; - case FEATURE_CHECK_OFF: - notFlip++; - break; - case ENABLE_FEATURE: - nbEnable++; - break; - case DISABLE_FEATURE: - nbDisable++; - default: - break; - } + for (Event evt : qEvents) { + if (evt.getTimestamp() > startTime && evt.getTimestamp() < endTime) { + switch (evt.getType()) { + case FEATURE_CHECK_ON: + nbFlip++; + break; + case FEATURE_CHECK_OFF: + notFlip++; + break; + case ENABLE_FEATURE: + nbEnable++; + break; + case DISABLE_FEATURE: + nbDisable++; + default: + break; } } } diff --git a/ff4j-core/src/main/java/org/ff4j/audit/repository/JdbcEventRepository.java b/ff4j-core/src/main/java/org/ff4j/audit/repository/JdbcEventRepository.java index 42c60cab3..1265ee65d 100644 --- a/ff4j-core/src/main/java/org/ff4j/audit/repository/JdbcEventRepository.java +++ b/ff4j-core/src/main/java/org/ff4j/audit/repository/JdbcEventRepository.java @@ -66,24 +66,23 @@ public int getTotalEventCount() { Connection sqlConn = null; PreparedStatement stmt = null; ResultSet rs = null; + int totalEvent = 0; try { // Get collection from Pool sqlConn = dataSource.getConnection(); stmt = sqlConn.prepareStatement(SQL_AUDIT_COUNT); rs = stmt.executeQuery(); if (rs.next()) { - return rs.getInt(1); + totalEvent = rs.getInt(1); } - } catch(Exception exc) { throw new AuditAccessException("Cannot read audit information from database ", exc); - } finally { closeResultSet(rs); closeStatement(stmt); closeConnection(sqlConn); } - return 0; + return totalEvent; } /** {@inheritDoc} */ @@ -112,7 +111,7 @@ public boolean saveEvent(Event evt) { sqlConn.commit(); } catch(Exception exc) { - throw new RuntimeException("Cannot insert event into DB", exc); + throw new AuditAccessException("Cannot insert event into DB", exc); } finally { closeStatement(stmt); diff --git a/ff4j-core/src/test/java/org/ff4j/test/audit/InMemoryEventRepositoryTest.java b/ff4j-core/src/test/java/org/ff4j/test/audit/InMemoryEventRepositoryTest.java index 93b96e1c3..ad08237d0 100644 --- a/ff4j-core/src/test/java/org/ff4j/test/audit/InMemoryEventRepositoryTest.java +++ b/ff4j-core/src/test/java/org/ff4j/test/audit/InMemoryEventRepositoryTest.java @@ -60,6 +60,17 @@ public void testNotExceedLimit() throws InterruptedException { } + @Test + public void testInMemoryTest() { + InMemoryEventRepository repo1 = new InMemoryEventRepository(limit); + repo1.saveEvent(new Event("aer", EventType.FEATURE_CHECK_ON)); + repo1.saveEvent(new Event("aer", EventType.FEATURE_CHECK_ON)); + repo1.getHitsPieChart((System.currentTimeMillis() - 10000), (System.currentTimeMillis() + 10000)); + + repo1.saveEvent(new Event("aer", EventType.ENABLE_FEATURE)); + repo1.getFeatureHitsPie("aer", (System.currentTimeMillis() - 100000), (System.currentTimeMillis() + 100000)); + } + @Test public void testNotExceedLimit2() throws InterruptedException { InMemoryEventRepository repo1 = new InMemoryEventRepository(limit); diff --git a/ff4j-core/src/test/java/org/ff4j/test/audit/JdbcEventRepositoryTest.java b/ff4j-core/src/test/java/org/ff4j/test/audit/JdbcEventRepositoryTest.java index abdf7ce45..42fdd3f40 100644 --- a/ff4j-core/src/test/java/org/ff4j/test/audit/JdbcEventRepositoryTest.java +++ b/ff4j-core/src/test/java/org/ff4j/test/audit/JdbcEventRepositoryTest.java @@ -1,5 +1,11 @@ package org.ff4j.test.audit; +import static org.mockito.Mockito.doThrow; + +import java.sql.SQLException; + +import javax.sql.DataSource; + import org.ff4j.audit.Event; import org.ff4j.audit.EventType; @@ -25,9 +31,13 @@ import org.ff4j.audit.repository.EventRepository; import org.ff4j.audit.repository.JdbcEventRepository; +import org.ff4j.exception.AuditAccessException; +import org.ff4j.exception.FeatureAccessException; +import org.ff4j.utils.Util; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; @@ -38,8 +48,8 @@ * @author Cedrick Lunven (@clunven) */ public class JdbcEventRepositoryTest extends AbstractEventRepositoryTest { - - /** DataBase. */ + + /** DataBase. */ private EmbeddedDatabase db; /** Builder. */ @@ -66,7 +76,7 @@ public void tearDown() throws Exception { @Override protected EventRepository initRepository() { //sqlDataSource = JdbcTestHelper.createInMemoryHQLDataSource(); - builder = new EmbeddedDatabaseBuilder(); + builder = new EmbeddedDatabaseBuilder(); db = builder.setType(EmbeddedDatabaseType.HSQL).// addScript("classpath:schema-ddl.sql").// addScript("classpath:ff-store.sql").// @@ -84,6 +94,60 @@ public void testJdbcSpec() { jrepo.getHitsPieChart((System.currentTimeMillis() - 10000), (System.currentTimeMillis() + 10000)); jrepo.setDataSource(null); } + + @Test(expected = AuditAccessException.class) + public void testJdbcSpec2() throws SQLException { + JdbcEventRepository jrepo = (JdbcEventRepository) repo; + DataSource mockDS = Mockito.mock(DataSource.class); + doThrow(new SQLException()).when(mockDS).getConnection(); + jrepo.setDataSource(mockDS); + jrepo.getTotalEventCount(); + } + + @Test(expected = AuditAccessException.class) + public void testJdbcSaveEventKO() throws SQLException { + JdbcEventRepository jrepo = (JdbcEventRepository) repo; + DataSource mockDS = Mockito.mock(DataSource.class); + doThrow(new SQLException()).when(mockDS).getConnection(); + jrepo.setDataSource(mockDS); + jrepo.saveEvent(new Event("ee", EventType.CREATE_FEATURE)); + } + + @Test(expected = FeatureAccessException.class) + public void testJdbcFeatureNamesKO() throws SQLException { + JdbcEventRepository jrepo = (JdbcEventRepository) repo; + DataSource mockDS = Mockito.mock(DataSource.class); + doThrow(new SQLException()).when(mockDS).getConnection(); + jrepo.setDataSource(mockDS); + jrepo.getFeatureNames(); + } + + @Test(expected = FeatureAccessException.class) + public void testJdbcHitPieCharts() throws SQLException { + JdbcEventRepository jrepo = (JdbcEventRepository) repo; + DataSource mockDS = Mockito.mock(DataSource.class); + doThrow(new SQLException()).when(mockDS).getConnection(); + jrepo.setDataSource(mockDS); + jrepo.getHitsPieChart(0, 1); + } + + @Test(expected = AuditAccessException.class) + public void testJdbcHitBarCharts() throws SQLException { + JdbcEventRepository jrepo = (JdbcEventRepository) repo; + DataSource mockDS = Mockito.mock(DataSource.class); + doThrow(new SQLException()).when(mockDS).getConnection(); + jrepo.setDataSource(mockDS); + jrepo.getHitsBarChart(Util.set("1"), 0, 1, 2); + } + + @Test(expected = AuditAccessException.class) + public void testgetFeatureHitsPieKo() throws SQLException { + JdbcEventRepository jrepo = (JdbcEventRepository) repo; + DataSource mockDS = Mockito.mock(DataSource.class); + doThrow(new SQLException()).when(mockDS).getConnection(); + jrepo.setDataSource(mockDS); + jrepo.getFeatureHitsPie("f1", 0, 1); + } } // \ No newline at end of file diff --git a/ff4j-web/src/main/java/org/ff4j/web/embedded/ConsoleOperations.java b/ff4j-web/src/main/java/org/ff4j/web/embedded/ConsoleOperations.java index d1751130a..cc1d3f547 100644 --- a/ff4j-web/src/main/java/org/ff4j/web/embedded/ConsoleOperations.java +++ b/ff4j-web/src/main/java/org/ff4j/web/embedded/ConsoleOperations.java @@ -34,12 +34,14 @@ import javax.servlet.http.HttpServletResponse; import org.ff4j.FF4j; +import org.ff4j.conf.XmlConfig; import org.ff4j.conf.XmlParser; import org.ff4j.core.Feature; import org.ff4j.core.FeatureStore; import org.ff4j.core.FlippingStrategy; import org.ff4j.property.AbstractProperty; import org.ff4j.property.PropertyFactory; +import org.ff4j.property.store.PropertyStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -272,8 +274,10 @@ public static void updateFeatureDescription(FF4j ff4j, HttpServletRequest req) { */ public static void importFile(FF4j ff4j, InputStream in) throws IOException { + FeatureStore store = ff4j.getFeatureStore(); - Map mapsOfFeat = new XmlParser().parseConfigurationFile(in).getFeatures(); + XmlConfig xmlConfig = new XmlParser().parseConfigurationFile(in); + Map mapsOfFeat = xmlConfig.getFeatures(); for (Entry feature : mapsOfFeat.entrySet()) { if (store.exist(feature.getKey())) { store.update(feature.getValue()); @@ -282,6 +286,17 @@ public static void importFile(FF4j ff4j, InputStream in) } } LOGGER.info(mapsOfFeat.size() + " features have been imported."); + + PropertyStore pstore = ff4j.getPropertiesStore(); + Map> mapsOfProperties = xmlConfig.getProperties(); + for (Entry> p : mapsOfProperties.entrySet()) { + if (pstore.existProperty(p.getKey())) { + pstore.updateProperty(p.getValue()); + } else { + pstore.createProperty(p.getValue()); + } + } + LOGGER.info(mapsOfProperties.size() + " features have been imported."); } /**