/**
* Copyright (c) Codice Foundation
* <p>
* This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or any later version.
* <p>
* 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
* Lesser General Public License for more details. A copy of the GNU Lesser General Public License
* is distributed along with this program and can be found at
* <http://www.gnu.org/licenses/lgpl.html>.
*/
package org.codice.ddf.registry.api.impl;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.core.Is.is;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.stream.Collectors;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
import org.codice.ddf.cxf.SecureCxfClientFactory;
import org.codice.ddf.parser.Parser;
import org.codice.ddf.parser.xml.XmlParser;
import org.codice.ddf.registry.common.RegistryConstants;
import org.codice.ddf.registry.common.metacard.RegistryObjectMetacardType;
import org.codice.ddf.registry.schemabindings.helper.MetacardMarshaller;
import org.codice.ddf.spatial.ogc.csw.catalog.common.Csw;
import org.codice.ddf.spatial.ogc.csw.catalog.common.CswAxisOrder;
import org.codice.ddf.spatial.ogc.csw.catalog.common.CswSourceConfiguration;
import org.codice.ddf.spatial.ogc.csw.catalog.common.source.CswFilterFactory;
import org.codice.ddf.spatial.ogc.csw.catalog.common.transaction.CswTransactionRequest;
import org.codice.ddf.spatial.ogc.csw.catalog.common.transformer.TransformerManager;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.opengis.filter.Filter;
import org.osgi.framework.BundleContext;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import com.thoughtworks.xstream.converters.Converter;
import ddf.catalog.Constants;
import ddf.catalog.data.Metacard;
import ddf.catalog.data.Result;
import ddf.catalog.data.impl.MetacardImpl;
import ddf.catalog.data.impl.ResultImpl;
import ddf.catalog.filter.FilterAdapter;
import ddf.catalog.filter.FilterBuilder;
import ddf.catalog.filter.FilterDelegate;
import ddf.catalog.filter.proxy.adapter.GeotoolsFilterAdapterImpl;
import ddf.catalog.filter.proxy.builder.GeotoolsFilterBuilder;
import ddf.catalog.operation.CreateRequest;
import ddf.catalog.operation.CreateResponse;
import ddf.catalog.operation.OperationTransaction;
import ddf.catalog.operation.QueryRequest;
import ddf.catalog.operation.SourceResponse;
import ddf.catalog.operation.impl.CreateRequestImpl;
import ddf.catalog.operation.impl.DeleteRequestImpl;
import ddf.catalog.operation.impl.OperationTransactionImpl;
import ddf.catalog.operation.impl.QueryImpl;
import ddf.catalog.operation.impl.QueryRequestImpl;
import ddf.catalog.operation.impl.SourceResponseImpl;
import ddf.catalog.operation.impl.UpdateRequestImpl;
import ddf.catalog.source.IngestException;
import ddf.catalog.source.UnsupportedQueryException;
import ddf.security.SecurityConstants;
import ddf.security.Subject;
import ddf.security.encryption.EncryptionService;
import net.opengis.cat.csw.v_2_0_2.BriefRecordType;
import net.opengis.cat.csw.v_2_0_2.CapabilitiesType;
import net.opengis.cat.csw.v_2_0_2.ElementSetType;
import net.opengis.cat.csw.v_2_0_2.InsertResultType;
import net.opengis.cat.csw.v_2_0_2.TransactionResponseType;
import net.opengis.cat.csw.v_2_0_2.TransactionSummaryType;
import net.opengis.cat.csw.v_2_0_2.dc.elements.SimpleLiteral;
import net.opengis.filter.v_1_1_0.FilterType;
public class TestRegistryStore {
private RegistryStoreImpl registryStore;
private MetacardMarshaller marshaller;
private Parser parser;
private BundleContext context;
private Converter provider;
private CswSourceConfiguration cswSourceConfiguration;
private SecureCxfClientFactory factory;
private TransformerManager transformer;
private ConfigurationAdmin configAdmin;
private FilterAdapter filterAdapter = new GeotoolsFilterAdapterImpl();
private FilterBuilder filterBuilder = spy(new GeotoolsFilterBuilder());
private Configuration configuration;
private Subject subject;
private EncryptionService encryptionService;
private List<Result> queryResults;
private Dictionary<String, Object> properties;
private boolean availability = false;
@Before
public void setup() throws Exception {
parser = new XmlParser();
marshaller = new MetacardMarshaller(new XmlParser());
context = mock(BundleContext.class);
provider = mock(Converter.class);
cswSourceConfiguration = new CswSourceConfiguration();
factory = mock(SecureCxfClientFactory.class);
transformer = mock(TransformerManager.class);
encryptionService = mock(EncryptionService.class);
configAdmin = mock(ConfigurationAdmin.class);
configuration = mock(Configuration.class);
subject = mock(Subject.class);
queryResults = new ArrayList<>();
registryStore = spy(new RegistryStoreImpl(context,
cswSourceConfiguration,
provider,
factory,
encryptionService) {
@Override
protected void validateOperation() {
}
@Override
public boolean isAvailable(){
return availability;
}
@Override
protected SourceResponse query(QueryRequest queryRequest, ElementSetType elementSetName,
List<QName> elementNames, Csw csw) throws UnsupportedQueryException {
if (queryResults == null) {
throw new UnsupportedQueryException("Test - Bad Query");
}
return new SourceResponseImpl(queryRequest, queryResults);
}
@Override
protected CapabilitiesType getCapabilities() {
return mock(CapabilitiesType.class);
}
@Override
public void configureCswSource() {};
@Override
protected Subject getSystemSubject(){
return subject;
}
@Override
BundleContext getBundleContext() {
return context;
}
});
registryStore.setFilterBuilder(filterBuilder);
registryStore.setFilterAdapter(filterAdapter);
registryStore.setConfigAdmin(configAdmin);
registryStore.setMetacardMarshaller(new MetacardMarshaller(parser));
registryStore.setSchemaTransformerManager(transformer);
registryStore.setAutoPush(true);
registryStore.setRegistryUrl("http://test.url:0101/example");
properties = new Hashtable<>();
properties.put(RegistryStoreImpl.ID, "registryId");
registryStore.setMetacardMarshaller(marshaller);
when(configAdmin.getConfiguration(any())).thenReturn(configuration);
when(configuration.getProperties()).thenReturn(properties);
}
@Test(expected = IngestException.class)
public void testCreateNonRegistryMetacard() throws Exception {
MetacardImpl mcard = getDefaultMetacard();
mcard.setAttribute(RegistryObjectMetacardType.REGISTRY_ID, null);
CreateRequest request = new CreateRequestImpl(mcard);
registryStore.create(request);
}
@Test
public void testCreateWithExistingMetacard() throws Exception {
Metacard mcard = getDefaultMetacard();
queryResults.add(new ResultImpl(mcard));
CreateRequest request = new CreateRequestImpl(mcard);
CreateResponse response = registryStore.create(request);
assertThat(response.getCreatedMetacards()
.get(0), is(mcard));
}
@Test
public void testCreateNoExistingMetacard() throws Exception {
Metacard mcard = getDefaultMetacard();
Csw csw = mock(Csw.class);
TransactionResponseType responseType = mock(TransactionResponseType.class);
InsertResultType insertResultType = mock(InsertResultType.class);
BriefRecordType briefRecord = mock(BriefRecordType.class);
JAXBElement identifier = mock(JAXBElement.class);
SimpleLiteral literal = mock(SimpleLiteral.class);
when(literal.getContent()).thenReturn(Collections.singletonList(mcard.getId()));
when(identifier.getValue()).thenReturn(literal);
when(briefRecord.getIdentifier()).thenReturn(Collections.singletonList(identifier));
when(insertResultType.getBriefRecord()).thenReturn(Collections.singletonList(briefRecord));
when(responseType.getInsertResult()).thenReturn(Collections.singletonList(insertResultType));
when(factory.getClientForSubject(any())).thenReturn(csw);
when(csw.transaction(any())).thenReturn(responseType);
when(transformer.getTransformerIdForSchema(any())).thenReturn("myInsertType");
queryResults.add(new ResultImpl(mcard));
CreateRequest request = new CreateRequestImpl(mcard);
CreateResponse response = registryStore.create(request);
assertThat(response.getCreatedMetacards()
.get(0), is(mcard));
}
@Test
public void testUpdate() throws Exception {
Csw csw = mock(Csw.class);
TransactionResponseType responseType = mock(TransactionResponseType.class);
TransactionSummaryType tst = new TransactionSummaryType();
tst.setTotalUpdated(new BigInteger("1"));
when(responseType.getTransactionSummary()).thenReturn(tst);
when(factory.getClientForSubject(any())).thenReturn(csw);
when(csw.transaction(any())).thenReturn(responseType);
when(transformer.getTransformerIdForSchema(any())).thenReturn(null);
UpdateRequestImpl request = new UpdateRequestImpl("testId", getDefaultMetacard());
MetacardImpl updatedMcard = getDefaultMetacard();
updatedMcard.setId("newTestId");
OperationTransactionImpl opTrans =
new OperationTransactionImpl(OperationTransaction.OperationType.UPDATE,
Collections.singletonList(updatedMcard));
request.getProperties()
.put(Constants.OPERATION_TRANSACTION_KEY, opTrans);
queryResults.add(new ResultImpl(getDefaultMetacard()));
registryStore.update(request);
assertThat(request.getUpdates()
.get(0)
.getValue()
.getMetadata()
.contains("value=\"newTestId\""), is(true));
}
@Test(expected = IngestException.class)
public void testUpdateNonRegistryMetacard() throws Exception {
MetacardImpl updatedMcard = getDefaultMetacard();
updatedMcard.setAttribute(RegistryObjectMetacardType.REGISTRY_ID, null);
UpdateRequestImpl request = new UpdateRequestImpl("testId", updatedMcard);
registryStore.update(request);
}
@Test
public void registryInfoQuery() throws Exception {
assertThat(registryStore.getRegistryId(), is(""));
queryResults.add(new ResultImpl(getDefaultMetacard()));
registryStore.registryInfoQuery();
ArgumentCaptor<QueryRequest> captor = ArgumentCaptor.forClass(QueryRequest.class);
verify(registryStore).query(captor.capture());
assertThat(captor.getValue()
.getPropertyValue(SecurityConstants.SECURITY_SUBJECT), notNullValue());
assertThat(registryStore.getRegistryId(), is("registryId"));
}
@Test
public void testUpdateConfigurationIOException() throws Exception {
assertThat(registryStore.getRegistryId(), is(""));
when(configAdmin.getConfiguration(any())).thenThrow(new IOException());
queryResults.add(new ResultImpl(getDefaultMetacard()));
registryStore.registryInfoQuery();
}
@Test
public void testUpdateConfigurationURISyntaxException() throws Exception {
assertThat(registryStore.getRegistryId(), is(""));
registryStore.setRegistryUrl("^invalid^^^^uri^^");
queryResults.add(new ResultImpl(getDefaultMetacard()));
registryStore.registryInfoQuery();
String registryId = (String) properties.get(RegistryObjectMetacardType.REGISTRY_ID);
assertThat(registryId, is(nullValue()));
}
@Test
public void testUpdateConfigurationUriNoPort() throws Exception {
assertThat(registryStore.getRegistryId(), is(""));
queryResults.add(new ResultImpl(getDefaultMetacard()));
registryStore.setRegistryUrl("http://testurl/example");
registryStore.registryInfoQuery();
assertThat(registryStore.getRegistryId(), is("registryId"));
}
@Test
public void registryInfoQueryNoIdentityMetacard() throws Exception {
assertThat(registryStore.getRegistryId(), is(""));
registryStore.registryInfoQuery();
assertThat(registryStore.getRegistryId(), is(""));
}
@Test
public void testNonRegistryQuery() throws Exception {
Filter filter = filterBuilder.attribute(RegistryObjectMetacardType.REGISTRY_ID)
.is()
.like()
.text("registryId");
QueryRequest testRequest = new QueryRequestImpl(new QueryImpl(filter));
queryResults.add(new ResultImpl(getDefaultMetacard()));
SourceResponse answer = registryStore.query(testRequest);
List<Result> testResults = answer.getResults();
assertThat(testResults.size(), is(0));
}
@Test
public void testRegistryQuery() throws Exception {
Filter filter = filterBuilder.attribute(Metacard.TAGS)
.is()
.like()
.text(RegistryConstants.REGISTRY_TAG);
queryResults.add(new ResultImpl(getDefaultMetacard()));
QueryRequest testRequest = new QueryRequestImpl(new QueryImpl(filter));
SourceResponse answer = registryStore.query(testRequest);
List<Result> testResults = answer.getResults();
assertThat(testResults.size(), is(1));
}
@Test
public void testRegistryQueryNullGetId() throws Exception {
Filter filter = filterBuilder.attribute(Metacard.TAGS)
.is()
.like()
.text(RegistryConstants.REGISTRY_TAG);
queryResults.add(new ResultImpl(getDefaultMetacard()));
QueryRequest testRequest = new QueryRequestImpl(new QueryImpl(filter));
registryStore.setRegistryId("registryId");
SourceResponse answer = registryStore.query(testRequest);
List<Result> testResults = answer.getResults();
assertThat(testResults.size(), is(1));
}
@Test
public void testRegistryGetIdNotNull() throws Exception {
Filter filter = filterBuilder.attribute(Metacard.TAGS)
.is()
.like()
.text(RegistryConstants.REGISTRY_TAG);
queryResults.add(new ResultImpl(getDefaultMetacard()));
QueryRequest testRequest = new QueryRequestImpl(new QueryImpl(filter));
registryStore.setRegistryId("registryId");
registryStore.setId("d");
SourceResponse answer = registryStore.query(testRequest);
List<Result> testResults = answer.getResults();
assertThat(testResults.size(), is(1));
}
@Test
public void testDelete() throws Exception {
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
Csw csw = mock(Csw.class);
TransactionResponseType transResponse = mock(TransactionResponseType.class);
TransactionSummaryType transSummary = mock(TransactionSummaryType.class);
when(transResponse.getTransactionSummary()).thenReturn(transSummary);
when(transSummary.getTotalDeleted()).thenReturn(new BigInteger("1"));
when(csw.transaction(any(CswTransactionRequest.class))).thenReturn(transResponse);
when(factory.getClientForSubject(any())).thenReturn(csw);
when(transformer.getTransformerIdForSchema(any())).thenReturn(null);
FilterAdapter mockAdaptor = mock(FilterAdapter.class);
CswFilterFactory filterFactory = new CswFilterFactory(CswAxisOrder.LAT_LON, false);
FilterType filterType = filterFactory.buildPropertyIsLikeFilter(Metacard.ID,
"testId",
false);
when(mockAdaptor.adapt(any(Filter.class),
any(FilterDelegate.class))).thenReturn(filterType);
registryStore.setFilterAdapter(mockAdaptor);
DeleteRequestImpl request = new DeleteRequestImpl(Collections.singletonList(
RegistryObjectMetacardType.REGISTRY_ID), "registryId", new HashMap<>());
OperationTransactionImpl opTrans =
new OperationTransactionImpl(OperationTransaction.OperationType.DELETE,
Collections.singletonList(getDefaultMetacard()));
request.getProperties()
.put(Constants.OPERATION_TRANSACTION_KEY, opTrans);
registryStore.delete(request);
verify(filterBuilder).attribute(captor.capture());
assertThat(captor.getValue(), is("id"));
}
@Test
public void testInit() throws Exception {
RegistryStoreImpl registryStore = spy(new RegistryStoreImpl(context,
cswSourceConfiguration,
provider,
factory,
encryptionService) {
@Override
protected void validateOperation() {
}
@Override
public boolean isAvailable(){
return availability;
}
@Override
protected SourceResponse query(QueryRequest queryRequest, ElementSetType elementSetName,
List<QName> elementNames, Csw csw) throws UnsupportedQueryException {
if (queryResults == null) {
throw new UnsupportedQueryException("Test - Bad Query");
}
return new SourceResponseImpl(queryRequest, queryResults);
}
@Override
public SourceResponse query(QueryRequest request) throws UnsupportedQueryException {
return new SourceResponseImpl(request, Collections.singletonList(new Result() {
@Override
public Metacard getMetacard() {
MetacardImpl metacard = new MetacardImpl();
metacard.setAttribute(RegistryObjectMetacardType.REGISTRY_ID, "registryId");
metacard.setAttribute(Metacard.TITLE, "title");
return metacard;
}
@Override
public Double getRelevanceScore() {
return null;
}
@Override
public Double getDistanceInMeters() {
return null;
}
}));
}
@Override
protected CapabilitiesType getCapabilities() {
return mock(CapabilitiesType.class);
}
@Override
public void configureCswSource() {};
@Override
protected Subject getSystemSubject(){
return subject;
}
@Override
BundleContext getBundleContext() {
return context;
}
});
registryStore.setFilterBuilder(filterBuilder);
registryStore.setFilterAdapter(filterAdapter);
registryStore.setConfigAdmin(configAdmin);
registryStore.setMetacardMarshaller(new MetacardMarshaller(parser));
registryStore.setSchemaTransformerManager(transformer);
registryStore.setAutoPush(true);
registryStore.setRegistryUrl("http://test.url:0101/example");
properties = new Hashtable<>();
properties.put(RegistryStoreImpl.ID, "registryId");
registryStore.setMetacardMarshaller(marshaller);
Csw csw = mock(Csw.class);
when(factory.getClientForSubject(any())).thenReturn(csw);
cswSourceConfiguration.setCswUrl("https://localhost");
cswSourceConfiguration.setPollIntervalMinutes(1);
queryResults.add(new ResultImpl(getDefaultMetacard()));
registryStore.init();
assertThat(registryStore.getRegistryId(), is("registryId"));
}
private MetacardImpl getDefaultMetacard() {
InputStream inputStream = getClass().getResourceAsStream("/csw-full-registry-package.xml");
BufferedReader buffer = new BufferedReader(new InputStreamReader(inputStream));
String xml = buffer.lines()
.collect(Collectors.joining("\n"));
MetacardImpl mcard = new MetacardImpl(new RegistryObjectMetacardType());
mcard.setId("testId");
mcard.setTags(Collections.singleton(RegistryConstants.REGISTRY_TAG));
mcard.setAttribute(RegistryObjectMetacardType.REGISTRY_ID, "registryId");
mcard.setContentTypeName(RegistryConstants.REGISTRY_NODE_METACARD_TYPE_NAME);
mcard.setMetadata(xml);
mcard.setTitle("testRegistryMetacard");
return mcard;
}
}