/* Copyright 2013 Red Hat, Inc. and/or its affiliates. This file is part of lightblue. This program is free software: you can redistribute it and/or modify it under the terms of the GNU 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 General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.redhat.lightblue.savedsearch; import java.util.List; import java.util.ArrayList; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import com.redhat.lightblue.util.test.AbstractJsonSchemaTest; import com.redhat.lightblue.metadata.EntityMetadata; import com.redhat.lightblue.metadata.PredefinedFields; import com.redhat.lightblue.metadata.parser.Extensions; import com.redhat.lightblue.metadata.parser.JSONMetadataParser; import com.redhat.lightblue.metadata.TypeResolver; import com.redhat.lightblue.metadata.Metadata; import com.redhat.lightblue.metadata.types.DefaultTypes; import com.redhat.lightblue.metadata.test.DatabaseMetadata; import com.redhat.lightblue.metadata.MetadataListener; import com.redhat.lightblue.crud.validator.DefaultFieldConstraintValidators; import com.redhat.lightblue.crud.validator.EmptyEntityConstraintValidators; import com.redhat.lightblue.mediator.Mediator; import com.redhat.lightblue.mediator.OperationContext; import com.redhat.lightblue.crud.*; import com.redhat.lightblue.query.*; import com.redhat.lightblue.Request; import com.redhat.lightblue.Response; import com.redhat.lightblue.TestDataStoreParser; import com.redhat.lightblue.util.JsonDoc; import com.redhat.lightblue.util.Path; public class SavedSearchCacheTest extends AbstractJsonSchemaTest { private Mediator mediator; private class TestMetadata extends DatabaseMetadata { @Override public EntityMetadata getEntityMetadata(String entityName, String version) { return getMd(entityName + ".json"); } } private EntityMetadata getMd(String fname) { try { JsonNode node = loadJsonNode(fname); Extensions<JsonNode> extensions = new Extensions<>(); extensions.addDefaultExtensions(); extensions.registerDataStoreParser("mongo", new TestDataStoreParser<JsonNode>()); TypeResolver resolver = new DefaultTypes(); JSONMetadataParser parser = new JSONMetadataParser(extensions, resolver, JsonNodeFactory.instance); EntityMetadata md = parser.parseEntityMetadata(node); PredefinedFields.ensurePredefinedFields(md); return md; } catch (Exception e) { throw new RuntimeException(e); } } private static final class TestMediator extends Mediator { OperationContext ctx; public TestMediator(Metadata md, Factory factory) { super(md, factory); } @Override protected OperationContext newCtx(Request request, CRUDOperation CRUDOperation) { return ctx = super.newCtx(request, CRUDOperation); } } private static OperationContext getLastContext(Mediator m) { return ((TestMediator) m).ctx; } List<JsonDoc> foundDocs=null; private class TestCrudController implements CRUDController { @Override public CRUDInsertionResponse insert(CRUDOperationContext ctx, Projection projection) { return null; } @Override public CRUDSaveResponse save(CRUDOperationContext ctx, boolean upsert, Projection projection) { return null; } @Override public CRUDUpdateResponse update(CRUDOperationContext ctx, QueryExpression query, UpdateExpression update, Projection projection) { return new CRUDUpdateResponse(); } @Override public CRUDDeleteResponse delete(CRUDOperationContext ctx, QueryExpression query) { return new CRUDDeleteResponse(); } @Override public CRUDFindResponse find(CRUDOperationContext ctx, QueryExpression query, Projection projection, Sort sort, Long from, Long to) { CRUDFindResponse r=new CRUDFindResponse(); if(foundDocs!=null) { r.setSize(foundDocs.size()); ctx.setDocumentStream(DocumentStream.map(new ListDocumentStream<JsonDoc>(foundDocs),d->new DocCtx(d))); } else { ctx.setDocumentStream(new ListDocumentStream<DocCtx>(new ArrayList<DocCtx>())); } return r; } @Override public void updatePredefinedFields(CRUDOperationContext ctx, JsonDoc doc) {} @Override public MetadataListener getMetadataListener() {return null;} } @Before public void initMediator() throws Exception { Factory factory = new Factory(); factory.addFieldConstraintValidators(new DefaultFieldConstraintValidators()); factory.addEntityConstraintValidators(new EmptyEntityConstraintValidators()); factory.addCRUDController("mongo", new TestCrudController()); mediator = new TestMediator(new TestMetadata(), factory); } @Test public void findNoneTest() throws Exception { SavedSearchCache cache=new SavedSearchCache(null); Assert.assertNull(cache.getSavedSearch(mediator,null,"testSearch","test","1.0")); } @Test public void findOneTest_with_version() throws Exception { SavedSearchCache cache=new SavedSearchCache(null); foundDocs=new ArrayList<JsonDoc>(); foundDocs.add(makeSearch("testSearch","test","{\"field\":\"a\",\"op\":\"=\",\"rvalue\":1}","1.0.0")); JsonNode doc=cache.getSavedSearch(mediator,null,"testSearch","test","1.0.0"); Assert.assertTrue(doc instanceof ObjectNode); } // Find the search with entity version 1.0.0, there is one search with no version so that should be used for all versions @Test public void findOneTest_with_version_null_test_ver() throws Exception { SavedSearchCache cache=new SavedSearchCache(null); foundDocs=new ArrayList<JsonDoc>(); foundDocs.add(makeSearch("testSearch","test","{\"field\":\"a\",\"op\":\"=\",\"rvalue\":1}")); JsonNode doc=cache.getSavedSearch(mediator,null,"testSearch","test","1.0.0"); Assert.assertTrue(doc instanceof ObjectNode); } // Find the search with entity version 1.0.0, there are two searches, one without version, one with 1.0.0. Return the 1.0.0 version @Test public void findOneTest_two_searches() throws Exception { SavedSearchCache cache=new SavedSearchCache(null); foundDocs=new ArrayList<JsonDoc>(); foundDocs.add(makeSearch("testSearch","test","{\"field\":\"a\",\"op\":\"=\",\"rvalue\":1}")); foundDocs.add(makeSearch("testSearch","test","{\"field\":\"a\",\"op\":\"=\",\"rvalue\":1}","1.0.0")); JsonNode doc=cache.getSavedSearch(mediator,null,"testSearch","test","1.0.0"); Assert.assertTrue(doc instanceof ObjectNode); Assert.assertEquals("1.0.0",doc.get("versions").get(0).asText()); } // Find the search with entity version 1.0.0, there are two searches, one without version, one with 1.0. Return the 1.0.0 version @Test public void findOneTest_two_searches_partial1() throws Exception { SavedSearchCache cache=new SavedSearchCache(null); foundDocs=new ArrayList<JsonDoc>(); foundDocs.add(makeSearch("testSearch","test","{\"field\":\"a\",\"op\":\"=\",\"rvalue\":1}")); foundDocs.add(makeSearch("testSearch","test","{\"field\":\"a\",\"op\":\"=\",\"rvalue\":1}","1.0")); JsonNode doc=cache.getSavedSearch(mediator,null,"testSearch","test","1.0.0"); Assert.assertTrue(doc instanceof ObjectNode); Assert.assertEquals("1.0",doc.get("versions").get(0).asText()); } // Find the search with entity version 1.0.0, there are two searches, one without version, one with 1. Return the 1 version @Test public void findOneTest_two_searches_partial2() throws Exception { SavedSearchCache cache=new SavedSearchCache(null); foundDocs=new ArrayList<JsonDoc>(); foundDocs.add(makeSearch("testSearch","test","{\"field\":\"a\",\"op\":\"=\",\"rvalue\":1}")); foundDocs.add(makeSearch("testSearch","test","{\"field\":\"a\",\"op\":\"=\",\"rvalue\":1}","1")); JsonNode doc=cache.getSavedSearch(mediator,null,"testSearch","test","1.0.0"); Assert.assertTrue(doc instanceof ObjectNode); Assert.assertEquals("1",doc.get("versions").get(0).asText()); } // Find the search with entity version 2.0.0, there are two searches, one with version 3, one with 1. Return none @Test public void findOneTest_two_searches_partial_none() throws Exception { SavedSearchCache cache=new SavedSearchCache(null); foundDocs=new ArrayList<JsonDoc>(); foundDocs.add(makeSearch("testSearch","test","{\"field\":\"a\",\"op\":\"=\",\"rvalue\":1}","3")); foundDocs.add(makeSearch("testSearch","test","{\"field\":\"a\",\"op\":\"=\",\"rvalue\":1}","1")); JsonNode doc=cache.getSavedSearch(mediator,null,"testSearch","test","2.0.0"); Assert.assertNull(doc); } // Find the search with entity version 2.0.0, there are three searches, one with version 3, one with null, one with 1. Return null search @Test public void findOneTest_two_searches_partial_default() throws Exception { SavedSearchCache cache=new SavedSearchCache(null); foundDocs=new ArrayList<JsonDoc>(); foundDocs.add(makeSearch("testSearch","test","{\"field\":\"a\",\"op\":\"=\",\"rvalue\":1}","3")); foundDocs.add(makeSearch("testSearch","test","{\"field\":\"a\",\"op\":\"=\",\"rvalue\":1}","1")); foundDocs.add(makeSearch("testSearch","test","{\"field\":\"a\",\"op\":\"=\",\"rvalue\":1}")); JsonNode doc=cache.getSavedSearch(mediator,null,"testSearch","test","2.0.0"); Assert.assertEquals(0,doc.get("versions").size()); } private JsonDoc makeSearch(String searchName,String entity,String query, String...versions) { JsonDoc doc=new JsonDoc(JsonNodeFactory.instance.objectNode()); doc.modify(new Path("name"),JsonNodeFactory.instance.textNode(searchName),true); doc.modify(new Path("entity"),JsonNodeFactory.instance.textNode(entity),true); doc.modify(new Path("versions"),makeArray(versions),true); doc.modify(new Path("query"),JsonNodeFactory.instance.textNode(query),true); return doc; } private JsonNode makeArray(String...versions) { ArrayNode node=JsonNodeFactory.instance.arrayNode(); for(String x:versions) if(x==null) node.add(JsonNodeFactory.instance.nullNode()); else node.add(JsonNodeFactory.instance.textNode(x)); return node; } }