/* * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others. * * 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. * * Contributors: * Thierry Delprat */ package org.nuxeo.elasticsearch; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.concurrent.TimeUnit; import javax.inject.Inject; import org.junit.Test; import org.junit.runner.RunWith; import org.nuxeo.ecm.core.api.CoreSession; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.SortInfo; import org.nuxeo.ecm.core.api.VersioningOption; import org.nuxeo.ecm.core.versioning.VersioningService; import org.nuxeo.ecm.platform.audit.api.AuditLogger; import org.nuxeo.ecm.platform.audit.api.AuditReader; import org.nuxeo.ecm.platform.audit.api.LogEntry; import org.nuxeo.ecm.platform.audit.impl.LogEntryImpl; import org.nuxeo.ecm.platform.query.api.PageProvider; import org.nuxeo.ecm.platform.query.api.PageProviderDefinition; import org.nuxeo.ecm.platform.query.api.PageProviderService; import org.nuxeo.elasticsearch.test.RepositoryElasticSearchFeature; import org.nuxeo.runtime.api.Framework; import org.nuxeo.runtime.test.runner.Deploy; import org.nuxeo.runtime.test.runner.Features; import org.nuxeo.runtime.test.runner.FeaturesRunner; import org.nuxeo.runtime.test.runner.LocalDeploy; import org.nuxeo.runtime.transaction.TransactionHelper; @Deploy({ "org.nuxeo.ecm.platform.audit.api", "org.nuxeo.runtime.metrics", "org.nuxeo.ecm.platform.audit", "org.nuxeo.ecm.platform.uidgen.core", "org.nuxeo.elasticsearch.seqgen", "org.nuxeo.elasticsearch.seqgen.test:elasticsearch-seqgen-index-test-contrib.xml", "org.nuxeo.elasticsearch.audit", "org.nuxeo.admin.center" }) @RunWith(FeaturesRunner.class) @Features(RepositoryElasticSearchFeature.class) @LocalDeploy({ "org.nuxeo.elasticsearch.audit:elasticsearch-test-contrib.xml", "org.nuxeo.elasticsearch.audit:elasticsearch-audit-index-test-contrib.xml", "org.nuxeo.elasticsearch.audit:audit-test-contrib.xml" }) @SuppressWarnings("unchecked") public class TestESHistoryProvider { protected static final Calendar testDate = Calendar.getInstance(); protected DocumentModel doc; protected DocumentModel proxy; protected List<DocumentModel> versions; protected Date t1; protected Date t2; protected boolean verbose = false; protected void dump(Object ob) { System.out.println(ob.toString()); } protected void dump(List<?> obs) { for (Object ob : obs) { dump(ob); } } protected @Inject CoreSession session; protected void waitForAsyncCompletion() throws InterruptedException { TransactionHelper.commitOrRollbackTransaction(); TransactionHelper.startTransaction(); assertTrue(Framework.getLocalService(AuditLogger.class).await(10, TimeUnit.SECONDS)); } protected void createTestEntries() throws Exception { AuditReader reader = Framework.getLocalService(AuditReader.class); DocumentModel section = session.createDocumentModel("/", "section", "Folder"); section = session.createDocument(section); doc = session.createDocumentModel("/", "doc", "File"); doc.setPropertyValue("dc:title", "TestDoc"); // create the doc doc = session.createDocument(doc); // wait at least 1s to be sure we have a precise timestamp in all DB // backend Thread.sleep(500); t1 = new Date(); Thread.sleep(600); // do some updates for (int i = 0; i < 5; i++) { doc.setPropertyValue("dc:description", "Update " + i); doc.putContextData("comment", "Update " + i); doc = session.saveDocument(doc); waitForAsyncCompletion(); } // wait at least 1s to be sure we have a precise timestamp in all DB // backend Thread.sleep(600); t2 = new Date(); Thread.sleep(500); // create a version doc.putContextData(VersioningService.VERSIONING_OPTION, VersioningOption.MINOR); doc = session.saveDocument(doc); session.save(); waitForAsyncCompletion(); // wait at least 1s to be sure we have a precise timestamp in all DB // backend Thread.sleep(1100); // do some more updates for (int i = 5; i < 10; i++) { doc.setPropertyValue("dc:description", "Update " + i); doc.putContextData("comment", "Update " + i); doc = session.saveDocument(doc); session.save(); waitForAsyncCompletion(); } // wait at least 1s to be sure we have a precise timestamp in all DB // backend Thread.sleep(1100); proxy = session.publishDocument(doc, section); session.save(); waitForAsyncCompletion(); Thread.sleep(1100); // wait at least 1s to be sure we have a precise // timestamp in all DB backend // do some more updates for (int i = 10; i < 15; i++) { doc.setPropertyValue("dc:description", "Update " + i); doc.putContextData("comment", "Update " + i); doc = session.saveDocument(doc); session.save(); } Thread.sleep(500); waitForAsyncCompletion(); versions = session.getVersions(doc.getRef()); assertEquals(2, versions.size()); if (verbose) { for (DocumentModel version : versions) { System.out.println("version: " + version.getId()); System.out.println("version series: " + version.getVersionSeriesId()); System.out.println("version label: " + version.getVersionLabel()); System.out.println("version date: " + ((Calendar) version.getPropertyValue("dc:modified")).getTime()); } } LogEntryGen.flushAndSync(); // bonus entry ! LogEntry createdEntry = new LogEntryImpl(); createdEntry.setEventId("bonusEvent"); createdEntry.setCategory("bonusCategory"); createdEntry.setDocUUID(doc.getId()); createdEntry.setEventDate(new Date()); createdEntry.setDocPath(doc.getPathAsString()); createdEntry.setRepositoryId("test"); List<LogEntry> entries = new ArrayList<>(); entries.add(createdEntry); Framework.getLocalService(AuditLogger.class).addLogEntries(entries); LogEntryGen.flushAndSync(); List<LogEntry> logs = reader.getLogEntriesFor(doc.getId(), doc.getRepositoryName()); if (verbose) { dump(logs); } if (verbose) { String matchAll = "{\n" + " \"match_all\" : { }\n" + " }"; logs = (List<LogEntry>) reader.nativeQuery(matchAll, 0, 30); System.out.println("Total entries = " + logs.size()); dump(logs); } } @Test public void testDocumentHistoryPageProvider() throws Exception { createTestEntries(); PageProviderService pps = Framework.getService(PageProviderService.class); assertNotNull(pps); PageProvider<?> pp; List<LogEntry> entries; PageProviderDefinition ppdef = pps.getPageProviderDefinition("DOCUMENT_HISTORY_PROVIDER"); assertNotNull(ppdef); long startIdx = 0; List<SortInfo> si = Arrays.asList(new SortInfo("id", true)); DocumentModel searchDoc = session.createDocumentModel("BasicAuditSearch"); searchDoc.setPathInfo("/", "auditsearch"); searchDoc = session.createDocument(searchDoc); for (String ppName : new String[] { "DOCUMENT_HISTORY_PROVIDER_OLD", "DOCUMENT_HISTORY_PROVIDER" }) { if (ppName.endsWith("OLD")) { pp = pps.getPageProvider(ppName, si, Long.valueOf(20), Long.valueOf(0), new HashMap<String, Serializable>(), doc.getId()); } else { pp = pps.getPageProvider(ppName, si, Long.valueOf(20), Long.valueOf(0), new HashMap<String, Serializable>(), doc); } assertNotNull(pp); searchDoc.setPropertyValue("basicauditsearch:eventIds", null); searchDoc.setPropertyValue("basicauditsearch:eventCategories", null); searchDoc.setPropertyValue("basicauditsearch:startDate", null); searchDoc.setPropertyValue("basicauditsearch:endDate", null); pp.setSearchDocumentModel(searchDoc); // Get Live doc history entries = (List<LogEntry>) pp.getCurrentPage(); if (verbose) { System.out.println("Live doc history"); dump(entries); } // create, 15+1 update , 2 checkin, 1 bonus assertEquals(20, entries.size()); startIdx = entries.get(0).getId(); // endIdx = entries.get(17).getId(); // filter on eventId searchDoc.setPropertyValue("basicauditsearch:eventIds", new String[] { "documentModified" }); searchDoc.setPropertyValue("basicauditsearch:eventCategories", null); pp.setSearchDocumentModel(searchDoc); entries = (List<LogEntry>) pp.getCurrentPage(); assertEquals(16, entries.size()); // filter on category searchDoc.setPropertyValue("basicauditsearch:eventIds", null); searchDoc.setPropertyValue("basicauditsearch:eventCategories", new String[] { "eventDocumentCategory" }); pp.setSearchDocumentModel(searchDoc); entries = (List<LogEntry>) pp.getCurrentPage(); assertEquals(19, entries.size()); // filter on category searchDoc.setPropertyValue("basicauditsearch:eventIds", null); searchDoc.setPropertyValue("basicauditsearch:eventCategories", new String[] { "eventDocumentCategory", "bonusCategory" }); pp.setSearchDocumentModel(searchDoc); entries = (List<LogEntry>) pp.getCurrentPage(); assertEquals(20, entries.size()); // filter on Date ! searchDoc.setPropertyValue("basicauditsearch:eventIds", null); searchDoc.setPropertyValue("basicauditsearch:eventCategories", null); searchDoc.setPropertyValue("basicauditsearch:startDate", t1); searchDoc.setPropertyValue("basicauditsearch:endDate", t2); pp.setSearchDocumentModel(searchDoc); entries = (List<LogEntry>) pp.getCurrentPage(); assertEquals(5, entries.size()); } searchDoc.setPropertyValue("basicauditsearch:eventIds", null); searchDoc.setPropertyValue("basicauditsearch:eventCategories", null); searchDoc.setPropertyValue("basicauditsearch:startDate", null); searchDoc.setPropertyValue("basicauditsearch:endDate", null); // Get Proxy history pp = pps.getPageProvider("DOCUMENT_HISTORY_PROVIDER", si, Long.valueOf(30), Long.valueOf(0), new HashMap<String, Serializable>(), proxy); pp.setSearchDocumentModel(searchDoc); entries = (List<LogEntry>) pp.getCurrentPage(); if (verbose) { System.out.println("Proxy doc history"); dump(entries); } // 19 - 5 updates + create + proxyPublished int proxyEntriesCount = 19 - 5 + 1 + 1; assertEquals(proxyEntriesCount, entries.size()); assertEquals(Long.valueOf(startIdx).longValue(), entries.get(0).getId()); assertEquals(Long.valueOf(startIdx + proxyEntriesCount + 1).longValue(), entries.get(proxyEntriesCount - 1).getId()); // Get version 1 history pp = pps.getPageProvider("DOCUMENT_HISTORY_PROVIDER", si, Long.valueOf(20), Long.valueOf(0), new HashMap<String, Serializable>(), versions.get(0)); pp.setSearchDocumentModel(searchDoc); entries = (List<LogEntry>) pp.getCurrentPage(); if (verbose) { System.out.println("Version " + versions.get(0).getVersionLabel() + " doc history"); dump(entries); } // creation + 5 updates + update + checkin + created int version1EntriesCount = 1 + 5 + 1 + 1 + 1; if (version1EntriesCount == entries.size()) { assertEquals(Long.valueOf(startIdx).longValue(), entries.get(0).getId()); assertEquals(Long.valueOf(startIdx + version1EntriesCount - 1).longValue(), entries.get(version1EntriesCount - 1).getId()); } else { // because update even may be 1ms behind checkin/created ! assertEquals(version1EntriesCount - 1, entries.size()); } // get version 2 history pp = pps.getPageProvider("DOCUMENT_HISTORY_PROVIDER", si, Long.valueOf(20), Long.valueOf(0), new HashMap<String, Serializable>(), versions.get(1)); pp.setSearchDocumentModel(searchDoc); entries = (List<LogEntry>) pp.getCurrentPage(); if (verbose) { System.out.println("Version " + versions.get(1).getVersionLabel() + " doc history"); dump(entries); } // creation + 5x2 updates + checkin/update + checkin + created int versin2EntriesCount = 1 + 5 * 2 + 1 + 1 + 1 + 1; assertEquals(versin2EntriesCount, entries.size()); assertEquals(Long.valueOf(startIdx).longValue(), entries.get(0).getId()); assertEquals(Long.valueOf(startIdx + versin2EntriesCount).longValue(), entries.get(versin2EntriesCount - 1).getId()); } }