/* * JBoss, Home of Professional Open Source * Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors * as indicated by the @authors tag. All rights reserved. */ package org.jboss.elasticsearch.river.remote; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import junit.framework.Assert; import org.apache.http.HttpStatus; import org.elasticsearch.common.jackson.core.JsonParseException; import org.elasticsearch.common.logging.ESLoggerFactory; import org.elasticsearch.common.settings.SettingsException; import org.jboss.elasticsearch.river.remote.HttpRemoteSystemClientBase.HttpCallException; import org.jboss.elasticsearch.river.remote.exception.RemoteDocumentNotFoundException; import org.junit.Test; import org.mockito.Mockito; import static org.mockito.Mockito.mock; /** * Unit test for {@link GetJSONClient}. * * @author Vlastimil Elias (velias at redhat dot com) */ public class GetJSONClientTest { public static final void main(String... args) throws Exception { GetJSONClient tested = new GetJSONClient(); Map<String, Object> config = new HashMap<>(); config.put( GetJSONClient.CFG_URL_GET_DOCUMENTS, "https://issues.jboss.org/rest/api/2/search?jql=" + URLEncoder.encode("project=ORG and updatedDate >= \"2014/09/15 01:00\"", "UTF-8")); config.put(GetJSONClient.CFG_USERNAME, ""); config.put(GetJSONClient.CFG_PASSWORD, ""); config.put(GetJSONClient.CFG_TIMEOUT, "50s"); config.put(GetJSONClient.CFG_GET_DOCS_RES_FIELD_DOCUMENTS, "issues"); config.put(GetJSONClient.CFG_GET_DOCS_RES_FIELD_TOTALCOUNT, "total"); tested.init(mockEsIntegrationComponent(), config, false, null); ChangedDocumentsResults ret = tested.getChangedDocuments("ORG", 0, true, null); System.out.println("Documents count: " + ret.getDocumentsCount()); } @Test public void init() { try { Map<String, Object> config = new HashMap<String, Object>(); GetJSONClient tested = new GetJSONClient(); tested.init(mockEsIntegrationComponent(), config, false, null); Assert.fail("SettingsException not thrown"); } catch (SettingsException e) { // OK } try { Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "bad url format"); GetJSONClient tested = new GetJSONClient(); tested.init(mockEsIntegrationComponent(), config, false, null); Assert.fail("SettingsException not thrown"); } catch (SettingsException e) { // OK } // case - basic config, no getSpaces required, no authentication { IESIntegration esMock = mockEsIntegrationComponent(); GetJSONClient tested = new GetJSONClient(); Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents"); tested.init(esMock, config, false, null); Assert.assertEquals("http://test.org/documents", tested.urlGetDocuments); Assert.assertNull(tested.urlGetSpaces); Assert.assertNull(tested.getSpacesResField); Assert.assertFalse(tested.isAuthConfigured); Assert.assertEquals(GetJSONClient.HEADER_ACCEPT_DEFAULT, tested.headers.get("Accept")); Mockito.verify(esMock).createLogger(GetJSONClient.class); } // case - error - getSpaces is required but not configured! try { GetJSONClient tested = new GetJSONClient(); Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents"); tested.init(mockEsIntegrationComponent(), config, true, null); Assert.fail("SettingsException not thrown"); } catch (SettingsException e) { // OK } try { GetJSONClient tested = new GetJSONClient(); Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents"); config.put(GetJSONClient.CFG_URL_GET_SPACES, "bad url format"); tested.init(mockEsIntegrationComponent(), config, true, null); Assert.fail("SettingsException not thrown"); } catch (SettingsException e) { // OK } // case - basic config, getSpaces required, authentication with pwd in config, accept header changed { GetJSONClient tested = new GetJSONClient(); Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents"); config.put(GetJSONClient.CFG_URL_GET_SPACES, "http://test.org/spaces"); config.put(GetJSONClient.CFG_GET_SPACES_RESPONSE_FIELD, ""); config.put(GetJSONClient.CFG_USERNAME, "myuser"); config.put(GetJSONClient.CFG_PASSWORD, "paaswd"); config.put(GetJSONClient.CFG_HEADER_ACCEPT, "app/json"); Map<String,Object> valueForRootResFields = new HashMap<String,Object>(1); valueForRootResFields.put("dev", "container_info.dev"); config.put(GetJSONClient.CFG_GET_ROOT_RES_FIELDS_MAPPING, valueForRootResFields); IPwdLoader pwdLoaderMock = Mockito.mock(IPwdLoader.class); tested.init(mockEsIntegrationComponent(), config, true, pwdLoaderMock); Assert.assertEquals("http://test.org/documents", tested.urlGetDocuments); Assert.assertEquals("http://test.org/spaces", tested.urlGetSpaces); Assert.assertNull(tested.getSpacesResField); Assert.assertEquals("app/json", tested.headers.get("Accept")); Assert.assertTrue(tested.isAuthConfigured); Assert.assertNotNull(tested.getRootResFieldsMapping); Assert.assertEquals("container_info.dev", tested.getRootResFieldsMapping.get("dev")); Mockito.verifyZeroInteractions(pwdLoaderMock); } // case - basic config, getSpaces required, authentication with pwd from loader { GetJSONClient tested = new GetJSONClient(); Map<String, Object> config = new HashMap<String, Object>(); HashMap<String,String> passwordTest = new HashMap<>(); passwordTest.put("pwd", "paaswd"); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents"); config.put(GetJSONClient.CFG_URL_GET_SPACES, "http://test.org/spaces"); config.put(GetJSONClient.CFG_USERNAME, "myuser"); IPwdLoader pwdLoaderMock = Mockito.mock(IPwdLoader.class); Mockito.when(pwdLoaderMock.loadKey("myuser")).thenReturn(passwordTest); tested.init(mockEsIntegrationComponent(), config, true, pwdLoaderMock); Assert.assertEquals("http://test.org/documents", tested.urlGetDocuments); Assert.assertEquals("http://test.org/spaces", tested.urlGetSpaces); Assert.assertNull(tested.getSpacesResField); Assert.assertEquals(GetJSONClient.HEADER_ACCEPT_DEFAULT, tested.headers.get("Accept")); Assert.assertTrue(tested.isAuthConfigured); Mockito.verify(pwdLoaderMock).loadKey("myuser"); } // case - basic config, authentication put pwd not defined { GetJSONClient tested = new GetJSONClient(); Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents"); config.put(GetJSONClient.CFG_URL_GET_SPACES, "http://test.org/spaces"); config.put(GetJSONClient.CFG_USERNAME, "myuser"); tested.init(mockEsIntegrationComponent(), config, true, null); Assert.assertEquals("http://test.org/documents", tested.urlGetDocuments); Assert.assertEquals("http://test.org/spaces", tested.urlGetSpaces); Assert.assertNull(tested.getSpacesResField); Assert.assertEquals(GetJSONClient.HEADER_ACCEPT_DEFAULT, tested.headers.get("Accept")); Assert.assertFalse(tested.isAuthConfigured); } // case - basic config, api key in password file { GetJSONClient tested = new GetJSONClient(); Map<String, Object> config = new HashMap<String, Object>(); HashMap<String,String> passwordTest = new HashMap<>(); passwordTest.put("stackoverflow", "paaswd"); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents"); config.put(GetJSONClient.CFG_URL_GET_SPACES, "http://test.org/spaces"); config.put(GetJSONClient.CFG_EMBED_URL_API_KEY_USERNAME, "stackoverflow"); IPwdLoader pwdLoaderMock = Mockito.mock(IPwdLoader.class); Mockito.when(pwdLoaderMock.loadKey("stackoverflow")).thenReturn(passwordTest); tested.init(mockEsIntegrationComponent(), config, true, pwdLoaderMock); Assert.assertEquals("http://test.org/documents", tested.urlGetDocuments); Assert.assertEquals("http://test.org/spaces", tested.urlGetSpaces); Assert.assertNull(tested.getSpacesResField); Assert.assertEquals(GetJSONClient.HEADER_ACCEPT_DEFAULT, tested.headers.get("Accept")); Mockito.verify(pwdLoaderMock).loadKey("stackoverflow"); } // case - error when both detail UIRLa nd detail field are provided try { GetJSONClient tested = new GetJSONClient(); Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents"); config.put(GetJSONClient.CFG_URL_GET_DOCUMENT_DETAILS, "http://test.org/documents"); config.put(GetJSONClient.CFG_URL_GET_DOCUMENT_DETAILS_FIELD, "detailfield"); tested.init(mockEsIntegrationComponent(), config, false, null); Assert.fail("SettingsException not thrown"); } catch (SettingsException e) { // OK } // case - invalid numbers for updatedAfterInitialValue try { GetJSONClient tested = new GetJSONClient(); Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents"); config.put(GetJSONClient.CFG_URL_GET_DOCUMENT_DETAILS, "http://test.org/documents"); config.put(GetJSONClient.CFG_UPDATED_AFTER_INITIAL_VALUE, "notAnumber"); tested.init(mockEsIntegrationComponent(), config, false, null); Assert.fail("SettingsException not thrown"); } catch (SettingsException e) { // OK } // case - invalid numbers for updatedBeforeTimeSpanFromUpdatedAfter try { GetJSONClient tested = new GetJSONClient(); Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents"); config.put(GetJSONClient.CFG_URL_GET_DOCUMENT_DETAILS, "http://test.org/documents"); config.put(GetJSONClient.CFG_UPDATED_BEFORE_TIME_SPAN_FROM_UPDATED_AFTER, "notAnumber"); tested.init(mockEsIntegrationComponent(), config, false, null); Assert.fail("SettingsException not thrown"); } catch (SettingsException e) { // OK } } @Test public void getAllSpaces() throws Exception { // case - incorrect JSON response format try { IRemoteSystemClient tested = getAllSpaces_createTested("[ \"ORG\", \"PPP\"", null); tested.getAllSpaces(); Assert.fail("JsonParseException expected"); } catch (JsonParseException e) { // OK } // case - incorrect response content given to configured field try { IRemoteSystemClient tested = getAllSpaces_createTested("{ \"spaces\" : 10 }", "spaces"); tested.getAllSpaces(); Assert.fail("Exception expected"); } catch (Exception e) { // OK } // case - error because field is required but response doesn't contains map try { IRemoteSystemClient tested = getAllSpaces_createTested("[ \"ORG\", \"PPP\", 10]", "spaces"); tested.getAllSpaces(); Assert.fail("Exception expected"); } catch (Exception e) { // OK } // case - simple array in JSON response { IRemoteSystemClient tested = getAllSpaces_createTested("[ \"ORG\", \"PPP\", 10]", null); List<String> ret = tested.getAllSpaces(); Assert.assertNotNull(ret); Assert.assertEquals(3, ret.size()); Assert.assertTrue(ret.contains("ORG")); Assert.assertTrue(ret.contains("PPP")); Assert.assertTrue(ret.contains("10")); } // case - simple map in JSON response { IRemoteSystemClient tested = getAllSpaces_createTested("{ \"ORG\" : {}, \"PPP\" : {}}", null); List<String> ret = tested.getAllSpaces(); Assert.assertNotNull(ret); Assert.assertEquals(2, ret.size()); Assert.assertTrue(ret.contains("ORG")); Assert.assertTrue(ret.contains("PPP")); } // case - array in field of JSON response { IRemoteSystemClient tested = getAllSpaces_createTested("{ \"spaces\" : [ \"ORG\", \"PPP\", 10]}", "spaces"); List<String> ret = tested.getAllSpaces(); Assert.assertNotNull(ret); Assert.assertEquals(3, ret.size()); Assert.assertTrue(ret.contains("ORG")); Assert.assertTrue(ret.contains("PPP")); Assert.assertTrue(ret.contains("10")); } // case - map in field of JSON response { IRemoteSystemClient tested = getAllSpaces_createTested("{ \"spaces\" : { \"ORG\" : {}, \"PPP\" : {}}}", "spaces"); List<String> ret = tested.getAllSpaces(); Assert.assertNotNull(ret); Assert.assertEquals(2, ret.size()); Assert.assertTrue(ret.contains("ORG")); Assert.assertTrue(ret.contains("PPP")); } // case - evaluation of nested field of JSON response { IRemoteSystemClient tested = getAllSpaces_createTested( "{ \"ret\" : { \"spaces\" : { \"ORG\" : {}, \"PPP\" : {}}}}", "ret.spaces"); List<String> ret = tested.getAllSpaces(); Assert.assertNotNull(ret); Assert.assertEquals(2, ret.size()); Assert.assertTrue(ret.contains("ORG")); Assert.assertTrue(ret.contains("PPP")); } } private IRemoteSystemClient getAllSpaces_createTested(final String returnJson, String configSpacesResponseField) { Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents"); config.put(GetJSONClient.CFG_URL_GET_SPACES, "http://test.org/spaces"); config.put(GetJSONClient.CFG_GET_SPACES_RESPONSE_FIELD, configSpacesResponseField); IRemoteSystemClient tested = new GetJSONClient() { @Override protected HttpResponseContent performHttpGetCall(String url, Map<String, String> headers) throws Exception, HttpCallException { Assert.assertEquals("http://test.org/spaces", url); return new HttpResponseContent("application/json", returnJson.getBytes("UTF-8")); }; }; tested.init(mockEsIntegrationComponent(), config, true, null); return tested; } @Test public void getChangedDocuments() throws Exception { try { Map<String, Object> config = new HashMap<String, Object>(); config .put( GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://totallyrandomdomain.org/documents?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}&it={indexingType}"); IRemoteSystemClient tested = createTestedInstance(config, "invalid json", "http://totallyrandomdomain.org/documents?docSpace=myspace&docUpdatedAfter=&startAtIndex=0&it=full"); tested.getChangedDocuments("myspace", 0, true, null); Assert.fail("JsonParseException expected"); } catch (JsonParseException e) { // OK } // case - simple response with direct list, no total { Map<String, Object> config = new HashMap<String, Object>(); config .put( GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://totallyrandomdomain.org/documents?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}&it={indexingType}"); IRemoteSystemClient tested = createTestedInstance(config, "[{\"key\" : \"a\"},{\"key\" : \"b\"}]", "http://totallyrandomdomain.org/documents?docSpace=myspace&docUpdatedAfter=1256&startAtIndex=12&it=inc"); ChangedDocumentsResults ret = tested.getChangedDocuments("myspace", 12, false, new Date(1256l)); Assert.assertEquals(2, ret.getDocumentsCount()); Assert.assertEquals(12, ret.getStartAt()); Assert.assertEquals(null, ret.getTotal()); Assert.assertEquals("a", ret.getDocuments().get(0).get("key")); Assert.assertEquals("b", ret.getDocuments().get(1).get("key")); } // case - object response with documents and total in nested fields { Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://totallyrandomdomain.org/documents?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}"); config.put(GetJSONClient.CFG_GET_DOCS_RES_FIELD_DOCUMENTS, "response.items"); config.put(GetJSONClient.CFG_GET_DOCS_RES_FIELD_TOTALCOUNT, "response.total"); Map<String,Object> valueForRootResFields = new HashMap<String,Object>(1); valueForRootResFields.put("dev", "response.container_info.dev"); config.put(GetJSONClient.CFG_GET_ROOT_RES_FIELDS_MAPPING, valueForRootResFields); IRemoteSystemClient tested = createTestedInstance(config, "{\"response\": { \"total\":20 , \"container_info\": { \"dev\":\"false\"}," + "\"items\":[{\"key\" : \"a\"},{\"key\" : \"b\"}]}}", "http://totallyrandomdomain.org/documents?docSpace=myspace&docUpdatedAfter=1256&startAtIndex=12"); ChangedDocumentsResults ret = tested.getChangedDocuments("myspace", 12, false, new Date(1256l)); Assert.assertEquals(2, ret.getDocumentsCount()); Assert.assertEquals(12, ret.getStartAt()); Assert.assertEquals(new Integer(20), ret.getTotal()); Assert.assertEquals("a", ret.getDocuments().get(0).get("key")); Assert.assertEquals("b", ret.getDocuments().get(1).get("key")); Assert.assertEquals("false", ret.getDocuments().get(0).get("dev")); Assert.assertEquals("false", ret.getDocuments().get(1).get("dev")); } // case - test that forced pause parameter is correctly parsed and processed { Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://averynotexistinghostname.org/documents?docSpace={space}&docUpdatedAfter={updatedAfter}"); config.put(GetJSONClient.CFG_FORCED_INDEXING_PAUSE_FIELD,"backoff"); config.put(GetJSONClient.CFG_FORCED_INDEXING_PAUSE_FIELD_TIME_UNIT, "SECONDS"); config.put(GetJSONClient.CFG_GET_DOCS_RES_FIELD_DOCUMENTS, "items"); Long timeBeforeExecution = System.currentTimeMillis(); IRemoteSystemClient tested = createTestedInstance(config, "{ \"backoff\": 2, \"items\":[] }", "http://averynotexistinghostname.org/documents?docSpace=space&docUpdatedAfter=1000000000000"); tested.getChangedDocuments("space", 0, true, new Date(1000000000000L)); tested.getChangedDocuments("space", 0, true, new Date(1000000000000L)); Assert.assertTrue(System.currentTimeMillis() >= timeBeforeExecution+2000L); // now we'll see if delay is properly cleared if the time already passed during processing try { Thread.sleep( 2000 ); } catch( InterruptedException e ) { //ignore } timeBeforeExecution = System.currentTimeMillis(); tested.getChangedDocuments("space", 0, true, new Date(1000000000000L)); Assert.assertTrue( System.currentTimeMillis()-timeBeforeExecution < 2000 ); } // case - test that minimal request delay parameter is correctly parsed and processed { Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://averynotexistinghostname.org/documents?docSpace={space}&docUpdatedAfter={updatedAfter}"); config.put(GetJSONClient.CFG_MIN_GET_DOCUMENTS_DELAY,"2000"); config.put(GetJSONClient.CFG_GET_DOCS_RES_FIELD_DOCUMENTS, "items"); Long timeBeforeExecution = System.currentTimeMillis(); IRemoteSystemClient tested = createTestedInstance(config, "{ \"items\":[] }", "http://averynotexistinghostname.org/documents?docSpace=space&docUpdatedAfter=1000000000000"); tested.getChangedDocuments("space", 0, true, new Date(1000000000000L)); Assert.assertTrue(System.currentTimeMillis() >= timeBeforeExecution+2000L); } } private IRemoteSystemClient createTestedInstance(Map<String, Object> config, final String returnJson, final String expectadCallUrl) { IRemoteSystemClient tested = new GetJSONClient() { @Override protected HttpResponseContent performHttpCall(String url, Map<String, String> headers, HttpMethodType methodType) throws Exception, HttpCallException { Assert.assertEquals(expectadCallUrl, url); return new HttpResponseContent("application/json", returnJson.getBytes("UTF-8")); }; }; tested.init(mockEsIntegrationComponent(), config, false, null); return tested; } private IRemoteSystemClient createTestedInstanceWithHttpCallException(Map<String, Object> config, final int returnHttpCode) { IRemoteSystemClient tested = new GetJSONClient() { @Override protected HttpResponseContent performHttpGetCall(String url, Map<String, String> headers) throws Exception, HttpCallException { throw new HttpCallException(url, returnHttpCode, "response content"); }; }; tested.init(mockEsIntegrationComponent(), config, false, null); return tested; } @SuppressWarnings("unchecked") @Test public void getChangedDocumentDetails_urlConfigured() throws Exception { // case - test REST not called if URL is not configured { Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}"); IRemoteSystemClient tested = createTestedInstance(config, "invalid json", "not called"); Assert.assertNull(tested.getChangedDocumentDetails("myspace", "myid", null)); } // case - test bad JSON returned try { Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}"); config.put(GetJSONClient.CFG_URL_GET_DOCUMENT_DETAILS, "http://test.org/document?docSpace={space}&id={id}"); IRemoteSystemClient tested = createTestedInstance(config, "invalid json", "http://test.org/document?docSpace=myspace&id=myid"); tested.getChangedDocumentDetails("myspace", "myid", null); Assert.fail("JsonParseException expected"); } catch (JsonParseException e) { // OK } // case - test JSON object returned { Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}"); config.put(GetJSONClient.CFG_URL_GET_DOCUMENT_DETAILS, "http://test.org/document?docSpace={space}&id={id}"); IRemoteSystemClient tested = createTestedInstance(config, "{\"item1\":\"val1\"}", "http://test.org/document?docSpace=myspace&id=myid"); Object ret = tested.getChangedDocumentDetails("myspace", "myid", null); Assert.assertNotNull(ret); Assert.assertTrue(ret instanceof Map); Map<String, Object> rm = (Map<String, Object>) ret; Assert.assertEquals(1, rm.size()); Assert.assertEquals("val1", rm.get("item1")); } // case - test JSON array returned { Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}"); config.put(GetJSONClient.CFG_URL_GET_DOCUMENT_DETAILS, "http://test.org/document?docSpace={space}&id={id}"); IRemoteSystemClient tested = createTestedInstance(config, "[{\"item1\":\"val1\"}]", "http://test.org/document?docSpace=myspace&id=myid"); Object ret = tested.getChangedDocumentDetails("myspace", "myid", null); Assert.assertNotNull(ret); Assert.assertTrue(ret instanceof List); List<Map<String, Object>> rm = (List<Map<String, Object>>) ret; Assert.assertEquals("val1", rm.get(0).get("item1")); } // case - HTTP code 404 must throw special exception not to fail indexing completely- issue #11 { Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}"); config.put(GetJSONClient.CFG_URL_GET_DOCUMENT_DETAILS, "http://test.org/document?docSpace={space}&id={id}"); IRemoteSystemClient tested = createTestedInstanceWithHttpCallException(config, HttpStatus.SC_NOT_FOUND); try { tested.getChangedDocumentDetails("myspace", "myid", null); Assert.fail("RemoteDocumentNotFoundException expected"); } catch (RemoteDocumentNotFoundException e) { // OK } } // case - other HTTP codes must throw RestCallHttpException to fail indexing completely { Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}"); config.put(GetJSONClient.CFG_URL_GET_DOCUMENT_DETAILS, "http://test.org/document?docSpace={space}&id={id}"); IRemoteSystemClient tested = createTestedInstanceWithHttpCallException(config, HttpStatus.SC_FORBIDDEN); try { tested.getChangedDocumentDetails("myspace", "myid", null); Assert.fail("RestCallHttpException expected"); } catch (HttpCallException e) { // OK } } } @SuppressWarnings("unchecked") @Test public void getChangedDocumentDetails_urlFieldConfigured() throws Exception { // case - no item data available { Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}"); config.put(GetJSONClient.CFG_URL_GET_DOCUMENT_DETAILS_FIELD, "detailUrlField"); IRemoteSystemClient tested = createTestedInstance(config, "invalid json", "http://test.org/document/5656525"); Assert.assertNull(tested.getChangedDocumentDetails("myspace", "myid", null)); } // case - no URL available in item data { Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}"); config.put(GetJSONClient.CFG_URL_GET_DOCUMENT_DETAILS_FIELD, "detailUrlField"); IRemoteSystemClient tested = createTestedInstance(config, "invalid json", "http://test.org/document/5656525"); Map<String, Object> itemData = new HashMap<>(); Assert.assertNull(tested.getChangedDocumentDetails("myspace", "myid", itemData)); } // case - invalid object available in item data instead of String with URL { Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}"); config.put(GetJSONClient.CFG_URL_GET_DOCUMENT_DETAILS_FIELD, "detailUrlField"); IRemoteSystemClient tested = createTestedInstance(config, "invalid json", "http://test.org/document/5656525"); Map<String, Object> itemData = new HashMap<>(); itemData.put("detailUrlField", new HashMap<>()); Assert.assertNull(tested.getChangedDocumentDetails("myspace", "myid", itemData)); } // case - invalid URL format in item data { Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}"); config.put(GetJSONClient.CFG_URL_GET_DOCUMENT_DETAILS_FIELD, "detailUrlField"); IRemoteSystemClient tested = createTestedInstance(config, "invalid json", "http://test.org/document/5656525"); Map<String, Object> itemData = new HashMap<>(); itemData.put("detailUrlField", "invalid url format"); Assert.assertNull(tested.getChangedDocumentDetails("myspace", "myid", itemData)); } // case - test bad JSON returned try { Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}"); config.put(GetJSONClient.CFG_URL_GET_DOCUMENT_DETAILS_FIELD, "detailUrlField"); IRemoteSystemClient tested = createTestedInstance(config, "invalid json", "http://test.org/document/5656525"); Map<String, Object> itemData = new HashMap<>(); itemData.put("detailUrlField", "http://test.org/document/5656525"); tested.getChangedDocumentDetails("myspace", "myid", itemData); Assert.fail("JsonParseException expected"); } catch (JsonParseException e) { // OK } // case - test JSON object returned, dot notation for field configuration { Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}"); config.put(GetJSONClient.CFG_URL_GET_DOCUMENT_DETAILS_FIELD, "nested.detailUrlField"); IRemoteSystemClient tested = createTestedInstance(config, "{\"item1\":\"val1\"}", "http://test.org/document/5656525"); Map<String, Object> itemData = new HashMap<>(); itemData.put("detailUrlField", "http://test.org/document/5656525"); Map<String, Object> itemData2 = new HashMap<>(); itemData2.put("nested", itemData); Object ret = tested.getChangedDocumentDetails("myspace", "myid", itemData2); Assert.assertNotNull(ret); Assert.assertTrue(ret instanceof Map); Map<String, Object> rm = (Map<String, Object>) ret; Assert.assertEquals(1, rm.size()); Assert.assertEquals("val1", rm.get("item1")); } // case - HTTP code 404 must throw special exception not to fail indexing completely- issue #11 { Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}"); config.put(GetJSONClient.CFG_URL_GET_DOCUMENT_DETAILS_FIELD, "detailUrlField"); IRemoteSystemClient tested = createTestedInstanceWithHttpCallException(config, HttpStatus.SC_NOT_FOUND); try { Map<String, Object> itemData = new HashMap<>(); itemData.put("detailUrlField", "http://test.org/document/5656525"); tested.getChangedDocumentDetails("myspace", "myid", itemData); Assert.fail("RemoteDocumentNotFoundException expected"); } catch (RemoteDocumentNotFoundException e) { // OK } } // case - other HTTP codes must throw RestCallHttpException to fail indexing completely { Map<String, Object> config = new HashMap<String, Object>(); config.put(GetJSONClient.CFG_URL_GET_DOCUMENTS, "http://test.org/documents?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}"); config.put(GetJSONClient.CFG_URL_GET_DOCUMENT_DETAILS_FIELD, "detailUrlField"); IRemoteSystemClient tested = createTestedInstanceWithHttpCallException(config, HttpStatus.SC_FORBIDDEN); try { Map<String, Object> itemData = new HashMap<>(); itemData.put("detailUrlField", "http://test.org/document/5656525"); tested.getChangedDocumentDetails("myspace", "myid", itemData); Assert.fail("RestCallHttpException expected"); } catch (HttpCallException e) { // OK } } } @Test public void enhanceUrlGetDocumentDetails() throws UnsupportedEncodingException { Assert.assertEquals("http://test.org?docSpace=myspace&id=myid", GetJSONClient.enhanceUrlGetDocumentDetails("http://test.org?docSpace={space}&id={id}", "myspace", "myid")); // param URL encoding test Assert.assertEquals("http://test.org?docSpace=myspac%26e&id=my%26id", GetJSONClient.enhanceUrlGetDocumentDetails("http://test.org?docSpace={space}&id={id}", "myspac&e", "my&id")); } @Test public void enhanceUrlGetDocuments() throws UnsupportedEncodingException { Assert .assertEquals( "http://test.org?docSpace=myspace&docUpdatedAfter=123456&docUpdatedBefore=123756&startAtIndex=0&it=full", GetJSONClient .enhanceUrlGetDocuments( "http://test.org?docSpace={space}&docUpdatedAfter={updatedAfter}&docUpdatedBefore={updatedBefore}&startAtIndex={startAtIndex}&it={indexingType}", "myspace", new Date(123456l), DateTimeUtils.CUSTOM_MILLISEC_EPOCH_DATETIME_FORMAT, null, 300L, 0, true, null)); // Testing if not providing updatedAfter date format doesn't break the call and uses the correct milisecond-based format as the default. Assert .assertEquals( "http://test.org?docSpace=myspace&docUpdatedAfter=123456&startAtIndex=0&it=full", GetJSONClient .enhanceUrlGetDocuments( "http://test.org?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}&it={indexingType}", "myspace", new Date(123456l), null, null, null, 0, true, null)); Assert .assertEquals( "http://test.org?docSpace=myspace&docUpdatedAfter=20150404%26Fun&startAtIndex=0&it=full", GetJSONClient .enhanceUrlGetDocuments( "http://test.org?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}&it={indexingType}", "myspace", new Date(1428113546000L), "yyyyMMdd'&Fun'", null, null, 0, true, null)); Assert .assertEquals( "http://test.org?docSpace=my%26space&docUpdatedAfter=&startAtIndex=125&it=inc", GetJSONClient .enhanceUrlGetDocuments( "http://test.org?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}&it={indexingType}", "my&space", null, null, null, null, 125, false, null)); Assert .assertEquals( "http://test.org?docSpace=my%26space&docUpdatedAfter=1349108160000&startAtIndex=125&it=inc", GetJSONClient .enhanceUrlGetDocuments( "http://test.org?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}&it={indexingType}", "my&space", null, null, 1349108160000L, null, 125, false, null)); Assert .assertEquals( "http://test.org?docSpace=my%26space&docUpdatedAfter=1349108160000&startAtIndex=125&it=inc&apikey=password", GetJSONClient .enhanceUrlGetDocuments( "http://test.org?docSpace={space}&docUpdatedAfter={updatedAfter}&startAtIndex={startAtIndex}&it={indexingType}&apikey={apiKey}", "my&space", null, null, 1349108160000L, null, 125, false, "password")); } protected static IESIntegration mockEsIntegrationComponent() { IESIntegration esIntegrationMock = mock(IESIntegration.class); Mockito.when(esIntegrationMock.createLogger(Mockito.any(Class.class))).thenReturn( ESLoggerFactory.getLogger(GetJSONClient.class.getName())); return esIntegrationMock; } }