blob: 689073c9ca3c089039844e698abb51d35ff90b38 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.solr.handler.admin;
import com.google.common.collect.Maps;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.api.Api;
import org.apache.solr.api.ApiBag;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.cloud.api.collections.CategoryRoutedAlias;
import org.apache.solr.cloud.api.collections.RoutedAlias;
import org.apache.solr.common.cloud.DocCollection;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.params.CollectionAdminParams;
import org.apache.solr.common.params.CollectionParams;
import org.apache.solr.common.params.CommonAdminParams;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.common.params.ShardParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.CommandOperation;
import org.apache.solr.common.util.ContentStreamBase;
import org.apache.solr.core.backup.BackupManager;
import org.apache.solr.handler.CollectionsAPI;
import org.apache.solr.request.LocalSolrQueryRequest;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static org.apache.solr.common.params.CommonParams.ACTION;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Unit tests for the API mappings found in {@link org.apache.solr.handler.CollectionsAPI}.
*
* This test bears many similarities to {@link TestCollectionAPIs} which appears to test the mappings indirectly by
* checking message sent to the ZK overseer (which is similar, but not identical to the v1 param list). If there's no
* particular benefit to testing the mappings in this way (there very well may be), then we should combine these two
* test classes at some point in the future using the simpler approach here.
*
* Note that the V2 requests made by these tests are not necessarily semantically valid. They shouldn't be taken as
* examples. In several instances, mutually exclusive JSON parameters are provided. This is done to exercise conversion
* of all parameters, even if particular combinations are never expected in the same request.
*/
public class V2CollectionsAPIMappingTest extends SolrTestCaseJ4 {
private ApiBag apiBag;
private ArgumentCaptor<SolrQueryRequest> queryRequestCaptor;
private CollectionsHandler mockCollectionsHandler;
@BeforeClass
public static void ensureWorkingMockito() {
assumeWorkingMockito();
}
@Before
public void setupApiBag() throws Exception {
mockCollectionsHandler = mock(CollectionsHandler.class);
queryRequestCaptor = ArgumentCaptor.forClass(SolrQueryRequest.class);
apiBag = new ApiBag(false);
final CollectionsAPI collectionsAPI = new CollectionsAPI(mockCollectionsHandler);
apiBag.registerObject(collectionsAPI);
apiBag.registerObject(collectionsAPI.collectionsCommands);
}
@Test
public void testCreateCollectionAllProperties() throws Exception {
final SolrParams v1Params = captureConvertedV1Params("/collections", "POST",
"{'create': {" +
"'name': 'techproducts', " +
"'config':'_default', " +
"'router': {'name': 'composite', 'field': 'routeField', 'foo': 'bar'}, " +
"'shards': 'customShardName,anotherCustomShardName', " +
"'replicationFactor': 3," +
"'nrtReplicas': 1, " +
"'tlogReplicas': 1, " +
"'pullReplicas': 1, " +
"'nodeSet': ['localhost:8983_solr', 'localhost:7574_solr']," +
"'shuffleNodes': true," +
"'properties': {'foo': 'bar', 'foo2': 'bar2'}, " +
"'async': 'requestTrackingId', " +
"'waitForFinalState': false, " +
"'perReplicaState': false," +
"'numShards': 1}}");
assertEquals(CollectionParams.CollectionAction.CREATE.lowerName, v1Params.get(ACTION));
assertEquals("techproducts", v1Params.get(CommonParams.NAME));
assertEquals("_default", v1Params.get(CollectionAdminParams.COLL_CONF));
assertEquals("composite", v1Params.get("router.name"));
assertEquals("routeField", v1Params.get("router.field"));
assertEquals("bar", v1Params.get("router.foo"));
assertEquals("customShardName,anotherCustomShardName", v1Params.get(ShardParams.SHARDS));
assertEquals(3, v1Params.getPrimitiveInt(ZkStateReader.REPLICATION_FACTOR));
assertEquals(1, v1Params.getPrimitiveInt(ZkStateReader.NRT_REPLICAS));
assertEquals(1, v1Params.getPrimitiveInt(ZkStateReader.TLOG_REPLICAS));
assertEquals(1, v1Params.getPrimitiveInt(ZkStateReader.PULL_REPLICAS));
assertEquals("localhost:8983_solr,localhost:7574_solr", v1Params.get(CollectionAdminParams.CREATE_NODE_SET_PARAM));
assertEquals(true, v1Params.getPrimitiveBool(CollectionAdminParams.CREATE_NODE_SET_SHUFFLE_PARAM));
assertEquals("bar", v1Params.get("property.foo"));
assertEquals("bar2", v1Params.get("property.foo2"));
assertEquals("requestTrackingId", v1Params.get(CommonAdminParams.ASYNC));
assertEquals(false, v1Params.getPrimitiveBool(CommonAdminParams.WAIT_FOR_FINAL_STATE));
assertEquals(false, v1Params.getPrimitiveBool(DocCollection.PER_REPLICA_STATE));
assertEquals(1, v1Params.getPrimitiveInt(CollectionAdminParams.NUM_SHARDS));
}
@Test
public void testCreateAliasAllProperties() throws Exception {
final SolrParams v1Params = captureConvertedV1Params("/collections", "POST",
"{'create-alias': {" +
"'name': 'aliasName', " +
"'collections': ['techproducts1', 'techproducts2'], " +
"'tz': 'someTimeZone', " +
"'async': 'requestTrackingId', " +
"'router': {" +
" 'name': 'time', " +
" 'field': 'date_dt', " +
" 'interval': '+1HOUR', " +
" 'maxFutureMs': 3600, " +
" 'preemptiveCreateMath': 'somePreemptiveCreateMathString', " +
" 'autoDeleteAge': 'someAutoDeleteAgeExpression', " +
" 'maxCardinality': 36, " +
" 'mustMatch': 'someRegex', " +
"}, " +
"'create-collection': {" +
" 'numShards': 1, " +
" 'properties': {'foo': 'bar', 'foo2': 'bar2'}, " +
" 'replicationFactor': 3 " +
"}" +
"}}");
assertEquals(CollectionParams.CollectionAction.CREATEALIAS.lowerName, v1Params.get(ACTION));
assertEquals("aliasName", v1Params.get(CommonParams.NAME));
assertEquals("techproducts1,techproducts2", v1Params.get("collections"));
assertEquals("someTimeZone", v1Params.get(CommonParams.TZ.toLowerCase(Locale.ROOT)));
assertEquals("requestTrackingId", v1Params.get(CommonAdminParams.ASYNC));
assertEquals("time", v1Params.get(CollectionAdminRequest.CreateTimeRoutedAlias.ROUTER_TYPE_NAME));
assertEquals("date_dt", v1Params.get(CollectionAdminRequest.CreateTimeRoutedAlias.ROUTER_FIELD));
assertEquals("+1HOUR", v1Params.get(CollectionAdminRequest.CreateTimeRoutedAlias.ROUTER_INTERVAL));
assertEquals(3600, v1Params.getPrimitiveInt(CollectionAdminRequest.CreateTimeRoutedAlias.ROUTER_MAX_FUTURE));
assertEquals("somePreemptiveCreateMathString", v1Params.get(CollectionAdminRequest.CreateTimeRoutedAlias.ROUTER_PREEMPTIVE_CREATE_WINDOW));
assertEquals("someAutoDeleteAgeExpression", v1Params.get(CollectionAdminRequest.CreateTimeRoutedAlias.ROUTER_AUTO_DELETE_AGE));
assertEquals(36, v1Params.getPrimitiveInt(CategoryRoutedAlias.ROUTER_MAX_CARDINALITY));
assertEquals("someRegex", v1Params.get(CategoryRoutedAlias.ROUTER_MUST_MATCH));
assertEquals(1, v1Params.getPrimitiveInt(RoutedAlias.CREATE_COLLECTION_PREFIX + CollectionAdminParams.NUM_SHARDS));
assertEquals("bar", v1Params.get(RoutedAlias.CREATE_COLLECTION_PREFIX + "property.foo"));
assertEquals("bar2", v1Params.get(RoutedAlias.CREATE_COLLECTION_PREFIX + "property.foo2"));
assertEquals(3, v1Params.getPrimitiveInt(RoutedAlias.CREATE_COLLECTION_PREFIX + ZkStateReader.REPLICATION_FACTOR));
}
@Test
public void testDeleteAliasAllProperties() throws Exception {
final SolrParams v1Params = captureConvertedV1Params("/collections", "POST",
"{'delete-alias': {" +
"'name': 'aliasName', " +
"'async': 'requestTrackingId'" +
"}}");
assertEquals(CollectionParams.CollectionAction.DELETEALIAS.lowerName, v1Params.get(ACTION));
assertEquals("aliasName", v1Params.get(CommonParams.NAME));
assertEquals("requestTrackingId", v1Params.get(CommonAdminParams.ASYNC));
}
@Test
public void testSetAliasAllProperties() throws Exception {
final SolrParams v1Params = captureConvertedV1Params("/collections", "POST",
"{'set-alias-property': {" +
"'name': 'aliasName', " +
"'async': 'requestTrackingId', " +
"'properties': {'foo':'bar', 'foo2':'bar2'}" +
"}}");
assertEquals(CollectionParams.CollectionAction.ALIASPROP.lowerName, v1Params.get(ACTION));
assertEquals("aliasName", v1Params.get(CommonParams.NAME));
assertEquals("requestTrackingId", v1Params.get(CommonAdminParams.ASYNC));
assertEquals("bar", v1Params.get("property.foo"));
assertEquals("bar2", v1Params.get("property.foo2"));
}
@Test
public void testBackupAllProperties() throws Exception {
final SolrParams v1Params = captureConvertedV1Params("/collections", "POST",
"{'backup-collection': {" +
"'name': 'backupName', " +
"'collection': 'collectionName', " +
"'location': '/some/location/uri', " +
"'repository': 'someRepository', " +
"'followAliases': true, " +
"'indexBackup': 'copy-files', " +
"'commitName': 'someSnapshotName', " +
"'incremental': true, " +
"'async': 'requestTrackingId' " +
"}}");
assertEquals(CollectionParams.CollectionAction.BACKUP.lowerName, v1Params.get(ACTION));
assertEquals("backupName", v1Params.get(CommonParams.NAME));
assertEquals("collectionName", v1Params.get(BackupManager.COLLECTION_NAME_PROP));
assertEquals("/some/location/uri", v1Params.get(CoreAdminParams.BACKUP_LOCATION));
assertEquals("someRepository", v1Params.get(CoreAdminParams.BACKUP_REPOSITORY));
assertEquals(true, v1Params.getPrimitiveBool(CollectionAdminParams.FOLLOW_ALIASES));
assertEquals("copy-files", v1Params.get(CollectionAdminParams.INDEX_BACKUP_STRATEGY));
assertEquals("someSnapshotName", v1Params.get(CoreAdminParams.COMMIT_NAME));
assertEquals(true, v1Params.getPrimitiveBool(CoreAdminParams.BACKUP_INCREMENTAL));
assertEquals("requestTrackingId", v1Params.get(CommonAdminParams.ASYNC));
}
@Test
public void testRestoreAllProperties() throws Exception {
final SolrParams v1Params = captureConvertedV1Params("/collections", "POST",
"{'restore-collection': {" +
"'name': 'backupName', " +
"'collection': 'collectionName', " +
"'location': '/some/location/uri', " +
"'repository': 'someRepository', " +
"'backupId': 123, " +
"'async': 'requestTrackingId', " +
"'create-collection': {" +
" 'numShards': 1, " +
" 'properties': {'foo': 'bar', 'foo2': 'bar2'}, " +
" 'replicationFactor': 3 " +
"}" +
"}}");
assertEquals(CollectionParams.CollectionAction.RESTORE.lowerName, v1Params.get(ACTION));
assertEquals("backupName", v1Params.get(CommonParams.NAME));
assertEquals("collectionName", v1Params.get(BackupManager.COLLECTION_NAME_PROP));
assertEquals("/some/location/uri", v1Params.get(CoreAdminParams.BACKUP_LOCATION));
assertEquals("someRepository", v1Params.get(CoreAdminParams.BACKUP_REPOSITORY));
assertEquals(123, v1Params.getPrimitiveInt(CoreAdminParams.BACKUP_ID));
assertEquals("requestTrackingId", v1Params.get(CommonAdminParams.ASYNC));
// NOTE: Unlike other v2 APIs that have a nested object for collection-creation params, restore's v1 equivalent
// for these properties doesn't have a "create-collection." prefix.
assertEquals(1, v1Params.getPrimitiveInt(CollectionAdminParams.NUM_SHARDS));
assertEquals("bar", v1Params.get("property.foo"));
assertEquals("bar2", v1Params.get("property.foo2"));
assertEquals(3, v1Params.getPrimitiveInt(ZkStateReader.REPLICATION_FACTOR));
}
private SolrParams captureConvertedV1Params(String path, String method, String v2RequestBody) throws Exception {
final HashMap<String, String> parts = new HashMap<>();
final Api api = apiBag.lookup(path, method, parts);
final SolrQueryResponse rsp = new SolrQueryResponse();
final LocalSolrQueryRequest req = new LocalSolrQueryRequest(null, Maps.newHashMap()) {
@Override
public List<CommandOperation> getCommands(boolean validateInput) {
if (v2RequestBody == null) return Collections.emptyList();
return ApiBag.getCommandOperations(new ContentStreamBase.StringStream(v2RequestBody), api.getCommandSchema(), true);
}
@Override
public Map<String, String> getPathTemplateValues() {
return parts;
}
@Override
public String getHttpMethod() {
return method;
}
};
api.call(req, rsp);
verify(mockCollectionsHandler).handleRequestBody(queryRequestCaptor.capture(), any());
return queryRequestCaptor.getValue().getParams();
}
}