/*
* Copyright © 2014-2015 Cask Data, Inc.
*
* 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.
*/
package co.cask.cdap.data2.datafabric.dataset.service;
import co.cask.cdap.proto.DatasetModuleMeta;
import co.cask.cdap.proto.DatasetTypeMeta;
import co.cask.cdap.proto.Id;
import co.cask.common.http.HttpRequest;
import co.cask.common.http.HttpRequests;
import co.cask.common.http.HttpResponse;
import co.cask.common.http.ObjectResponse;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gson.reflect.TypeToken;
import org.apache.http.HttpStatus;
import org.apache.twill.filesystem.Location;
import org.junit.Assert;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
/**
* Unit-test for {@link DatasetTypeHandler}
*/
public class DatasetTypeHandlerTest extends DatasetServiceTestBase {
@Test
public void testBasics() throws Exception {
// nothing has been deployed, modules and types list is empty
List<DatasetModuleMeta> modules = getModules().getResponseObject();
Assert.assertEquals(0, modules.size());
List<DatasetTypeMeta> types = getTypes().getResponseObject();
Assert.assertEquals(0, types.size());
// deploy module
Assert.assertEquals(HttpStatus.SC_OK, deployModule("module1", TestModule1.class).getResponseCode());
// verify deployed module present in a list
modules = getModules().getResponseObject();
Assert.assertEquals(1, modules.size());
verify(modules.get(0),
"module1", TestModule1.class, ImmutableList.of("datasetType1"),
Collections.<String>emptyList(), Collections.<String>emptyList());
// verify deployed module info can be retrieved
verify(getModule("module1").getResponseObject(), "module1", TestModule1.class, ImmutableList.of("datasetType1"),
Collections.<String>emptyList(), Collections.<String>emptyList());
Assert.assertEquals(HttpStatus.SC_NOT_FOUND, getType("datasetType2").getResponseCode());
// verify type information can be retrieved
verify(getType("datasetType1").getResponseObject(), "datasetType1", ImmutableList.of("module1"));
Assert.assertEquals(HttpStatus.SC_NOT_FOUND, getType("datasetType2").getResponseCode());
types = getTypes().getResponseObject();
Assert.assertEquals(1, types.size());
verify(types.get(0), "datasetType1", ImmutableList.of("module1"));
// cannot deploy same module again
Assert.assertEquals(HttpStatus.SC_CONFLICT, deployModule("module1", TestModule1.class).getResponseCode());
// cannot deploy module with same types
Assert.assertEquals(HttpStatus.SC_CONFLICT, deployModule("not-module1", TestModule1.class).getResponseCode());
// deploy another module which depends on the first one
Assert.assertEquals(HttpStatus.SC_OK, deployModule("module2", TestModule2.class).getResponseCode());
// verify deployed module present in a list
modules = getModules().getResponseObject();
Assert.assertEquals(2, modules.size());
for (DatasetModuleMeta module : modules) {
if ("module1".equals(module.getName())) {
verify(module, "module1", TestModule1.class, ImmutableList.of("datasetType1"),
Collections.<String>emptyList(), ImmutableList.of("module2"));
} else if ("module2".equals(module.getName())) {
verify(module, "module2", TestModule2.class, ImmutableList.of("datasetType2"),
ImmutableList.of("module1"), Collections.<String>emptyList());
} else {
Assert.fail("unexpected module: " + module);
}
}
// verify deployed module info can be retrieved
verify(getModule("module2").getResponseObject(), "module2", TestModule2.class, ImmutableList.of("datasetType2"),
ImmutableList.of("module1"), Collections.<String>emptyList());
// verify type information can be retrieved
verify(getType("datasetType1").getResponseObject(), "datasetType1", ImmutableList.of("module1"));
verify(getType("datasetType2").getResponseObject(), "datasetType2", ImmutableList.of("module1", "module2"));
types = getTypes().getResponseObject();
Assert.assertEquals(2, types.size());
for (DatasetTypeMeta type : types) {
if ("datasetType1".equals(type.getName())) {
verify(type, "datasetType1", ImmutableList.of("module1"));
} else if ("datasetType2".equals(type.getName())) {
verify(type, "datasetType2", ImmutableList.of("module1", "module2"));
} else {
Assert.fail("unexpected type: " + type);
}
}
Assert.assertEquals(HttpStatus.SC_NOT_FOUND, deleteModule("non-existing-module").getResponseCode());
// cannot delete module1 since module2 depends on it, verify that nothing has been deleted
Assert.assertEquals(HttpStatus.SC_CONFLICT, deleteModule("module1").getResponseCode());
verify(getModule("module1").getResponseObject(), "module1", TestModule1.class, ImmutableList.of("datasetType1"),
Collections.<String>emptyList(), ImmutableList.of("module2"));
verify(getType("datasetType1").getResponseObject(), "datasetType1", ImmutableList.of("module1"));
Assert.assertEquals(2, getTypes().getResponseObject().size());
// delete module2, should be removed from usedBy list everywhere and all its types should no longer be available
Assert.assertEquals(HttpStatus.SC_OK, deleteModule("module2").getResponseCode());
Assert.assertEquals(HttpStatus.SC_NOT_FOUND, getType("datasetType2").getResponseCode());
verify(getModule("module1").getResponseObject(), "module1", TestModule1.class, ImmutableList.of("datasetType1"),
Collections.<String>emptyList(), Collections.<String>emptyList());
Assert.assertEquals(1, getModules().getResponseObject().size());
Assert.assertEquals(1, getTypes().getResponseObject().size());
Assert.assertEquals(HttpStatus.SC_NOT_FOUND, deleteModule("module2").getResponseCode());
Assert.assertEquals(HttpStatus.SC_OK, deleteModules().getResponseCode());
Assert.assertEquals(HttpStatus.SC_NOT_FOUND, getType("datasetType1").getResponseCode());
Assert.assertEquals(0, getModules().getResponseObject().size());
Assert.assertEquals(0, getTypes().getResponseObject().size());
}
@Test
public void testBundledJarModule() throws Exception {
//Get jar of TestModule1
Location module1Jar = createModuleJar(TestModule1.class);
// Create bundle jar with TestModule2 and TestModule1 inside it, request for deploy is made for Module1.
Assert.assertEquals(200, deployModuleBundled("module1", TestModule1.class.getName(),
TestModule2.class, module1Jar));
Assert.assertEquals(HttpStatus.SC_OK, deleteModules().getResponseCode());
List<DatasetModuleMeta> modules = getModules().getResponseObject();
Assert.assertEquals(0, modules.size());
}
@Test
public void testNotFound() throws Exception {
Id.Namespace nonExistent = Id.Namespace.from("nonExistent");
HttpResponse response = makeModulesRequest(nonExistent);
assertNamespaceNotFound(response, nonExistent);
response = makeTypesRequest(nonExistent);
assertNamespaceNotFound(response, nonExistent);
Id.DatasetModule datasetModule = Id.DatasetModule.from(nonExistent, "module");
response = makeModuleInfoRequest(datasetModule);
assertNamespaceNotFound(response, nonExistent);
Id.DatasetType datasetType = Id.DatasetType.from(nonExistent, "type");
response = makeTypeInfoRequest(datasetType);
assertNamespaceNotFound(response, nonExistent);
response = deployModule(datasetModule, TestModule1.class);
assertNamespaceNotFound(response, nonExistent);
response = deleteModule(datasetModule);
assertNamespaceNotFound(response, nonExistent);
response = deleteModules(nonExistent);
assertNamespaceNotFound(response, nonExistent);
}
private void verify(DatasetTypeMeta typeMeta, String typeName, List<String> modules) {
Assert.assertEquals(typeName, typeMeta.getName());
Assert.assertArrayEquals(modules.toArray(),
Lists.transform(typeMeta.getModules(),
new Function<DatasetModuleMeta, String>() {
@Nullable
@Override
public String apply(@Nullable DatasetModuleMeta input) {
return input == null ? null : input.getName();
}
}).toArray());
}
static void verify(DatasetModuleMeta moduleMeta, String moduleName, Class moduleClass,
List<String> types, List<String> usesModules, Collection<String> usedByModules) {
Assert.assertEquals(moduleName, moduleMeta.getName());
Assert.assertEquals(moduleClass.getName(), moduleMeta.getClassName());
Assert.assertArrayEquals(types.toArray(), moduleMeta.getTypes().toArray());
Assert.assertArrayEquals(usesModules.toArray(), moduleMeta.getUsesModules().toArray());
// using treeset just for sorting purposes
Assert.assertArrayEquals(Sets.newTreeSet(usedByModules).toArray(),
Sets.newTreeSet(moduleMeta.getUsedByModules()).toArray());
// note: we know it is local
Assert.assertNotNull(moduleMeta.getJarLocation());
Assert.assertTrue(new File(moduleMeta.getJarLocation()).exists());
}
private ObjectResponse<List<DatasetTypeMeta>> getTypes() throws IOException {
return getTypes(Id.Namespace.DEFAULT);
}
private ObjectResponse<List<DatasetTypeMeta>> getTypes(Id.Namespace namespaceId) throws IOException {
return ObjectResponse.fromJsonBody(makeTypesRequest(namespaceId),
new TypeToken<List<DatasetTypeMeta>>() {
}.getType());
}
private HttpResponse makeTypesRequest(Id.Namespace namespaceId) throws IOException {
HttpRequest request = HttpRequest.get(getUrl(namespaceId.getId(), "/data/types")).build();
return HttpRequests.execute(request);
}
private ObjectResponse<DatasetModuleMeta> getModule(String moduleName) throws IOException {
return getModule(Id.DatasetModule.from(Id.Namespace.DEFAULT, moduleName));
}
private ObjectResponse<DatasetModuleMeta> getModule(Id.DatasetModule module) throws IOException {
return ObjectResponse.fromJsonBody(makeModuleInfoRequest(module), DatasetModuleMeta.class);
}
private HttpResponse makeModuleInfoRequest(Id.DatasetModule module) throws IOException {
HttpRequest request = HttpRequest.get(getUrl(module.getNamespaceId(), "/data/modules/" + module.getId())).build();
return HttpRequests.execute(request);
}
private ObjectResponse<DatasetTypeMeta> getType(String typeName) throws IOException {
return getType(Id.DatasetType.from(Id.Namespace.DEFAULT, typeName));
}
private ObjectResponse<DatasetTypeMeta> getType(Id.DatasetType datasetType) throws IOException {
return ObjectResponse.fromJsonBody(makeTypeInfoRequest(datasetType), DatasetTypeMeta.class);
}
private HttpResponse makeTypeInfoRequest(Id.DatasetType datasetType) throws IOException {
HttpRequest request = HttpRequest.get(
getUrl(datasetType.getNamespaceId(), "/data/types/" + datasetType.getId())).build();
return HttpRequests.execute(request);
}
}