Commit 705014eb by Shwetha GS

ATLAS-716 Entity update/delete notifications (shwethags)

parent 153fc362
...@@ -188,7 +188,7 @@ public class HiveMetaStoreBridge { ...@@ -188,7 +188,7 @@ public class HiveMetaStoreBridge {
List<String> guids = getAtlasClient().createEntity(entityJSON); List<String> guids = getAtlasClient().createEntity(entityJSON);
LOG.debug("created instance for type " + typeName + ", guid: " + guids); LOG.debug("created instance for type " + typeName + ", guid: " + guids);
return new Referenceable(guids.get(0), referenceable.getTypeName(), null); return new Referenceable(guids.get(guids.size() - 1), referenceable.getTypeName(), null);
} }
/** /**
......
...@@ -29,7 +29,6 @@ import org.apache.atlas.hive.bridge.HiveMetaStoreBridge; ...@@ -29,7 +29,6 @@ import org.apache.atlas.hive.bridge.HiveMetaStoreBridge;
import org.apache.atlas.hive.model.HiveDataModelGenerator; import org.apache.atlas.hive.model.HiveDataModelGenerator;
import org.apache.atlas.hive.model.HiveDataTypes; import org.apache.atlas.hive.model.HiveDataTypes;
import org.apache.atlas.hive.rewrite.HiveASTRewriter; import org.apache.atlas.hive.rewrite.HiveASTRewriter;
import org.apache.atlas.hive.rewrite.RewriteException;
import org.apache.atlas.typesystem.Referenceable; import org.apache.atlas.typesystem.Referenceable;
import org.apache.atlas.typesystem.Struct; import org.apache.atlas.typesystem.Struct;
import org.apache.atlas.typesystem.persistence.Id; import org.apache.atlas.typesystem.persistence.Id;
...@@ -66,6 +65,7 @@ import java.util.Map; ...@@ -66,6 +65,7 @@ import java.util.Map;
import static org.apache.atlas.hive.hook.HiveHook.lower; import static org.apache.atlas.hive.hook.HiveHook.lower;
import static org.apache.atlas.hive.hook.HiveHook.normalize; import static org.apache.atlas.hive.hook.HiveHook.normalize;
import static org.apache.atlas.hive.model.HiveDataModelGenerator.NAME;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.fail; import static org.testng.Assert.fail;
...@@ -197,8 +197,8 @@ public class HiveHookIT { ...@@ -197,8 +197,8 @@ public class HiveHookIT {
Assert.assertEquals(tableRef.get(HiveDataModelGenerator.TABLE_TYPE_ATTR), TableType.MANAGED_TABLE.name()); Assert.assertEquals(tableRef.get(HiveDataModelGenerator.TABLE_TYPE_ATTR), TableType.MANAGED_TABLE.name());
Assert.assertEquals(tableRef.get(HiveDataModelGenerator.COMMENT), "table comment"); Assert.assertEquals(tableRef.get(HiveDataModelGenerator.COMMENT), "table comment");
String entityName = HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, DEFAULT_DB, tableName); String entityName = HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, DEFAULT_DB, tableName);
Assert.assertEquals(tableRef.get(HiveDataModelGenerator.NAME), entityName); Assert.assertEquals(tableRef.get(NAME), entityName);
Assert.assertEquals(tableRef.get(HiveDataModelGenerator.NAME), "default." + tableName.toLowerCase() + "@" + CLUSTER_NAME); Assert.assertEquals(tableRef.get(NAME), "default." + tableName.toLowerCase() + "@" + CLUSTER_NAME);
Table t = hiveMetaStoreBridge.hiveClient.getTable(DEFAULT_DB, tableName); Table t = hiveMetaStoreBridge.hiveClient.getTable(DEFAULT_DB, tableName);
long createTime = Long.parseLong(t.getMetadata().getProperty(hive_metastoreConstants.DDL_TIME)) * HiveMetaStoreBridge.MILLIS_CONVERT_FACTOR; long createTime = Long.parseLong(t.getMetadata().getProperty(hive_metastoreConstants.DDL_TIME)) * HiveMetaStoreBridge.MILLIS_CONVERT_FACTOR;
...@@ -631,7 +631,7 @@ public class HiveHookIT { ...@@ -631,7 +631,7 @@ public class HiveHookIT {
final String newDBName = createDatabase(); final String newDBName = createDatabase();
assertTableIsRegistered(DEFAULT_DB, tableName); assertTableIsRegistered(DEFAULT_DB, tableName);
String columnGuid = assertColumnIsRegistered(HiveMetaStoreBridge.getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, DEFAULT_DB, tableName), HiveDataModelGenerator.NAME)); String columnGuid = assertColumnIsRegistered(HiveMetaStoreBridge.getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, DEFAULT_DB, tableName), NAME));
String sdGuid = assertSDIsRegistered(HiveMetaStoreBridge.getStorageDescQFName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, DEFAULT_DB, tableName)), null); String sdGuid = assertSDIsRegistered(HiveMetaStoreBridge.getStorageDescQFName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, DEFAULT_DB, tableName)), null);
assertDatabaseIsRegistered(newDBName); assertDatabaseIsRegistered(newDBName);
...@@ -649,10 +649,10 @@ public class HiveHookIT { ...@@ -649,10 +649,10 @@ public class HiveHookIT {
String query = String.format("alter table %s rename to %s", DEFAULT_DB + "." + tableName, newDBName + "." + newTableName); String query = String.format("alter table %s rename to %s", DEFAULT_DB + "." + tableName, newDBName + "." + newTableName);
runCommand(query); runCommand(query);
String newColGuid = assertColumnIsRegistered(HiveMetaStoreBridge.getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, newDBName, newTableName), HiveDataModelGenerator.NAME)); String newColGuid = assertColumnIsRegistered(HiveMetaStoreBridge.getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, newDBName, newTableName), NAME));
Assert.assertEquals(newColGuid, columnGuid); Assert.assertEquals(newColGuid, columnGuid);
assertColumnIsNotRegistered(HiveMetaStoreBridge.getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, newDBName, tableName), HiveDataModelGenerator.NAME)); assertColumnIsNotRegistered(HiveMetaStoreBridge.getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, newDBName, tableName), NAME));
assertTrait(columnGuid, colTraitDetails); assertTrait(columnGuid, colTraitDetails);
String newSdGuid = assertSDIsRegistered(HiveMetaStoreBridge.getStorageDescQFName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, newDBName, newTableName)), null); String newSdGuid = assertSDIsRegistered(HiveMetaStoreBridge.getStorageDescQFName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, newDBName, newTableName)), null);
...@@ -676,7 +676,16 @@ public class HiveHookIT { ...@@ -676,7 +676,16 @@ public class HiveHookIT {
private List<Referenceable> getColumns(String dbName, String tableName) throws Exception { private List<Referenceable> getColumns(String dbName, String tableName) throws Exception {
String tableId = assertTableIsRegistered(dbName, tableName); String tableId = assertTableIsRegistered(dbName, tableName);
Referenceable tableRef = atlasClient.getEntity(tableId); Referenceable tableRef = atlasClient.getEntity(tableId);
return ((List<Referenceable>)tableRef.get(HiveDataModelGenerator.COLUMNS));
//with soft delete, the deleted columns are returned as well. So, filter the deleted ones
List<Referenceable> columns = ((List<Referenceable>) tableRef.get(HiveDataModelGenerator.COLUMNS));
List<Referenceable> activeColumns = new ArrayList<>();
for (Referenceable col : columns) {
if (col.getId().getState() == Id.EntityState.ACTIVE) {
activeColumns.add(col);
}
}
return activeColumns;
} }
...@@ -723,21 +732,15 @@ public class HiveHookIT { ...@@ -723,21 +732,15 @@ public class HiveHookIT {
colDropped)); colDropped));
//Verify the number of columns present in the table //Verify the number of columns present in the table
assertTableIsRegistered(DEFAULT_DB, tableName, new AssertPredicate() { List<Referenceable> columns = getColumns(DEFAULT_DB, tableName);
@Override assertEquals(columns.size(), 1);
public void assertOnEntity(Referenceable tableRef) throws Exception { assertEquals(columns.get(0).get(NAME), "name");
List<Referenceable> columns = (List<Referenceable>) tableRef.get(HiveDataModelGenerator.COLUMNS);
Assert.assertEquals(columns.size(), 1);
Assert.assertEquals(columns.get(0).get(HiveDataModelGenerator.NAME), HiveDataModelGenerator.NAME);
}
});
} }
@Test @Test
public void testAlterTableChangeColumn() throws Exception { public void testAlterTableChangeColumn() throws Exception {
//Change name //Change name
String oldColName = HiveDataModelGenerator.NAME; String oldColName = NAME;
String newColName = "name1"; String newColName = "name1";
String tableName = createTable(); String tableName = createTable();
String query = String.format("alter table %s change %s %s string", tableName, oldColName, newColName); String query = String.format("alter table %s change %s %s string", tableName, oldColName, newColName);
...@@ -818,8 +821,8 @@ public class HiveHookIT { ...@@ -818,8 +821,8 @@ public class HiveHookIT {
@Override @Override
public void assertOnEntity(Referenceable entity) throws Exception { public void assertOnEntity(Referenceable entity) throws Exception {
List<Referenceable> columns = (List<Referenceable>) entity.get(HiveDataModelGenerator.COLUMNS); List<Referenceable> columns = (List<Referenceable>) entity.get(HiveDataModelGenerator.COLUMNS);
assertEquals(columns.get(0).get(HiveDataModelGenerator.NAME), finalNewColName); assertEquals(columns.get(0).get(NAME), finalNewColName);
assertEquals(columns.get(1).get(HiveDataModelGenerator.NAME), "id"); assertEquals(columns.get(1).get(NAME), "id");
} }
} }
); );
...@@ -846,8 +849,8 @@ public class HiveHookIT { ...@@ -846,8 +849,8 @@ public class HiveHookIT {
@Override @Override
public void assertOnEntity(Referenceable entity) throws Exception { public void assertOnEntity(Referenceable entity) throws Exception {
List<Referenceable> columns = (List<Referenceable>) entity.get(HiveDataModelGenerator.COLUMNS); List<Referenceable> columns = (List<Referenceable>) entity.get(HiveDataModelGenerator.COLUMNS);
assertEquals(columns.get(1).get(HiveDataModelGenerator.NAME), finalNewColName2); assertEquals(columns.get(1).get(NAME), finalNewColName2);
assertEquals(columns.get(0).get(HiveDataModelGenerator.NAME), "id"); assertEquals(columns.get(0).get(NAME), "id");
} }
} }
); );
...@@ -955,7 +958,7 @@ public class HiveHookIT { ...@@ -955,7 +958,7 @@ public class HiveHookIT {
Referenceable hdfsPathRef = atlasClient.getEntity(hdfsPathId); Referenceable hdfsPathRef = atlasClient.getEntity(hdfsPathId);
Assert.assertEquals(hdfsPathRef.get("path"), testPathNormed); Assert.assertEquals(hdfsPathRef.get("path"), testPathNormed);
Assert.assertEquals(hdfsPathRef.get(HiveDataModelGenerator.NAME), testPathNormed); Assert.assertEquals(hdfsPathRef.get(NAME), testPathNormed);
// Assert.assertEquals(hdfsPathRef.get("name"), new Path(testPath).getName()); // Assert.assertEquals(hdfsPathRef.get("name"), new Path(testPath).getName());
Assert.assertEquals(hdfsPathRef.get(AtlasClient.REFERENCEABLE_ATTRIBUTE_NAME), testPathNormed); Assert.assertEquals(hdfsPathRef.get(AtlasClient.REFERENCEABLE_ATTRIBUTE_NAME), testPathNormed);
...@@ -964,7 +967,7 @@ public class HiveHookIT { ...@@ -964,7 +967,7 @@ public class HiveHookIT {
private String assertHDFSPathIsRegistered(String path) throws Exception { private String assertHDFSPathIsRegistered(String path) throws Exception {
LOG.debug("Searching for hdfs path {}", path); LOG.debug("Searching for hdfs path {}", path);
return assertEntityIsRegistered(FSDataTypes.HDFS_PATH().toString(), HiveDataModelGenerator.NAME, path, null); return assertEntityIsRegistered(FSDataTypes.HDFS_PATH().toString(), NAME, path, null);
} }
@Test @Test
...@@ -1014,7 +1017,7 @@ public class HiveHookIT { ...@@ -1014,7 +1017,7 @@ public class HiveHookIT {
ImmutableList<String> cols = ImmutableList.of("id"); ImmutableList<String> cols = ImmutableList.of("id");
runBucketSortQuery(tableName, 5, cols, cols); runBucketSortQuery(tableName, 5, cols, cols);
cols = ImmutableList.of("id", HiveDataModelGenerator.NAME); cols = ImmutableList.of("id", NAME);
runBucketSortQuery(tableName, 2, cols, cols); runBucketSortQuery(tableName, 2, cols, cols);
} }
...@@ -1077,7 +1080,7 @@ public class HiveHookIT { ...@@ -1077,7 +1080,7 @@ public class HiveHookIT {
assertTableIsRegistered(DEFAULT_DB, tableName); assertTableIsRegistered(DEFAULT_DB, tableName);
assertColumnIsRegistered(HiveMetaStoreBridge.getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, DEFAULT_DB, tableName), "id")); assertColumnIsRegistered(HiveMetaStoreBridge.getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, DEFAULT_DB, tableName), "id"));
assertColumnIsRegistered(HiveMetaStoreBridge.getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, DEFAULT_DB, tableName), HiveDataModelGenerator.NAME)); assertColumnIsRegistered(HiveMetaStoreBridge.getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, DEFAULT_DB, tableName), NAME));
final String query = String.format("drop table %s ", tableName); final String query = String.format("drop table %s ", tableName);
runCommand(query); runCommand(query);
...@@ -1086,7 +1089,7 @@ public class HiveHookIT { ...@@ -1086,7 +1089,7 @@ public class HiveHookIT {
"id")); "id"));
assertColumnIsNotRegistered(HiveMetaStoreBridge assertColumnIsNotRegistered(HiveMetaStoreBridge
.getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, DEFAULT_DB, tableName), .getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, DEFAULT_DB, tableName),
HiveDataModelGenerator.NAME)); NAME));
assertTableIsNotRegistered(DEFAULT_DB, tableName); assertTableIsNotRegistered(DEFAULT_DB, tableName);
} }
...@@ -1110,7 +1113,7 @@ public class HiveHookIT { ...@@ -1110,7 +1113,7 @@ public class HiveHookIT {
HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, dbName, tableNames[0]), "id")); HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, dbName, tableNames[0]), "id"));
assertColumnIsNotRegistered(HiveMetaStoreBridge assertColumnIsNotRegistered(HiveMetaStoreBridge
.getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, dbName, tableNames[0]), .getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, dbName, tableNames[0]),
HiveDataModelGenerator.NAME)); NAME));
for(int i = 0; i < numTables; i++) { for(int i = 0; i < numTables; i++) {
assertTableIsNotRegistered(dbName, tableNames[i]); assertTableIsNotRegistered(dbName, tableNames[i]);
...@@ -1175,7 +1178,7 @@ public class HiveHookIT { ...@@ -1175,7 +1178,7 @@ public class HiveHookIT {
assertTableIsRegistered(DEFAULT_DB, viewName); assertTableIsRegistered(DEFAULT_DB, viewName);
assertColumnIsRegistered(HiveMetaStoreBridge.getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, DEFAULT_DB, viewName), "id")); assertColumnIsRegistered(HiveMetaStoreBridge.getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, DEFAULT_DB, viewName), "id"));
assertColumnIsRegistered(HiveMetaStoreBridge.getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, DEFAULT_DB, viewName), HiveDataModelGenerator.NAME)); assertColumnIsRegistered(HiveMetaStoreBridge.getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, DEFAULT_DB, viewName), NAME));
query = String.format("drop view %s ", viewName); query = String.format("drop view %s ", viewName);
...@@ -1185,7 +1188,7 @@ public class HiveHookIT { ...@@ -1185,7 +1188,7 @@ public class HiveHookIT {
"id")); "id"));
assertColumnIsNotRegistered(HiveMetaStoreBridge assertColumnIsNotRegistered(HiveMetaStoreBridge
.getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, DEFAULT_DB, viewName), .getColumnQualifiedName(HiveMetaStoreBridge.getTableQualifiedName(CLUSTER_NAME, DEFAULT_DB, viewName),
HiveDataModelGenerator.NAME)); NAME));
assertTableIsNotRegistered(DEFAULT_DB, viewName); assertTableIsNotRegistered(DEFAULT_DB, viewName);
} }
...@@ -1349,7 +1352,7 @@ public class HiveHookIT { ...@@ -1349,7 +1352,7 @@ public class HiveHookIT {
if (inputTblName != null) { if (inputTblName != null) {
Referenceable inputTableRef = new Referenceable(HiveDataTypes.HIVE_TABLE.name(), new HashMap<String, Object>() {{ Referenceable inputTableRef = new Referenceable(HiveDataTypes.HIVE_TABLE.name(), new HashMap<String, Object>() {{
put(HiveDataModelGenerator.NAME, inputTblName); put(NAME, inputTblName);
}}); }});
inputs = new ArrayList<Referenceable>(); inputs = new ArrayList<Referenceable>();
inputs.add(inputTableRef); inputs.add(inputTableRef);
...@@ -1357,7 +1360,7 @@ public class HiveHookIT { ...@@ -1357,7 +1360,7 @@ public class HiveHookIT {
List<Referenceable> outputs = null; List<Referenceable> outputs = null;
if (outputTblName != null) { if (outputTblName != null) {
Referenceable outputTableRef = new Referenceable(HiveDataTypes.HIVE_TABLE.name(), new HashMap<String, Object>() {{ Referenceable outputTableRef = new Referenceable(HiveDataTypes.HIVE_TABLE.name(), new HashMap<String, Object>() {{
put(HiveDataModelGenerator.NAME, outputTblName); put(NAME, outputTblName);
}}); }});
outputs = new ArrayList<Referenceable>(); outputs = new ArrayList<Referenceable>();
......
...@@ -20,11 +20,14 @@ package org.apache.atlas; ...@@ -20,11 +20,14 @@ package org.apache.atlas;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientHandlerException; import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource; import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.DefaultClientConfig; import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
import com.sun.jersey.client.urlconnection.URLConnectionClientHandler; import com.sun.jersey.client.urlconnection.URLConnectionClientHandler;
import org.apache.atlas.security.SecureClientUtils; import org.apache.atlas.security.SecureClientUtils;
import org.apache.atlas.typesystem.Referenceable; import org.apache.atlas.typesystem.Referenceable;
...@@ -45,6 +48,7 @@ import org.codehaus.jettison.json.JSONException; ...@@ -45,6 +48,7 @@ import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject; import org.codehaus.jettison.json.JSONObject;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.ws.rs.HttpMethod; import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
...@@ -54,8 +58,10 @@ import java.net.ConnectException; ...@@ -54,8 +58,10 @@ import java.net.ConnectException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.List; import java.util.List;
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter; import java.util.Map;
import static org.apache.atlas.security.SecurityProperties.TLS_ENABLED; import static org.apache.atlas.security.SecurityProperties.TLS_ENABLED;
/** /**
...@@ -65,9 +71,10 @@ public class AtlasClient { ...@@ -65,9 +71,10 @@ public class AtlasClient {
private static final Logger LOG = LoggerFactory.getLogger(AtlasClient.class); private static final Logger LOG = LoggerFactory.getLogger(AtlasClient.class);
public static final String NAME = "name"; public static final String NAME = "name";
public static final String GUID = "GUID";
public static final String TYPE = "type"; public static final String TYPE = "type";
public static final String TYPENAME = "typeName"; public static final String TYPENAME = "typeName";
public static final String GUID = "GUID";
public static final String ENTITIES = "entities";
public static final String DEFINITION = "definition"; public static final String DEFINITION = "definition";
public static final String ERROR = "error"; public static final String ERROR = "error";
...@@ -340,6 +347,61 @@ public class AtlasClient { ...@@ -340,6 +347,61 @@ public class AtlasClient {
return service; return service;
} }
public static class EntityResult {
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
public static final String OP_CREATED = "created";
public static final String OP_UPDATED = "updated";
public static final String OP_DELETED = "deleted";
Map<String, List<String>> entities = new HashMap<>();
public EntityResult() {
//For gson
}
public EntityResult(List<String> created, List<String> updated, List<String> deleted) {
add(OP_CREATED, created);
add(OP_UPDATED, updated);
add(OP_DELETED, deleted);
}
private void add(String type, List<String> list) {
if (list != null && list.size() > 0) {
entities.put(type, list);
}
}
private List<String> get(String type) {
List<String> list = entities.get(type);
if (list == null) {
list = new ArrayList<>();
}
return list;
}
public List<String> getCreatedEntities() {
return get(OP_CREATED);
}
public List<String> getUpdateEntities() {
return get(OP_UPDATED);
}
public List<String> getDeletedEntities() {
return get(OP_DELETED);
}
@Override
public String toString() {
return gson.toJson(this);
}
public static EntityResult fromString(String json) throws AtlasServiceException {
return gson.fromJson(json, EntityResult.class);
}
}
/** /**
* Return status of the service instance the client is pointing to. * Return status of the service instance the client is pointing to.
* *
...@@ -562,11 +624,15 @@ public class AtlasClient { ...@@ -562,11 +624,15 @@ public class AtlasClient {
protected List<String> createEntity(JSONArray entities) throws AtlasServiceException { protected List<String> createEntity(JSONArray entities) throws AtlasServiceException {
LOG.debug("Creating entities: {}", entities); LOG.debug("Creating entities: {}", entities);
JSONObject response = callAPI(API.CREATE_ENTITY, entities.toString()); JSONObject response = callAPI(API.CREATE_ENTITY, entities.toString());
List<String> results = extractResults(response, GUID, new ExtractOperation<String, String>()); List<String> results = extractEntityResult(response).getCreatedEntities();
LOG.debug("Create entities returned results: {}", results); LOG.debug("Create entities returned results: {}", results);
return results; return results;
} }
protected EntityResult extractEntityResult(JSONObject response) throws AtlasServiceException {
return EntityResult.fromString(response.toString());
}
/** /**
* Create the given entity * Create the given entity
* @param entitiesAsJson entity(type instance) as json * @param entitiesAsJson entity(type instance) as json
...@@ -601,19 +667,19 @@ public class AtlasClient { ...@@ -601,19 +667,19 @@ public class AtlasClient {
* @return json array of guids which were updated/created * @return json array of guids which were updated/created
* @throws AtlasServiceException * @throws AtlasServiceException
*/ */
public List<String> updateEntities(Referenceable... entities) throws AtlasServiceException { public EntityResult updateEntities(Referenceable... entities) throws AtlasServiceException {
return updateEntities(Arrays.asList(entities)); return updateEntities(Arrays.asList(entities));
} }
protected List<String> updateEntities(JSONArray entities) throws AtlasServiceException { protected EntityResult updateEntities(JSONArray entities) throws AtlasServiceException {
LOG.debug("Updating entities: {}", entities); LOG.debug("Updating entities: {}", entities);
JSONObject response = callAPI(API.UPDATE_ENTITY, entities.toString()); JSONObject response = callAPI(API.UPDATE_ENTITY, entities.toString());
List<String> results = extractResults(response, GUID, new ExtractOperation<String, String>()); EntityResult results = extractEntityResult(response);
LOG.debug("Update entities returned results: {}", results); LOG.debug("Update entities returned results: {}", results);
return results; return results;
} }
public List<String> updateEntities(Collection<Referenceable> entities) throws AtlasServiceException { public EntityResult updateEntities(Collection<Referenceable> entities) throws AtlasServiceException {
JSONArray entitiesArray = getEntitiesArray(entities); JSONArray entitiesArray = getEntitiesArray(entities);
return updateEntities(entitiesArray); return updateEntities(entitiesArray);
} }
...@@ -625,9 +691,10 @@ public class AtlasClient { ...@@ -625,9 +691,10 @@ public class AtlasClient {
* @param attribute property key * @param attribute property key
* @param value property value * @param value property value
*/ */
public void updateEntityAttribute(final String guid, final String attribute, String value) throws AtlasServiceException { public EntityResult updateEntityAttribute(final String guid, final String attribute, String value)
throws AtlasServiceException {
LOG.debug("Updating entity id: {}, attribute name: {}, attribute value: {}", guid, attribute, value); LOG.debug("Updating entity id: {}, attribute name: {}, attribute value: {}", guid, attribute, value);
callAPIWithRetries(API.UPDATE_ENTITY_PARTIAL, value, new ResourceCreator() { JSONObject response = callAPIWithRetries(API.UPDATE_ENTITY_PARTIAL, value, new ResourceCreator() {
@Override @Override
public WebResource createResource() { public WebResource createResource() {
API api = API.UPDATE_ENTITY_PARTIAL; API api = API.UPDATE_ENTITY_PARTIAL;
...@@ -636,6 +703,7 @@ public class AtlasClient { ...@@ -636,6 +703,7 @@ public class AtlasClient {
return resource; return resource;
} }
}); });
return extractEntityResult(response);
} }
@VisibleForTesting @VisibleForTesting
...@@ -665,10 +733,11 @@ public class AtlasClient { ...@@ -665,10 +733,11 @@ public class AtlasClient {
* @param guid guid * @param guid guid
* @param entity entity definition * @param entity entity definition
*/ */
public void updateEntity(String guid, Referenceable entity) throws AtlasServiceException { public EntityResult updateEntity(String guid, Referenceable entity) throws AtlasServiceException {
String entityJson = InstanceSerialization.toJson(entity, true); String entityJson = InstanceSerialization.toJson(entity, true);
LOG.debug("Updating entity id {} with {}", guid, entityJson); LOG.debug("Updating entity id {} with {}", guid, entityJson);
callAPI(API.UPDATE_ENTITY_PARTIAL, entityJson, guid); JSONObject response = callAPI(API.UPDATE_ENTITY_PARTIAL, entityJson, guid);
return extractEntityResult(response);
} }
/** /**
...@@ -691,8 +760,9 @@ public class AtlasClient { ...@@ -691,8 +760,9 @@ public class AtlasClient {
* @param uniqueAttributeValue Attribute Value that uniquely identifies the entity * @param uniqueAttributeValue Attribute Value that uniquely identifies the entity
* @param entity entity definition * @param entity entity definition
*/ */
public String updateEntity(final String entityType, final String uniqueAttributeName, final String uniqueAttributeValue, public EntityResult updateEntity(final String entityType, final String uniqueAttributeName,
Referenceable entity) throws AtlasServiceException { final String uniqueAttributeValue,
Referenceable entity) throws AtlasServiceException {
final API api = API.UPDATE_ENTITY_PARTIAL; final API api = API.UPDATE_ENTITY_PARTIAL;
String entityJson = InstanceSerialization.toJson(entity, true); String entityJson = InstanceSerialization.toJson(entity, true);
LOG.debug("Updating entity type: {}, attributeName: {}, attributeValue: {}, entity: {}", entityType, LOG.debug("Updating entity type: {}, attributeName: {}, attributeValue: {}, entity: {}", entityType,
...@@ -707,7 +777,7 @@ public class AtlasClient { ...@@ -707,7 +777,7 @@ public class AtlasClient {
return resource; return resource;
} }
}); });
String result = getString(response, GUID); EntityResult result = extractEntityResult(response);
LOG.debug("Update entity returned result: {}", result); LOG.debug("Update entity returned result: {}", result);
return result; return result;
} }
...@@ -724,10 +794,10 @@ public class AtlasClient { ...@@ -724,10 +794,10 @@ public class AtlasClient {
* Delete the specified entities from the repository * Delete the specified entities from the repository
* *
* @param guids guids of entities to delete * @param guids guids of entities to delete
* @return List of deleted entity guids * @return List of entity ids updated/deleted
* @throws AtlasServiceException * @throws AtlasServiceException
*/ */
public List<String> deleteEntities(final String ... guids) throws AtlasServiceException { public EntityResult deleteEntities(final String ... guids) throws AtlasServiceException {
LOG.debug("Deleting entities: {}", guids); LOG.debug("Deleting entities: {}", guids);
JSONObject jsonResponse = callAPIWithRetries(API.DELETE_ENTITIES, null, new ResourceCreator() { JSONObject jsonResponse = callAPIWithRetries(API.DELETE_ENTITIES, null, new ResourceCreator() {
@Override @Override
...@@ -740,7 +810,7 @@ public class AtlasClient { ...@@ -740,7 +810,7 @@ public class AtlasClient {
return resource; return resource;
} }
}); });
List<String> results = extractResults(jsonResponse, GUID, new ExtractOperation<String, String>()); EntityResult results = extractEntityResult(jsonResponse);
LOG.debug("Delete entities returned results: {}", results); LOG.debug("Delete entities returned results: {}", results);
return results; return results;
} }
...@@ -750,9 +820,9 @@ public class AtlasClient { ...@@ -750,9 +820,9 @@ public class AtlasClient {
* @param entityType Type of the entity being deleted * @param entityType Type of the entity being deleted
* @param uniqueAttributeName Attribute Name that uniquely identifies the entity * @param uniqueAttributeName Attribute Name that uniquely identifies the entity
* @param uniqueAttributeValue Attribute Value that uniquely identifies the entity * @param uniqueAttributeValue Attribute Value that uniquely identifies the entity
* @return List of deleted entity guids(including composite references from that entity) * @return List of entity ids updated/deleted(including composite references from that entity)
*/ */
public List<String> deleteEntity(String entityType, String uniqueAttributeName, String uniqueAttributeValue) public EntityResult deleteEntity(String entityType, String uniqueAttributeName, String uniqueAttributeValue)
throws AtlasServiceException { throws AtlasServiceException {
LOG.debug("Deleting entity type: {}, attributeName: {}, attributeValue: {}", entityType, uniqueAttributeName, LOG.debug("Deleting entity type: {}, attributeName: {}, attributeValue: {}", entityType, uniqueAttributeName,
uniqueAttributeValue); uniqueAttributeValue);
...@@ -762,7 +832,7 @@ public class AtlasClient { ...@@ -762,7 +832,7 @@ public class AtlasClient {
resource = resource.queryParam(ATTRIBUTE_NAME, uniqueAttributeName); resource = resource.queryParam(ATTRIBUTE_NAME, uniqueAttributeName);
resource = resource.queryParam(ATTRIBUTE_VALUE, uniqueAttributeValue); resource = resource.queryParam(ATTRIBUTE_VALUE, uniqueAttributeValue);
JSONObject jsonResponse = callAPIWithResource(API.DELETE_ENTITIES, resource, null); JSONObject jsonResponse = callAPIWithResource(API.DELETE_ENTITIES, resource, null);
List<String> results = extractResults(jsonResponse, GUID, new ExtractOperation<String, String>()); EntityResult results = extractEntityResult(jsonResponse);
LOG.debug("Delete entities returned results: {}", results); LOG.debug("Delete entities returned results: {}", results);
return results; return results;
} }
...@@ -901,7 +971,7 @@ public class AtlasClient { ...@@ -901,7 +971,7 @@ public class AtlasClient {
return extractResults(jsonResponse, AtlasClient.EVENTS, new ExtractOperation<EntityAuditEvent, JSONObject>() { return extractResults(jsonResponse, AtlasClient.EVENTS, new ExtractOperation<EntityAuditEvent, JSONObject>() {
@Override @Override
EntityAuditEvent extractElement(JSONObject element) throws JSONException { EntityAuditEvent extractElement(JSONObject element) throws JSONException {
return EntityAuditEvent.GSON.fromJson(element.toString(), EntityAuditEvent.class); return SerDe.GSON.fromJson(element.toString(), EntityAuditEvent.class);
} }
}); });
......
...@@ -18,16 +18,14 @@ ...@@ -18,16 +18,14 @@
package org.apache.atlas; package org.apache.atlas;
import com.google.gson.Gson; import org.apache.atlas.typesystem.IReferenceableInstance;
import com.google.gson.GsonBuilder; import org.apache.atlas.typesystem.json.InstanceSerialization;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
/** /**
* Structure of entity audit event * Structure of entity audit event
*/ */
public class EntityAuditEvent { public class EntityAuditEvent {
public static final Gson GSON = new GsonBuilder().create();
public enum EntityAuditAction { public enum EntityAuditAction {
ENTITY_CREATE, ENTITY_UPDATE, ENTITY_DELETE, TAG_ADD, TAG_DELETE ENTITY_CREATE, ENTITY_UPDATE, ENTITY_DELETE, TAG_ADD, TAG_DELETE
} }
...@@ -38,16 +36,19 @@ public class EntityAuditEvent { ...@@ -38,16 +36,19 @@ public class EntityAuditEvent {
private EntityAuditAction action; private EntityAuditAction action;
private String details; private String details;
private String eventKey; private String eventKey;
private IReferenceableInstance entityDefinition;
public EntityAuditEvent() { public EntityAuditEvent() {
} }
public EntityAuditEvent(String entityId, Long ts, String user, EntityAuditAction action, String details) { public EntityAuditEvent(String entityId, Long ts, String user, EntityAuditAction action, String details,
IReferenceableInstance entityDefinition) throws AtlasException {
this.entityId = entityId; this.entityId = entityId;
this.timestamp = ts; this.timestamp = ts;
this.user = user; this.user = user;
this.action = action; this.action = action;
this.details = details; this.details = details;
this.entityDefinition = entityDefinition;
} }
@Override @Override
...@@ -62,10 +63,12 @@ public class EntityAuditEvent { ...@@ -62,10 +63,12 @@ public class EntityAuditEvent {
EntityAuditEvent otherEvent = (EntityAuditEvent) other; EntityAuditEvent otherEvent = (EntityAuditEvent) other;
return StringUtils.equals(entityId, otherEvent.entityId) && return StringUtils.equals(entityId, otherEvent.entityId) &&
(timestamp == otherEvent.timestamp) && (timestamp == otherEvent.timestamp) &&
StringUtils.equals(user, otherEvent.user) && (action == otherEvent.action) && StringUtils.equals(user, otherEvent.user) &&
StringUtils.equals(details, otherEvent.details) && (action == otherEvent.action) &&
StringUtils.equals(eventKey, otherEvent.eventKey); StringUtils.equals(details, otherEvent.details) &&
StringUtils.equals(eventKey, otherEvent.eventKey) &&
StringUtils.equals(getEntityDefinitionString(), otherEvent.getEntityDefinitionString());
} }
@Override @Override
...@@ -75,11 +78,11 @@ public class EntityAuditEvent { ...@@ -75,11 +78,11 @@ public class EntityAuditEvent {
@Override @Override
public String toString() { public String toString() {
return GSON.toJson(this); return SerDe.GSON.toJson(this);
} }
public static EntityAuditEvent fromString(String eventString) { public static EntityAuditEvent fromString(String eventString) {
return GSON.fromJson(eventString, EntityAuditEvent.class); return SerDe.GSON.fromJson(eventString, EntityAuditEvent.class);
} }
public String getEntityId() { public String getEntityId() {
...@@ -129,4 +132,19 @@ public class EntityAuditEvent { ...@@ -129,4 +132,19 @@ public class EntityAuditEvent {
public void setEventKey(String eventKey) { public void setEventKey(String eventKey) {
this.eventKey = eventKey; this.eventKey = eventKey;
} }
public IReferenceableInstance getEntityDefinition() {
return entityDefinition;
}
public String getEntityDefinitionString() {
if (entityDefinition != null) {
return InstanceSerialization.toJson(entityDefinition, true);
}
return null;
}
public void setEntityDefinition(String entityDefinition) {
this.entityDefinition = InstanceSerialization.fromJsonReferenceable(entityDefinition, true);
}
} }
/**
* 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
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* 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.atlas;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import org.apache.atlas.typesystem.IReferenceableInstance;
import org.apache.atlas.typesystem.IStruct;
import org.apache.atlas.typesystem.Referenceable;
import org.apache.atlas.typesystem.Struct;
import org.apache.atlas.typesystem.json.InstanceSerialization;
import java.lang.reflect.Type;
public class SerDe {
public static final Gson GSON = new GsonBuilder().
registerTypeAdapter(IStruct.class, new StructDeserializer()).
registerTypeAdapter(IReferenceableInstance.class, new ReferenceableSerializerDeserializer()).
registerTypeAdapter(Referenceable.class, new ReferenceableSerializerDeserializer()).
create();
/**
* Serde for Struct used by AbstractNotificationConsumer.GSON.
*/
public static final class StructDeserializer implements JsonDeserializer<IStruct>, JsonSerializer<IStruct> {
@Override
public IStruct deserialize(final JsonElement json, final Type type,
final JsonDeserializationContext context) {
return context.deserialize(json, Struct.class);
}
@Override
public JsonElement serialize(IStruct src, Type typeOfSrc, JsonSerializationContext context) {
String instanceJson = InstanceSerialization.toJson(src, true);
return new JsonParser().parse(instanceJson).getAsJsonObject();
}
}
/**
* Serde for Referenceable used by AbstractNotificationConsumer.GSON.
*/
public static final class ReferenceableSerializerDeserializer implements JsonDeserializer<IStruct>,
JsonSerializer<IReferenceableInstance> {
@Override
public IReferenceableInstance deserialize(final JsonElement json, final Type type,
final JsonDeserializationContext context) {
return InstanceSerialization.fromJsonReferenceable(json.toString(), true);
}
@Override
public JsonElement serialize(IReferenceableInstance src, Type typeOfSrc, JsonSerializationContext context) {
String instanceJson = InstanceSerialization.toJson(src, true);
return new JsonParser().parse(instanceJson).getAsJsonObject();
}
}
}
...@@ -21,8 +21,12 @@ import com.sun.jersey.api.client.Client; ...@@ -21,8 +21,12 @@ import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientHandlerException; import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource; import com.sun.jersey.api.client.WebResource;
import org.apache.atlas.typesystem.Referenceable;
import org.apache.atlas.typesystem.json.InstanceSerialization;
import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.Configuration;
import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.UserGroupInformation;
import org.codehaus.jettison.json.JSONObject;
import org.mockito.Matchers;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeMethod;
...@@ -33,9 +37,12 @@ import javax.ws.rs.core.UriBuilder; ...@@ -33,9 +37,12 @@ import javax.ws.rs.core.UriBuilder;
import java.net.ConnectException; import java.net.ConnectException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
...@@ -76,6 +83,25 @@ public class AtlasClientTest { ...@@ -76,6 +83,25 @@ public class AtlasClientTest {
assertTrue(atlasClient.isServerReady()); assertTrue(atlasClient.isServerReady());
} }
@Test
public void testCreateEntity() throws Exception {
setupRetryParams();
AtlasClient atlasClient = new AtlasClient(service, configuration);
WebResource.Builder builder = setupBuilder(AtlasClient.API.CREATE_ENTITY, service);
ClientResponse response = mock(ClientResponse.class);
when(response.getStatus()).thenReturn(Response.Status.CREATED.getStatusCode());
JSONObject jsonResponse = new JSONObject(new AtlasClient.EntityResult(Arrays.asList("id"), null, null).toString());
when(response.getEntity(String.class)).thenReturn(jsonResponse.toString());
String entityJson = InstanceSerialization.toJson(new Referenceable("type"), true);
when(builder.method(anyString(), Matchers.<Class>any(), anyString())).thenReturn(response);
List<String> ids = atlasClient.createEntity(entityJson);
assertEquals(ids.size(), 1);
assertEquals(ids.get(0), "id");
}
private WebResource.Builder setupBuilder(AtlasClient.API api, WebResource webResource) { private WebResource.Builder setupBuilder(AtlasClient.API api, WebResource webResource) {
when(webResource.path(api.getPath())).thenReturn(service); when(webResource.path(api.getPath())).thenReturn(service);
WebResource.Builder builder = getBuilder(service); WebResource.Builder builder = getBuilder(service);
......
...@@ -97,6 +97,11 @@ public class MessageVersion implements Comparable<MessageVersion> { ...@@ -97,6 +97,11 @@ public class MessageVersion implements Comparable<MessageVersion> {
} }
@Override
public String toString() {
return "MessageVersion[version=" + version + "]";
}
// ----- helper methods -------------------------------------------------- // ----- helper methods --------------------------------------------------
/** /**
......
...@@ -17,11 +17,11 @@ ...@@ -17,11 +17,11 @@
*/ */
package org.apache.atlas.notification; package org.apache.atlas.notification;
import com.google.gson.reflect.TypeToken;
import org.apache.atlas.notification.entity.EntityMessageDeserializer; import org.apache.atlas.notification.entity.EntityMessageDeserializer;
import org.apache.atlas.notification.entity.EntityNotification; import org.apache.atlas.notification.entity.EntityNotification;
import org.apache.atlas.notification.hook.HookMessageDeserializer; import org.apache.atlas.notification.hook.HookMessageDeserializer;
import org.apache.atlas.notification.hook.HookNotification; import org.apache.atlas.notification.hook.HookNotification;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.List; import java.util.List;
......
...@@ -31,7 +31,7 @@ import java.lang.reflect.Type; ...@@ -31,7 +31,7 @@ import java.lang.reflect.Type;
public abstract class VersionedMessageDeserializer<T> implements MessageDeserializer<T> { public abstract class VersionedMessageDeserializer<T> implements MessageDeserializer<T> {
public static final String VERSION_MISMATCH_MSG = public static final String VERSION_MISMATCH_MSG =
"Notification message version mismatch. Expected %s but recieved %s"; "Notification message version mismatch. Expected %s but recieved %s. Message %s";
private final Type versionedMessageType; private final Type versionedMessageType;
private final MessageVersion expectedVersion; private final MessageVersion expectedVersion;
...@@ -90,18 +90,16 @@ public abstract class VersionedMessageDeserializer<T> implements MessageDeserial ...@@ -90,18 +90,16 @@ public abstract class VersionedMessageDeserializer<T> implements MessageDeserial
// message has newer version // message has newer version
if (comp > 0) { if (comp > 0) {
String msg = String.format(VERSION_MISMATCH_MSG, expectedVersion, versionedMessage.getVersion()); String msg =
String.format(VERSION_MISMATCH_MSG, expectedVersion, versionedMessage.getVersion(), messageJson);
notificationLogger.error(msg); notificationLogger.error(msg);
notificationLogger.info(messageJson);
throw new IncompatibleVersionException(msg); throw new IncompatibleVersionException(msg);
} }
// message has older version // message has older version
if (comp < 0) { if (comp < 0) {
notificationLogger.info( notificationLogger.info(String.format(VERSION_MISMATCH_MSG, expectedVersion, versionedMessage.getVersion(),
String.format(VERSION_MISMATCH_MSG, expectedVersion, versionedMessage.getVersion())); messageJson));
notificationLogger.info(messageJson);
} }
} }
} }
...@@ -27,9 +27,13 @@ import java.lang.reflect.Type; ...@@ -27,9 +27,13 @@ import java.lang.reflect.Type;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import static org.mockito.Matchers.endsWith;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.testng.Assert.*; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
/** /**
* AbstractNotificationConsumer tests. * AbstractNotificationConsumer tests.
...@@ -110,17 +114,17 @@ public class AbstractNotificationConsumerTest { ...@@ -110,17 +114,17 @@ public class AbstractNotificationConsumerTest {
assertTrue(consumer.hasNext()); assertTrue(consumer.hasNext());
assertEquals(new TestMessage("sValue2", 98), consumer.next()); assertEquals(new TestMessage("sValue2", 98), consumer.next());
verify(logger).info(json2); verify(logger).info(endsWith(json2));
assertTrue(consumer.hasNext()); assertTrue(consumer.hasNext());
assertEquals(new TestMessage("sValue3", 97), consumer.next()); assertEquals(new TestMessage("sValue3", 97), consumer.next());
verify(logger).info(json3); verify(logger).info(endsWith(json3));
assertTrue(consumer.hasNext()); assertTrue(consumer.hasNext());
assertEquals(new TestMessage("sValue4", 96), consumer.next()); assertEquals(new TestMessage("sValue4", 96), consumer.next());
verify(logger).info(json4); verify(logger).info(endsWith(json4));
assertFalse(consumer.hasNext()); assertFalse(consumer.hasNext());
} }
...@@ -154,7 +158,7 @@ public class AbstractNotificationConsumerTest { ...@@ -154,7 +158,7 @@ public class AbstractNotificationConsumerTest {
consumer.next(); consumer.next();
fail("Expected VersionMismatchException!"); fail("Expected VersionMismatchException!");
} catch (IncompatibleVersionException e) { } catch (IncompatibleVersionException e) {
verify(logger).info(json2); verify(logger).error(endsWith(json2));
} }
assertFalse(consumer.hasNext()); assertFalse(consumer.hasNext());
......
...@@ -3,6 +3,7 @@ Apache Atlas Release Notes ...@@ -3,6 +3,7 @@ Apache Atlas Release Notes
--trunk - unreleased --trunk - unreleased
INCOMPATIBLE CHANGES: INCOMPATIBLE CHANGES:
ATLAS-716 Entity update/delete notifications (shwethags)
ATLAS-619 Canonicalize hive queries (sumasai) ATLAS-619 Canonicalize hive queries (sumasai)
ATLAS-497 Simple Authorization (saqeeb.s via yhemanth) ATLAS-497 Simple Authorization (saqeeb.s via yhemanth)
ATLAS-661 REST API Authentication (nixonrodrigues via yhemanth) ATLAS-661 REST API Authentication (nixonrodrigues via yhemanth)
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
package org.apache.atlas.repository; package org.apache.atlas.repository;
import org.apache.atlas.AtlasClient;
import org.apache.atlas.AtlasException; import org.apache.atlas.AtlasException;
import org.apache.atlas.typesystem.ITypedReferenceableInstance; import org.apache.atlas.typesystem.ITypedReferenceableInstance;
import org.apache.atlas.typesystem.ITypedStruct; import org.apache.atlas.typesystem.ITypedStruct;
...@@ -26,7 +27,6 @@ import org.apache.atlas.typesystem.exception.EntityNotFoundException; ...@@ -26,7 +27,6 @@ import org.apache.atlas.typesystem.exception.EntityNotFoundException;
import org.apache.atlas.typesystem.exception.TraitNotFoundException; import org.apache.atlas.typesystem.exception.TraitNotFoundException;
import org.apache.atlas.typesystem.types.AttributeInfo; import org.apache.atlas.typesystem.types.AttributeInfo;
import org.apache.atlas.typesystem.types.IDataType; import org.apache.atlas.typesystem.types.IDataType;
import org.apache.atlas.typesystem.types.TypeUtils;
import java.util.List; import java.util.List;
...@@ -111,7 +111,7 @@ public interface MetadataRepository { ...@@ -111,7 +111,7 @@ public interface MetadataRepository {
* @return guids of deleted entities * @return guids of deleted entities
* @throws RepositoryException * @throws RepositoryException
*/ */
TypeUtils.Pair<List<String>, List<ITypedReferenceableInstance>> deleteEntities(List<String> guids) throws RepositoryException; AtlasClient.EntityResult deleteEntities(List<String> guids) throws RepositoryException;
// Trait management functions // Trait management functions
...@@ -147,13 +147,13 @@ public interface MetadataRepository { ...@@ -147,13 +147,13 @@ public interface MetadataRepository {
* Adds/Updates the property to the entity that corresponds to the GUID * Adds/Updates the property to the entity that corresponds to the GUID
* Supports only primitive attribute/Class Id updations. * Supports only primitive attribute/Class Id updations.
*/ */
TypeUtils.Pair<List<String>, List<String>> updatePartial(ITypedReferenceableInstance entity) throws RepositoryException; AtlasClient.EntityResult updatePartial(ITypedReferenceableInstance entity) throws RepositoryException;
/** /**
* Adds the property to the entity that corresponds to the GUID * Adds the property to the entity that corresponds to the GUID
* @param entitiesToBeUpdated The entities to be updated * @param entitiesToBeUpdated The entities to be updated
*/ */
TypeUtils.Pair<List<String>, List<String>> updateEntities(ITypedReferenceableInstance... entitiesToBeUpdated) throws RepositoryException; AtlasClient.EntityResult updateEntities(ITypedReferenceableInstance... entitiesToBeUpdated) throws RepositoryException;
/** /**
* Returns the entity for the given type and qualified name * Returns the entity for the given type and qualified name
......
...@@ -55,8 +55,9 @@ public class EntityAuditListener implements EntityChangeListener { ...@@ -55,8 +55,9 @@ public class EntityAuditListener implements EntityChangeListener {
} }
private EntityAuditEvent createEvent(ITypedReferenceableInstance entity, long ts, private EntityAuditEvent createEvent(ITypedReferenceableInstance entity, long ts,
EntityAuditEvent.EntityAuditAction action, String details) { EntityAuditEvent.EntityAuditAction action, String details)
return new EntityAuditEvent(entity.getId()._getId(), ts, RequestContext.get().getUser(), action, details); throws AtlasException {
return new EntityAuditEvent(entity.getId()._getId(), ts, RequestContext.get().getUser(), action, details, entity);
} }
@Override @Override
......
...@@ -78,6 +78,7 @@ public class HBaseBasedAuditRepository implements Service, EntityAuditRepository ...@@ -78,6 +78,7 @@ public class HBaseBasedAuditRepository implements Service, EntityAuditRepository
public static final byte[] COLUMN_ACTION = Bytes.toBytes("action"); public static final byte[] COLUMN_ACTION = Bytes.toBytes("action");
public static final byte[] COLUMN_DETAIL = Bytes.toBytes("detail"); public static final byte[] COLUMN_DETAIL = Bytes.toBytes("detail");
public static final byte[] COLUMN_USER = Bytes.toBytes("user"); public static final byte[] COLUMN_USER = Bytes.toBytes("user");
public static final byte[] COLUMN_DEFINITION = Bytes.toBytes("def");
private TableName tableName; private TableName tableName;
private Connection connection; private Connection connection;
...@@ -110,6 +111,7 @@ public class HBaseBasedAuditRepository implements Service, EntityAuditRepository ...@@ -110,6 +111,7 @@ public class HBaseBasedAuditRepository implements Service, EntityAuditRepository
addColumn(put, COLUMN_ACTION, event.getAction()); addColumn(put, COLUMN_ACTION, event.getAction());
addColumn(put, COLUMN_USER, event.getUser()); addColumn(put, COLUMN_USER, event.getUser());
addColumn(put, COLUMN_DETAIL, event.getDetails()); addColumn(put, COLUMN_DETAIL, event.getDetails());
addColumn(put, COLUMN_DEFINITION, event.getEntityDefinitionString());
puts.add(put); puts.add(put);
} }
table.put(puts); table.put(puts);
...@@ -183,6 +185,7 @@ public class HBaseBasedAuditRepository implements Service, EntityAuditRepository ...@@ -183,6 +185,7 @@ public class HBaseBasedAuditRepository implements Service, EntityAuditRepository
event.setUser(getResultString(result, COLUMN_USER)); event.setUser(getResultString(result, COLUMN_USER));
event.setAction(EntityAuditEvent.EntityAuditAction.valueOf(getResultString(result, COLUMN_ACTION))); event.setAction(EntityAuditEvent.EntityAuditAction.valueOf(getResultString(result, COLUMN_ACTION)));
event.setDetails(getResultString(result, COLUMN_DETAIL)); event.setDetails(getResultString(result, COLUMN_DETAIL));
event.setEntityDefinition(getResultString(result, COLUMN_DEFINITION));
events.add(event); events.add(event);
} }
LOG.info("Got events for entity id {}, starting timestamp {}, #records {}", entityId, startKey, events.size()); LOG.info("Got events for entity id {}, starting timestamp {}, #records {}", entityId, startKey, events.size());
......
...@@ -47,15 +47,16 @@ import static org.apache.atlas.repository.graph.GraphHelper.string; ...@@ -47,15 +47,16 @@ import static org.apache.atlas.repository.graph.GraphHelper.string;
public abstract class DeleteHandler { public abstract class DeleteHandler {
public static final Logger LOG = LoggerFactory.getLogger(DeleteHandler.class); public static final Logger LOG = LoggerFactory.getLogger(DeleteHandler.class);
private static final GraphHelper graphHelper = GraphHelper.getInstance(); protected static final GraphHelper graphHelper = GraphHelper.getInstance();
protected TypeSystem typeSystem; protected TypeSystem typeSystem;
private boolean shouldUpdateReverseAttribute; private boolean shouldUpdateReverseAttribute;
private boolean softDelete;
public DeleteHandler(TypeSystem typeSystem, boolean shouldUpdateReverseAttribute) { public DeleteHandler(TypeSystem typeSystem, boolean shouldUpdateReverseAttribute, boolean softDelete) {
this.typeSystem = typeSystem; this.typeSystem = typeSystem;
this.shouldUpdateReverseAttribute = shouldUpdateReverseAttribute; this.shouldUpdateReverseAttribute = shouldUpdateReverseAttribute;
this.softDelete = softDelete;
} }
/** /**
...@@ -64,16 +65,22 @@ public abstract class DeleteHandler { ...@@ -64,16 +65,22 @@ public abstract class DeleteHandler {
* @throws AtlasException * @throws AtlasException
*/ */
public void deleteEntity(Vertex instanceVertex) throws AtlasException { public void deleteEntity(Vertex instanceVertex) throws AtlasException {
RequestContext requestContext = RequestContext.get();
String guid = GraphHelper.getIdFromVertex(instanceVertex); String guid = GraphHelper.getIdFromVertex(instanceVertex);
Id.EntityState state = GraphHelper.getState(instanceVertex);
if (requestContext.getDeletedEntityIds().contains(guid) || state == Id.EntityState.DELETED) {
LOG.debug("Skipping deleting {} as its already deleted", guid);
return;
}
String typeName = GraphHelper.getTypeName(instanceVertex); String typeName = GraphHelper.getTypeName(instanceVertex);
RequestContext.get().recordDeletedEntity(guid, typeName); requestContext.recordEntityDelete(guid, typeName);
deleteAllTraits(instanceVertex); deleteAllTraits(instanceVertex);
deleteTypeVertex(instanceVertex); deleteTypeVertex(instanceVertex, false);
} }
protected abstract void deleteEdge(Edge edge) throws AtlasException; protected abstract void deleteEdge(Edge edge, boolean force) throws AtlasException;
/** /**
* Deletes a type vertex - can be entity(class type) or just vertex(struct/trait type) * Deletes a type vertex - can be entity(class type) or just vertex(struct/trait type)
...@@ -81,11 +88,11 @@ public abstract class DeleteHandler { ...@@ -81,11 +88,11 @@ public abstract class DeleteHandler {
* @param typeCategory * @param typeCategory
* @throws AtlasException * @throws AtlasException
*/ */
protected void deleteTypeVertex(Vertex instanceVertex, DataTypes.TypeCategory typeCategory) throws AtlasException { protected void deleteTypeVertex(Vertex instanceVertex, DataTypes.TypeCategory typeCategory, boolean force) throws AtlasException {
switch (typeCategory) { switch (typeCategory) {
case STRUCT: case STRUCT:
case TRAIT: case TRAIT:
deleteTypeVertex(instanceVertex); deleteTypeVertex(instanceVertex, force);
break; break;
case CLASS: case CLASS:
...@@ -102,7 +109,7 @@ public abstract class DeleteHandler { ...@@ -102,7 +109,7 @@ public abstract class DeleteHandler {
* @param instanceVertex * @param instanceVertex
* @throws AtlasException * @throws AtlasException
*/ */
protected void deleteTypeVertex(Vertex instanceVertex) throws AtlasException { protected void deleteTypeVertex(Vertex instanceVertex, boolean force) throws AtlasException {
LOG.debug("Deleting {}", string(instanceVertex)); LOG.debug("Deleting {}", string(instanceVertex));
String typeName = GraphHelper.getTypeName(instanceVertex); String typeName = GraphHelper.getTypeName(instanceVertex);
IDataType type = typeSystem.getDataType(IDataType.class, typeName); IDataType type = typeSystem.getDataType(IDataType.class, typeName);
...@@ -115,12 +122,12 @@ public abstract class DeleteHandler { ...@@ -115,12 +122,12 @@ public abstract class DeleteHandler {
switch (attributeInfo.dataType().getTypeCategory()) { switch (attributeInfo.dataType().getTypeCategory()) {
case CLASS: case CLASS:
//If its class attribute, delete the reference //If its class attribute, delete the reference
deleteReference(instanceVertex, edgeLabel, DataTypes.TypeCategory.CLASS, attributeInfo.isComposite); deleteEdgeReference(instanceVertex, edgeLabel, DataTypes.TypeCategory.CLASS, attributeInfo.isComposite);
break; break;
case STRUCT: case STRUCT:
//If its struct attribute, delete the reference //If its struct attribute, delete the reference
deleteReference(instanceVertex, edgeLabel, DataTypes.TypeCategory.STRUCT); deleteEdgeReference(instanceVertex, edgeLabel, DataTypes.TypeCategory.STRUCT, false);
break; break;
case ARRAY: case ARRAY:
...@@ -133,7 +140,7 @@ public abstract class DeleteHandler { ...@@ -133,7 +140,7 @@ public abstract class DeleteHandler {
if (edges != null) { if (edges != null) {
while (edges.hasNext()) { while (edges.hasNext()) {
Edge edge = edges.next(); Edge edge = edges.next();
deleteReference(edge, elementType, attributeInfo); deleteEdgeReference(edge, elementType.getTypeCategory(), attributeInfo.isComposite, false);
} }
} }
} }
...@@ -151,22 +158,31 @@ public abstract class DeleteHandler { ...@@ -151,22 +158,31 @@ public abstract class DeleteHandler {
if (keys != null) { if (keys != null) {
for (String key : keys) { for (String key : keys) {
String mapEdgeLabel = GraphHelper.getQualifiedNameForMapKey(edgeLabel, key); String mapEdgeLabel = GraphHelper.getQualifiedNameForMapKey(edgeLabel, key);
deleteReference(instanceVertex, mapEdgeLabel, valueTypeCategory, attributeInfo.isComposite); deleteEdgeReference(instanceVertex, mapEdgeLabel, valueTypeCategory, attributeInfo.isComposite);
} }
} }
} }
} }
} }
deleteVertex(instanceVertex, type.getTypeCategory()); deleteVertex(instanceVertex, force);
} }
public void deleteReference(Edge edge, IDataType dataType, AttributeInfo attributeInfo) throws AtlasException { /**
deleteReference(edge, dataType.getTypeCategory(), attributeInfo.isComposite); * Force delete is used to remove struct/trait in case of entity updates
} * @param edge
* @param typeCategory
public void deleteReference(Edge edge, DataTypes.TypeCategory typeCategory, boolean isComposite) throws AtlasException { * @param isComposite
* @param forceDeleteStructTrait
* @return returns true if the edge reference is hard deleted
* @throws AtlasException
*/
public boolean deleteEdgeReference(Edge edge, DataTypes.TypeCategory typeCategory, boolean isComposite,
boolean forceDeleteStructTrait) throws AtlasException {
LOG.debug("Deleting {}", string(edge)); LOG.debug("Deleting {}", string(edge));
boolean forceDelete =
(typeCategory == DataTypes.TypeCategory.STRUCT || typeCategory == DataTypes.TypeCategory.TRAIT)
? forceDeleteStructTrait : false;
if (typeCategory == DataTypes.TypeCategory.STRUCT || typeCategory == DataTypes.TypeCategory.TRAIT if (typeCategory == DataTypes.TypeCategory.STRUCT || typeCategory == DataTypes.TypeCategory.TRAIT
|| (typeCategory == DataTypes.TypeCategory.CLASS && isComposite)) { || (typeCategory == DataTypes.TypeCategory.CLASS && isComposite)) {
//If the vertex is of type struct/trait, delete the edge and then the reference vertex as the vertex is not shared by any other entities. //If the vertex is of type struct/trait, delete the edge and then the reference vertex as the vertex is not shared by any other entities.
...@@ -175,32 +191,28 @@ public abstract class DeleteHandler { ...@@ -175,32 +191,28 @@ public abstract class DeleteHandler {
Vertex vertexForDelete = edge.getVertex(Direction.IN); Vertex vertexForDelete = edge.getVertex(Direction.IN);
//If deleting the edge and then the in vertex, reverse attribute shouldn't be updated //If deleting the edge and then the in vertex, reverse attribute shouldn't be updated
deleteEdge(edge, false); deleteEdge(edge, false, forceDelete);
deleteTypeVertex(vertexForDelete, typeCategory); deleteTypeVertex(vertexForDelete, typeCategory, forceDelete);
} else { } else {
//If the vertex is of type class, and its not a composite attributes, the reference vertex' lifecycle is not controlled //If the vertex is of type class, and its not a composite attributes, the reference vertex' lifecycle is not controlled
//through this delete. Hence just remove the reference edge. Leave the reference vertex as is //through this delete. Hence just remove the reference edge. Leave the reference vertex as is
//If deleting just the edge, reverse attribute should be updated for any references //If deleting just the edge, reverse attribute should be updated for any references
//For example, for the department type system, if the person's manager edge is deleted, subordinates of manager should be updated //For example, for the department type system, if the person's manager edge is deleted, subordinates of manager should be updated
deleteEdge(edge, true); deleteEdge(edge, true, false);
} }
return !softDelete || forceDelete;
} }
public void deleteReference(Vertex instanceVertex, String edgeLabel, DataTypes.TypeCategory typeCategory) public void deleteEdgeReference(Vertex outVertex, String edgeLabel, DataTypes.TypeCategory typeCategory,
throws AtlasException { boolean isComposite) throws AtlasException {
deleteReference(instanceVertex, edgeLabel, typeCategory, false); Edge edge = GraphHelper.getEdgeForLabel(outVertex, edgeLabel);
}
public void deleteReference(Vertex instanceVertex, String edgeLabel, DataTypes.TypeCategory typeCategory,
boolean isComposite) throws AtlasException {
Edge edge = GraphHelper.getEdgeForLabel(instanceVertex, edgeLabel);
if (edge != null) { if (edge != null) {
deleteReference(edge, typeCategory, isComposite); deleteEdgeReference(edge, typeCategory, isComposite, false);
} }
} }
protected void deleteEdge(Edge edge, boolean updateReverseAttribute) throws AtlasException { protected void deleteEdge(Edge edge, boolean updateReverseAttribute, boolean force) throws AtlasException {
//update reverse attribute //update reverse attribute
if (updateReverseAttribute) { if (updateReverseAttribute) {
AttributeInfo attributeInfo = getAttributeForEdge(edge.getLabel()); AttributeInfo attributeInfo = getAttributeForEdge(edge.getLabel());
...@@ -210,28 +222,28 @@ public abstract class DeleteHandler { ...@@ -210,28 +222,28 @@ public abstract class DeleteHandler {
} }
} }
deleteEdge(edge); deleteEdge(edge, force);
} }
protected void deleteVertex(Vertex instanceVertex, DataTypes.TypeCategory typeCategory) throws AtlasException { protected void deleteVertex(Vertex instanceVertex, boolean force) throws AtlasException {
//Update external references(incoming edges) to this vertex //Update external references(incoming edges) to this vertex
LOG.debug("Setting the external references to {} to null(removing edges)", string(instanceVertex)); LOG.debug("Setting the external references to {} to null(removing edges)", string(instanceVertex));
Iterator<Edge> edges = instanceVertex.getEdges(Direction.IN).iterator(); Iterator<Edge> edges = instanceVertex.getEdges(Direction.IN).iterator();
while(edges.hasNext()) { while(edges.hasNext()) {
Edge edge = edges.next(); Edge edge = edges.next();
String edgeState = edge.getProperty(Constants.STATE_PROPERTY_KEY); Id.EntityState edgeState = GraphHelper.getState(edge);
if (Id.EntityState.ACTIVE.name().equals(edgeState)) { if (edgeState == Id.EntityState.ACTIVE) {
//Delete only the active edge references //Delete only the active edge references
AttributeInfo attribute = getAttributeForEdge(edge.getLabel()); AttributeInfo attribute = getAttributeForEdge(edge.getLabel());
//TODO use delete edge instead??
deleteEdgeBetweenVertices(edge.getVertex(Direction.OUT), edge.getVertex(Direction.IN), attribute.name); deleteEdgeBetweenVertices(edge.getVertex(Direction.OUT), edge.getVertex(Direction.IN), attribute.name);
deleteEdge(edge);
} }
} }
_deleteVertex(instanceVertex); _deleteVertex(instanceVertex, force);
} }
protected abstract void _deleteVertex(Vertex instanceVertex); protected abstract void _deleteVertex(Vertex instanceVertex, boolean force);
/** /**
* Deletes the edge between outvertex and inVertex. The edge is for attribute attributeName of outVertex * Deletes the edge between outvertex and inVertex. The edge is for attribute attributeName of outVertex
...@@ -245,7 +257,8 @@ public abstract class DeleteHandler { ...@@ -245,7 +257,8 @@ public abstract class DeleteHandler {
attributeName); attributeName);
String typeName = GraphHelper.getTypeName(outVertex); String typeName = GraphHelper.getTypeName(outVertex);
String outId = GraphHelper.getIdFromVertex(outVertex); String outId = GraphHelper.getIdFromVertex(outVertex);
if (outId != null && RequestContext.get().isDeletedEntity(outId)) { Id.EntityState state = GraphHelper.getState(outVertex);
if ((outId != null && RequestContext.get().isDeletedEntity(outId)) || state == Id.EntityState.DELETED) {
//If the reference vertex is marked for deletion, skip updating the reference //If the reference vertex is marked for deletion, skip updating the reference
return; return;
} }
...@@ -261,8 +274,10 @@ public abstract class DeleteHandler { ...@@ -261,8 +274,10 @@ public abstract class DeleteHandler {
//If its class attribute, its the only edge between two vertices //If its class attribute, its the only edge between two vertices
if (attributeInfo.multiplicity.nullAllowed()) { if (attributeInfo.multiplicity.nullAllowed()) {
edge = GraphHelper.getEdgeForLabel(outVertex, edgeLabel); edge = GraphHelper.getEdgeForLabel(outVertex, edgeLabel);
} if (shouldUpdateReverseAttribute) {
else { GraphHelper.setProperty(outVertex, propertyName, null);
}
} else {
// Cannot unset a required attribute. // Cannot unset a required attribute.
throw new NullRequiredAttributeException("Cannot unset required attribute " + GraphHelper.getQualifiedFieldName(type, attributeName) + throw new NullRequiredAttributeException("Cannot unset required attribute " + GraphHelper.getQualifiedFieldName(type, attributeName) +
" on " + string(outVertex) + " edge = " + edgeLabel); " on " + string(outVertex) + " edge = " + edgeLabel);
...@@ -275,23 +290,26 @@ public abstract class DeleteHandler { ...@@ -275,23 +290,26 @@ public abstract class DeleteHandler {
if (elements != null) { if (elements != null) {
elements = new ArrayList<>(elements); //Make a copy, else list.remove reflects on titan.getProperty() elements = new ArrayList<>(elements); //Make a copy, else list.remove reflects on titan.getProperty()
for (String elementEdgeId : elements) { for (String elementEdgeId : elements) {
Edge elementEdge = graphHelper.getEdgeById(elementEdgeId); Edge elementEdge = graphHelper.getEdgeByEdgeId(outVertex, edgeLabel, elementEdgeId);
if (elementEdge == null) { if (elementEdge == null) {
continue; continue;
} }
Vertex elementVertex = elementEdge.getVertex(Direction.IN); Vertex elementVertex = elementEdge.getVertex(Direction.IN);
if (elementVertex.getId().toString().equals(inVertex.getId().toString())) { if (elementVertex.getId().toString().equals(inVertex.getId().toString())) {
if (attributeInfo.multiplicity.nullAllowed() || elements.size() > attributeInfo.multiplicity.lower) { edge = elementEdge;
edge = elementEdge;
} //TODO element.size includes deleted items as well. should exclude
else { if (!attributeInfo.multiplicity.nullAllowed()
&& elements.size() <= attributeInfo.multiplicity.lower) {
// Deleting this edge would violate the attribute's lower bound. // Deleting this edge would violate the attribute's lower bound.
throw new NullRequiredAttributeException( throw new NullRequiredAttributeException(
"Cannot remove array element from required attribute " + "Cannot remove array element from required attribute " +
GraphHelper.getQualifiedFieldName(type, attributeName) + " on " + string(outVertex) + " " + string(elementEdge)); GraphHelper.getQualifiedFieldName(type, attributeName) + " on "
+ string(outVertex) + " " + string(elementEdge));
} }
if (shouldUpdateReverseAttribute || attributeInfo.isComposite) {
if (shouldUpdateReverseAttribute) {
//if composite attribute, remove the reference as well. else, just remove the edge //if composite attribute, remove the reference as well. else, just remove the edge
//for example, when table is deleted, process still references the table //for example, when table is deleted, process still references the table
//but when column is deleted, table will not reference the deleted column //but when column is deleted, table will not reference the deleted column
...@@ -299,8 +317,9 @@ public abstract class DeleteHandler { ...@@ -299,8 +317,9 @@ public abstract class DeleteHandler {
attributeName); attributeName);
elements.remove(elementEdge.getId().toString()); elements.remove(elementEdge.getId().toString());
GraphHelper.setProperty(outVertex, propertyName, elements); GraphHelper.setProperty(outVertex, propertyName, elements);
break;
} }
break;
} }
} }
} }
...@@ -312,11 +331,12 @@ public abstract class DeleteHandler { ...@@ -312,11 +331,12 @@ public abstract class DeleteHandler {
if (keys != null) { if (keys != null) {
keys = new ArrayList<>(keys); //Make a copy, else list.remove reflects on titan.getProperty() keys = new ArrayList<>(keys); //Make a copy, else list.remove reflects on titan.getProperty()
for (String key : keys) { for (String key : keys) {
String keyPropertyName = propertyName + "." + key; String keyPropertyName = GraphHelper.getQualifiedNameForMapKey(propertyName, key);
String mapEdgeId = outVertex.getProperty(keyPropertyName); String mapEdgeId = outVertex.getProperty(keyPropertyName);
Edge mapEdge = graphHelper.getEdgeById(mapEdgeId); Edge mapEdge = graphHelper.getEdgeByEdgeId(outVertex, keyPropertyName, mapEdgeId);
Vertex mapVertex = mapEdge.getVertex(Direction.IN); Vertex mapVertex = mapEdge.getVertex(Direction.IN);
if (mapVertex.getId().toString().equals(inVertex.getId().toString())) { if (mapVertex.getId().toString().equals(inVertex.getId().toString())) {
//TODO keys.size includes deleted items as well. should exclude
if (attributeInfo.multiplicity.nullAllowed() || keys.size() > attributeInfo.multiplicity.lower) { if (attributeInfo.multiplicity.nullAllowed() || keys.size() > attributeInfo.multiplicity.lower) {
edge = mapEdge; edge = mapEdge;
} }
...@@ -327,7 +347,7 @@ public abstract class DeleteHandler { ...@@ -327,7 +347,7 @@ public abstract class DeleteHandler {
GraphHelper.getQualifiedFieldName(type, attributeName) + " on " + string(outVertex) + " " + string(mapEdge)); GraphHelper.getQualifiedFieldName(type, attributeName) + " on " + string(outVertex) + " " + string(mapEdge));
} }
if (shouldUpdateReverseAttribute || attributeInfo.isComposite) { if (shouldUpdateReverseAttribute) {
//remove this key //remove this key
LOG.debug("Removing edge {}, key {} from the map attribute {}", string(mapEdge), key, LOG.debug("Removing edge {}, key {} from the map attribute {}", string(mapEdge), key,
attributeName); attributeName);
...@@ -351,9 +371,11 @@ public abstract class DeleteHandler { ...@@ -351,9 +371,11 @@ public abstract class DeleteHandler {
} }
if (edge != null) { if (edge != null) {
deleteEdge(edge); deleteEdge(edge, false);
RequestContext requestContext = RequestContext.get();
GraphHelper.setProperty(outVertex, Constants.MODIFICATION_TIMESTAMP_PROPERTY_KEY, GraphHelper.setProperty(outVertex, Constants.MODIFICATION_TIMESTAMP_PROPERTY_KEY,
RequestContext.get().getRequestTime()); requestContext.getRequestTime());
requestContext.recordEntityUpdate(outId);
} }
} }
...@@ -389,7 +411,7 @@ public abstract class DeleteHandler { ...@@ -389,7 +411,7 @@ public abstract class DeleteHandler {
for (String traitNameToBeDeleted : traitNames) { for (String traitNameToBeDeleted : traitNames) {
String relationshipLabel = GraphHelper.getTraitLabel(typeName, traitNameToBeDeleted); String relationshipLabel = GraphHelper.getTraitLabel(typeName, traitNameToBeDeleted);
deleteReference(instanceVertex, relationshipLabel, DataTypes.TypeCategory.TRAIT); deleteEdgeReference(instanceVertex, relationshipLabel, DataTypes.TypeCategory.TRAIT, false);
} }
} }
} }
...@@ -22,8 +22,10 @@ import com.google.common.base.Preconditions; ...@@ -22,8 +22,10 @@ import com.google.common.base.Preconditions;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import com.thinkaurelius.titan.core.TitanGraph; import com.thinkaurelius.titan.core.TitanGraph;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.GraphQuery; import com.tinkerpop.blueprints.GraphQuery;
import com.tinkerpop.blueprints.Vertex; import com.tinkerpop.blueprints.Vertex;
import org.apache.atlas.AtlasClient;
import org.apache.atlas.AtlasException; import org.apache.atlas.AtlasException;
import org.apache.atlas.GraphTransaction; import org.apache.atlas.GraphTransaction;
import org.apache.atlas.RequestContext; import org.apache.atlas.RequestContext;
...@@ -40,7 +42,6 @@ import org.apache.atlas.typesystem.types.ClassType; ...@@ -40,7 +42,6 @@ import org.apache.atlas.typesystem.types.ClassType;
import org.apache.atlas.typesystem.types.DataTypes; import org.apache.atlas.typesystem.types.DataTypes;
import org.apache.atlas.typesystem.types.IDataType; import org.apache.atlas.typesystem.types.IDataType;
import org.apache.atlas.typesystem.types.TypeSystem; import org.apache.atlas.typesystem.types.TypeSystem;
import org.apache.atlas.typesystem.types.TypeUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -258,8 +259,8 @@ public class GraphBackedMetadataRepository implements MetadataRepository { ...@@ -258,8 +259,8 @@ public class GraphBackedMetadataRepository implements MetadataRepository {
try { try {
final String entityTypeName = GraphHelper.getTypeName(instanceVertex); final String entityTypeName = GraphHelper.getTypeName(instanceVertex);
String relationshipLabel = GraphHelper.getTraitLabel(entityTypeName, traitNameToBeDeleted); String relationshipLabel = GraphHelper.getTraitLabel(entityTypeName, traitNameToBeDeleted);
Edge edge = GraphHelper.getEdgeForLabel(instanceVertex, relationshipLabel);
deleteHandler.deleteReference(instanceVertex, relationshipLabel, DataTypes.TypeCategory.TRAIT); deleteHandler.deleteEdgeReference(edge, DataTypes.TypeCategory.TRAIT, false, true);
// update the traits in entity once trait removal is successful // update the traits in entity once trait removal is successful
traitNames.remove(traitNameToBeDeleted); traitNames.remove(traitNameToBeDeleted);
...@@ -284,14 +285,15 @@ public class GraphBackedMetadataRepository implements MetadataRepository { ...@@ -284,14 +285,15 @@ public class GraphBackedMetadataRepository implements MetadataRepository {
@Override @Override
@GraphTransaction @GraphTransaction
public TypeUtils.Pair<List<String>, List<String>> updateEntities(ITypedReferenceableInstance... entitiesUpdated) throws RepositoryException { public AtlasClient.EntityResult updateEntities(ITypedReferenceableInstance... entitiesUpdated) throws RepositoryException {
LOG.info("updating entity {}", entitiesUpdated); LOG.info("updating entity {}", entitiesUpdated);
try { try {
TypedInstanceToGraphMapper instanceToGraphMapper = new TypedInstanceToGraphMapper(graphToInstanceMapper, deleteHandler); TypedInstanceToGraphMapper instanceToGraphMapper = new TypedInstanceToGraphMapper(graphToInstanceMapper, deleteHandler);
instanceToGraphMapper.mapTypedInstanceToGraph(TypedInstanceToGraphMapper.Operation.UPDATE_FULL, instanceToGraphMapper.mapTypedInstanceToGraph(TypedInstanceToGraphMapper.Operation.UPDATE_FULL,
entitiesUpdated); entitiesUpdated);
RequestContext requestContext = RequestContext.get(); RequestContext requestContext = RequestContext.get();
return TypeUtils.Pair.of(requestContext.getCreatedEntityIds(), requestContext.getUpdatedEntityIds()); return new AtlasClient.EntityResult(requestContext.getCreatedEntityIds(),
requestContext.getUpdatedEntityIds(), requestContext.getDeletedEntityIds());
} catch (AtlasException e) { } catch (AtlasException e) {
throw new RepositoryException(e); throw new RepositoryException(e);
} }
...@@ -299,13 +301,14 @@ public class GraphBackedMetadataRepository implements MetadataRepository { ...@@ -299,13 +301,14 @@ public class GraphBackedMetadataRepository implements MetadataRepository {
@Override @Override
@GraphTransaction @GraphTransaction
public TypeUtils.Pair<List<String>, List<String>> updatePartial(ITypedReferenceableInstance entity) throws RepositoryException { public AtlasClient.EntityResult updatePartial(ITypedReferenceableInstance entity) throws RepositoryException {
LOG.info("updating entity {}", entity); LOG.info("updating entity {}", entity);
try { try {
TypedInstanceToGraphMapper instanceToGraphMapper = new TypedInstanceToGraphMapper(graphToInstanceMapper, deleteHandler); TypedInstanceToGraphMapper instanceToGraphMapper = new TypedInstanceToGraphMapper(graphToInstanceMapper, deleteHandler);
instanceToGraphMapper.mapTypedInstanceToGraph(TypedInstanceToGraphMapper.Operation.UPDATE_PARTIAL, entity); instanceToGraphMapper.mapTypedInstanceToGraph(TypedInstanceToGraphMapper.Operation.UPDATE_PARTIAL, entity);
RequestContext requestContext = RequestContext.get(); RequestContext requestContext = RequestContext.get();
return TypeUtils.Pair.of(requestContext.getCreatedEntityIds(), requestContext.getUpdatedEntityIds()); return new AtlasClient.EntityResult(requestContext.getCreatedEntityIds(),
requestContext.getUpdatedEntityIds(), requestContext.getDeletedEntityIds());
} catch (AtlasException e) { } catch (AtlasException e) {
throw new RepositoryException(e); throw new RepositoryException(e);
} }
...@@ -313,7 +316,7 @@ public class GraphBackedMetadataRepository implements MetadataRepository { ...@@ -313,7 +316,7 @@ public class GraphBackedMetadataRepository implements MetadataRepository {
@Override @Override
@GraphTransaction @GraphTransaction
public TypeUtils.Pair<List<String>, List<ITypedReferenceableInstance>> deleteEntities(List<String> guids) throws RepositoryException { public AtlasClient.EntityResult deleteEntities(List<String> guids) throws RepositoryException {
if (guids == null || guids.size() == 0) { if (guids == null || guids.size() == 0) {
throw new IllegalArgumentException("guids must be non-null and non-empty"); throw new IllegalArgumentException("guids must be non-null and non-empty");
...@@ -337,6 +340,7 @@ public class GraphBackedMetadataRepository implements MetadataRepository { ...@@ -337,6 +340,7 @@ public class GraphBackedMetadataRepository implements MetadataRepository {
} }
} }
RequestContext requestContext = RequestContext.get(); RequestContext requestContext = RequestContext.get();
return new TypeUtils.Pair<>(requestContext.getDeletedEntityIds(), requestContext.getDeletedEntities()); return new AtlasClient.EntityResult(requestContext.getCreatedEntityIds(),
requestContext.getUpdatedEntityIds(), requestContext.getDeletedEntityIds());
} }
} }
...@@ -107,11 +107,13 @@ public final class GraphHelper { ...@@ -107,11 +107,13 @@ public final class GraphHelper {
// add timestamp information // add timestamp information
setProperty(vertexWithoutIdentity, Constants.TIMESTAMP_PROPERTY_KEY, RequestContext.get().getRequestTime()); setProperty(vertexWithoutIdentity, Constants.TIMESTAMP_PROPERTY_KEY, RequestContext.get().getRequestTime());
setProperty(vertexWithoutIdentity, Constants.MODIFICATION_TIMESTAMP_PROPERTY_KEY,
RequestContext.get().getRequestTime());
return vertexWithoutIdentity; return vertexWithoutIdentity;
} }
public Edge addEdge(Vertex fromVertex, Vertex toVertex, String edgeLabel) { private Edge addEdge(Vertex fromVertex, Vertex toVertex, String edgeLabel) {
LOG.debug("Adding edge for {} -> label {} -> {}", string(fromVertex), edgeLabel, string(toVertex)); LOG.debug("Adding edge for {} -> label {} -> {}", string(fromVertex), edgeLabel, string(toVertex));
Edge edge = titanGraph.addEdge(null, fromVertex, toVertex, edgeLabel); Edge edge = titanGraph.addEdge(null, fromVertex, toVertex, edgeLabel);
...@@ -127,12 +129,34 @@ public final class GraphHelper { ...@@ -127,12 +129,34 @@ public final class GraphHelper {
Iterable<Edge> edges = inVertex.getEdges(Direction.IN, edgeLabel); Iterable<Edge> edges = inVertex.getEdges(Direction.IN, edgeLabel);
for (Edge edge : edges) { for (Edge edge : edges) {
if (edge.getVertex(Direction.OUT).getId().toString().equals(outVertex.getId().toString())) { if (edge.getVertex(Direction.OUT).getId().toString().equals(outVertex.getId().toString())) {
return edge; Id.EntityState edgeState = getState(edge);
if (edgeState == null || edgeState == Id.EntityState.ACTIVE) {
return edge;
}
} }
} }
return addEdge(outVertex, inVertex, edgeLabel); return addEdge(outVertex, inVertex, edgeLabel);
} }
public Edge getEdgeByEdgeId(Vertex outVertex, String edgeLabel, String edgeId) {
if (edgeId == null) {
return null;
}
return titanGraph.getEdge(edgeId);
//TODO get edge id is expensive. Use this logic. But doesn't work for now
/**
Iterable<Edge> edges = outVertex.getEdges(Direction.OUT, edgeLabel);
for (Edge edge : edges) {
if (edge.getId().toString().equals(edgeId)) {
return edge;
}
}
return null;
**/
}
/** /**
* Args of the format prop1, key1, prop2, key2... * Args of the format prop1, key1, prop2, key2...
* Searches for a vertex with prop1=key1 && prop2=key2 * Searches for a vertex with prop1=key1 && prop2=key2
...@@ -180,15 +204,14 @@ public final class GraphHelper { ...@@ -180,15 +204,14 @@ public final class GraphHelper {
* @return * @return
*/ */
public static Edge getEdgeForLabel(Vertex vertex, String edgeLabel) { public static Edge getEdgeForLabel(Vertex vertex, String edgeLabel) {
String vertexState = vertex.getProperty(Constants.STATE_PROPERTY_KEY);
Iterator<Edge> iterator = GraphHelper.getOutGoingEdgesByLabel(vertex, edgeLabel); Iterator<Edge> iterator = GraphHelper.getOutGoingEdgesByLabel(vertex, edgeLabel);
Edge latestDeletedEdge = null; Edge latestDeletedEdge = null;
long latestDeletedEdgeTime = Long.MIN_VALUE; long latestDeletedEdgeTime = Long.MIN_VALUE;
while (iterator != null && iterator.hasNext()) { while (iterator != null && iterator.hasNext()) {
Edge edge = iterator.next(); Edge edge = iterator.next();
String edgeState = edge.getProperty(Constants.STATE_PROPERTY_KEY); Id.EntityState edgeState = getState(edge);
if (edgeState == null || Id.EntityState.ACTIVE.name().equals(edgeState)) { if (edgeState == null || edgeState == Id.EntityState.ACTIVE) {
LOG.debug("Found {}", string(edge)); LOG.debug("Found {}", string(edge));
return edge; return edge;
} else { } else {
...@@ -201,19 +224,8 @@ public final class GraphHelper { ...@@ -201,19 +224,8 @@ public final class GraphHelper {
} }
//If the vertex is deleted, return latest deleted edge //If the vertex is deleted, return latest deleted edge
if (Id.EntityState.DELETED.equals(vertexState)) { LOG.debug("Found {}", latestDeletedEdge == null ? "null" : string(latestDeletedEdge));
LOG.debug("Found {}", string(latestDeletedEdge)); return latestDeletedEdge;
return latestDeletedEdge;
}
return null;
}
public Edge getEdgeById(String edgeId) {
if(edgeId != null) {
return titanGraph.getEdge(edgeId);
}
return null;
} }
public static String vertexString(final Vertex vertex) { public static String vertexString(final Vertex vertex) {
...@@ -343,6 +355,15 @@ public final class GraphHelper { ...@@ -343,6 +355,15 @@ public final class GraphHelper {
return instanceVertex.getProperty(Constants.ENTITY_TYPE_PROPERTY_KEY); return instanceVertex.getProperty(Constants.ENTITY_TYPE_PROPERTY_KEY);
} }
public static Id.EntityState getState(Element element) {
String state = getStateAsString(element);
return state == null ? null : Id.EntityState.valueOf(state);
}
public static String getStateAsString(Element element) {
return element.getProperty(Constants.STATE_PROPERTY_KEY);
}
/** /**
* For the given type, finds an unique attribute and checks if there is an existing instance with the same * For the given type, finds an unique attribute and checks if there is an existing instance with the same
* unique value * unique value
......
...@@ -68,9 +68,9 @@ public final class GraphToTypedInstanceMapper { ...@@ -68,9 +68,9 @@ public final class GraphToTypedInstanceMapper {
LOG.debug("Mapping graph root vertex {} to typed instance for guid {}", instanceVertex, guid); LOG.debug("Mapping graph root vertex {} to typed instance for guid {}", instanceVertex, guid);
String typeName = instanceVertex.getProperty(Constants.ENTITY_TYPE_PROPERTY_KEY); String typeName = instanceVertex.getProperty(Constants.ENTITY_TYPE_PROPERTY_KEY);
List<String> traits = GraphHelper.getTraitNames(instanceVertex); List<String> traits = GraphHelper.getTraitNames(instanceVertex);
String state = GraphHelper.getStateAsString(instanceVertex);
Id id = new Id(guid, instanceVertex.<Integer>getProperty(Constants.VERSION_PROPERTY_KEY), typeName, Id id = new Id(guid, instanceVertex.<Integer>getProperty(Constants.VERSION_PROPERTY_KEY), typeName, state);
instanceVertex.<String>getProperty(Constants.STATE_PROPERTY_KEY));
LOG.debug("Created id {} for instance type {}", id, typeName); LOG.debug("Created id {} for instance type {}", id, typeName);
ClassType classType = typeSystem.getDataType(ClassType.class, typeName); ClassType classType = typeSystem.getDataType(ClassType.class, typeName);
...@@ -161,9 +161,9 @@ public final class GraphToTypedInstanceMapper { ...@@ -161,9 +161,9 @@ public final class GraphToTypedInstanceMapper {
Edge edge; Edge edge;
if (edgeId == null) { if (edgeId == null) {
edge = GraphHelper.getEdgeForLabel(instanceVertex, relationshipLabel);; edge = GraphHelper.getEdgeForLabel(instanceVertex, relationshipLabel);
} else { } else {
edge = graphHelper.getEdgeById(edgeId); edge = graphHelper.getEdgeByEdgeId(instanceVertex, relationshipLabel, edgeId);
} }
if (edge != null) { if (edge != null) {
...@@ -175,9 +175,10 @@ public final class GraphToTypedInstanceMapper { ...@@ -175,9 +175,10 @@ public final class GraphToTypedInstanceMapper {
LOG.debug("Found composite, mapping vertex to instance"); LOG.debug("Found composite, mapping vertex to instance");
return mapGraphToTypedInstance(guid, referenceVertex); return mapGraphToTypedInstance(guid, referenceVertex);
} else { } else {
String state = GraphHelper.getStateAsString(referenceVertex);
Id referenceId = Id referenceId =
new Id(guid, referenceVertex.<Integer>getProperty(Constants.VERSION_PROPERTY_KEY), new Id(guid, referenceVertex.<Integer>getProperty(Constants.VERSION_PROPERTY_KEY),
dataType.getName()); dataType.getName(), state);
LOG.debug("Found non-composite, adding id {} ", referenceId); LOG.debug("Found non-composite, adding id {} ", referenceId);
return referenceId; return referenceId;
} }
...@@ -271,7 +272,7 @@ public final class GraphToTypedInstanceMapper { ...@@ -271,7 +272,7 @@ public final class GraphToTypedInstanceMapper {
if (edgeId == null) { if (edgeId == null) {
edge = GraphHelper.getEdgeForLabel(instanceVertex, relationshipLabel); edge = GraphHelper.getEdgeForLabel(instanceVertex, relationshipLabel);
} else { } else {
edge = graphHelper.getEdgeById(edgeId); edge = graphHelper.getEdgeByEdgeId(instanceVertex, relationshipLabel, edgeId);
} }
if (edge != null) { if (edge != null) {
......
...@@ -26,20 +26,18 @@ import org.apache.atlas.typesystem.types.TypeSystem; ...@@ -26,20 +26,18 @@ import org.apache.atlas.typesystem.types.TypeSystem;
public class HardDeleteHandler extends DeleteHandler { public class HardDeleteHandler extends DeleteHandler {
private static final GraphHelper graphHelper = GraphHelper.getInstance();
@Inject @Inject
public HardDeleteHandler(TypeSystem typeSystem) { public HardDeleteHandler(TypeSystem typeSystem) {
super(typeSystem, true); super(typeSystem, true, false);
} }
@Override @Override
protected void _deleteVertex(Vertex instanceVertex) { protected void _deleteVertex(Vertex instanceVertex, boolean force) {
graphHelper.removeVertex(instanceVertex); graphHelper.removeVertex(instanceVertex);
} }
@Override @Override
protected void deleteEdge(Edge edge) throws AtlasException { protected void deleteEdge(Edge edge, boolean force) throws AtlasException {
graphHelper.removeEdge(edge); graphHelper.removeEdge(edge);
} }
} }
...@@ -32,24 +32,34 @@ import static org.apache.atlas.repository.Constants.STATE_PROPERTY_KEY; ...@@ -32,24 +32,34 @@ import static org.apache.atlas.repository.Constants.STATE_PROPERTY_KEY;
public class SoftDeleteHandler extends DeleteHandler { public class SoftDeleteHandler extends DeleteHandler {
@Inject @Inject
public SoftDeleteHandler(TypeSystem typeSystem) { public SoftDeleteHandler(TypeSystem typeSystem) {
super(typeSystem, false); super(typeSystem, false, true);
} }
@Override @Override
protected void _deleteVertex(Vertex instanceVertex) { protected void _deleteVertex(Vertex instanceVertex, boolean force) {
Id.EntityState state = Id.EntityState.valueOf((String) instanceVertex.getProperty(STATE_PROPERTY_KEY)); if (force) {
if (state != Id.EntityState.DELETED) { graphHelper.removeVertex(instanceVertex);
GraphHelper.setProperty(instanceVertex, STATE_PROPERTY_KEY, Id.EntityState.DELETED.name()); } else {
GraphHelper.setProperty(instanceVertex, MODIFICATION_TIMESTAMP_PROPERTY_KEY, RequestContext.get().getRequestTime()); Id.EntityState state = GraphHelper.getState(instanceVertex);
if (state != Id.EntityState.DELETED) {
GraphHelper.setProperty(instanceVertex, STATE_PROPERTY_KEY, Id.EntityState.DELETED.name());
GraphHelper.setProperty(instanceVertex, MODIFICATION_TIMESTAMP_PROPERTY_KEY,
RequestContext.get().getRequestTime());
}
} }
} }
@Override @Override
protected void deleteEdge(Edge edge) throws AtlasException { protected void deleteEdge(Edge edge, boolean force) throws AtlasException {
Id.EntityState state = Id.EntityState.valueOf((String) edge.getProperty(STATE_PROPERTY_KEY)); if (force) {
if (state != Id.EntityState.DELETED) { graphHelper.removeEdge(edge);
GraphHelper.setProperty(edge, STATE_PROPERTY_KEY, Id.EntityState.DELETED.name()); } else {
GraphHelper.setProperty(edge, MODIFICATION_TIMESTAMP_PROPERTY_KEY, RequestContext.get().getRequestTime()); Id.EntityState state = GraphHelper.getState(edge);
if (state != Id.EntityState.DELETED) {
GraphHelper.setProperty(edge, STATE_PROPERTY_KEY, Id.EntityState.DELETED.name());
GraphHelper
.setProperty(edge, MODIFICATION_TIMESTAMP_PROPERTY_KEY, RequestContext.get().getRequestTime());
}
} }
} }
} }
...@@ -101,15 +101,15 @@ public final class TypedInstanceToGraphMapper { ...@@ -101,15 +101,15 @@ public final class TypedInstanceToGraphMapper {
case CREATE: case CREATE:
List<String> ids = addOrUpdateAttributesAndTraits(operation, entitiesToCreate); List<String> ids = addOrUpdateAttributesAndTraits(operation, entitiesToCreate);
addFullTextProperty(entitiesToCreate, fulltextMapper); addFullTextProperty(entitiesToCreate, fulltextMapper);
requestContext.recordCreatedEntities(ids); requestContext.recordEntityCreate(ids);
break; break;
case UPDATE_FULL: case UPDATE_FULL:
case UPDATE_PARTIAL: case UPDATE_PARTIAL:
ids = addOrUpdateAttributesAndTraits(Operation.CREATE, entitiesToCreate); ids = addOrUpdateAttributesAndTraits(Operation.CREATE, entitiesToCreate);
requestContext.recordCreatedEntities(ids); requestContext.recordEntityCreate(ids);
ids = addOrUpdateAttributesAndTraits(operation, entitiesToUpdate); ids = addOrUpdateAttributesAndTraits(operation, entitiesToUpdate);
requestContext.recordUpdatedEntities(ids); requestContext.recordEntityUpdate(ids);
addFullTextProperty(entitiesToCreate, fulltextMapper); addFullTextProperty(entitiesToCreate, fulltextMapper);
addFullTextProperty(entitiesToUpdate, fulltextMapper); addFullTextProperty(entitiesToUpdate, fulltextMapper);
...@@ -218,8 +218,8 @@ public final class TypedInstanceToGraphMapper { ...@@ -218,8 +218,8 @@ public final class TypedInstanceToGraphMapper {
attrValue, currentEdge, edgeLabel, operation); attrValue, currentEdge, edgeLabel, operation);
if (currentEdge != null && !currentEdge.getId().toString().equals(newEdgeId)) { if (currentEdge != null && !currentEdge.getId().toString().equals(newEdgeId)) {
deleteHandler.deleteReference(currentEdge, attributeInfo.dataType().getTypeCategory(), deleteHandler.deleteEdgeReference(currentEdge, attributeInfo.dataType().getTypeCategory(),
attributeInfo.isComposite); attributeInfo.isComposite, true);
} }
break; break;
...@@ -326,7 +326,7 @@ public final class TypedInstanceToGraphMapper { ...@@ -326,7 +326,7 @@ public final class TypedInstanceToGraphMapper {
String propertyName = GraphHelper.getQualifiedFieldName(typedInstance, attributeInfo); String propertyName = GraphHelper.getQualifiedFieldName(typedInstance, attributeInfo);
List<String> currentElements = instanceVertex.getProperty(propertyName); List<String> currentElements = instanceVertex.getProperty(propertyName);
IDataType elementType = ((DataTypes.ArrayType) attributeInfo.dataType()).getElemType(); IDataType elementType = ((DataTypes.ArrayType) attributeInfo.dataType()).getElemType();
List<String> newElementsCreated = new ArrayList<>(); List<Object> newElementsCreated = new ArrayList<>();
if (!newAttributeEmpty) { if (!newAttributeEmpty) {
if (newElements != null && !newElements.isEmpty()) { if (newElements != null && !newElements.isEmpty()) {
...@@ -336,75 +336,53 @@ public final class TypedInstanceToGraphMapper { ...@@ -336,75 +336,53 @@ public final class TypedInstanceToGraphMapper {
currentElements.get(index) : null; currentElements.get(index) : null;
LOG.debug("Adding/updating element at position {}, current element {}, new element {}", index, LOG.debug("Adding/updating element at position {}, current element {}, new element {}", index,
currentElement, newElements.get(index)); currentElement, newElements.get(index));
String newEntry = addOrUpdateCollectionEntry(instanceVertex, attributeInfo, elementType, Object newEntry = addOrUpdateCollectionEntry(instanceVertex, attributeInfo, elementType,
newElements.get(index), currentElement, propertyName, operation); newElements.get(index), currentElement, propertyName, operation);
newElementsCreated.add(newEntry); newElementsCreated.add(newEntry);
} }
} }
} }
List<String> additionalEdges = removeUnusedEntries(instanceVertex, propertyName, currentElements,
newElementsCreated, elementType, attributeInfo);
newElementsCreated.addAll(additionalEdges);
// for dereference on way out // for dereference on way out
GraphHelper.setProperty(instanceVertex, propertyName, newElementsCreated); GraphHelper.setProperty(instanceVertex, propertyName, newElementsCreated);
removeUnusedEntries(currentElements, newElementsCreated, elementType, attributeInfo);
} }
private void removeUnusedEntries(List<String> currentEntries, List<String> newEntries, IDataType entryType, //Removes unused edges from the old collection, compared to the new collection
AttributeInfo attributeInfo) throws AtlasException { private List<String> removeUnusedEntries(Vertex instanceVertex, String edgeLabel,
if (currentEntries == null || currentEntries.isEmpty()) { Collection<String> currentEntries,
return; Collection<Object> newEntries,
} IDataType entryType, AttributeInfo attributeInfo) throws AtlasException {
if (currentEntries != null && !currentEntries.isEmpty()) {
LOG.debug("Removing unused entries from the old collection"); LOG.debug("Removing unused entries from the old collection");
if (entryType.getTypeCategory() == DataTypes.TypeCategory.STRUCT if (entryType.getTypeCategory() == DataTypes.TypeCategory.STRUCT
|| entryType.getTypeCategory() == DataTypes.TypeCategory.CLASS) { || entryType.getTypeCategory() == DataTypes.TypeCategory.CLASS) {
//Get map of edge id to edge //Remove the edges for (current edges - new edges)
Map<String, Edge> edgeMap = new HashMap<>(); List<String> cloneElements = new ArrayList<>(currentEntries);
getEdges(currentEntries, edgeMap); cloneElements.removeAll(newEntries);
getEdges(newEntries, edgeMap); List<String> additionalElements = new ArrayList<>();
LOG.debug("Removing unused entries from the old collection - {}", cloneElements);
//Get final set of in vertices
Set<String> newInVertices = new HashSet<>(); if (!cloneElements.isEmpty()) {
for (String edgeId : newEntries) { for (String edgeIdForDelete : cloneElements) {
Vertex inVertex = edgeMap.get(edgeId).getVertex(Direction.IN); Edge edge = graphHelper.getEdgeByEdgeId(instanceVertex, edgeLabel, edgeIdForDelete);
newInVertices.add(inVertex.getId().toString()); boolean deleted = deleteHandler.deleteEdgeReference(edge, entryType.getTypeCategory(),
} attributeInfo.isComposite, true);
if (!deleted) {
//Remove the edges for (current edges - new edges) additionalElements.add(edgeIdForDelete);
List<String> cloneElements = new ArrayList<>(currentEntries); }
cloneElements.removeAll(newEntries);
LOG.debug("Removing unused entries from the old collection - {}", cloneElements);
if (!cloneElements.isEmpty()) {
for (String edgeIdForDelete : cloneElements) {
Edge edge = edgeMap.get(edgeIdForDelete);
Vertex inVertex = edge.getVertex(Direction.IN);
if (newInVertices.contains(inVertex.getId().toString())) {
//If the edge.inVertex is in the new set of in vertices, just delete the edge
deleteHandler.deleteEdge(edge, true);
} else {
//else delete the edge + vertex
deleteHandler.deleteReference(edge, entryType.getTypeCategory(), attributeInfo.isComposite);
} }
} }
return additionalElements;
} }
} }
return new ArrayList<>();
} }
private void getEdges(List<String> edgeIds, Map<String, Edge> edgeMap) {
if (edgeIds == null) {
return;
}
for (String edgeId : edgeIds) {
if (!edgeMap.containsKey(edgeId)) {
edgeMap.put(edgeId, graphHelper.getEdgeById(edgeId));
}
}
}
/******************************************** MAP **************************************************/ /******************************************** MAP **************************************************/
private void mapMapCollectionToVertex(ITypedInstance typedInstance, Vertex instanceVertex, private void mapMapCollectionToVertex(ITypedInstance typedInstance, Vertex instanceVertex,
...@@ -421,38 +399,81 @@ public final class TypedInstanceToGraphMapper { ...@@ -421,38 +399,81 @@ public final class TypedInstanceToGraphMapper {
IDataType elementType = ((DataTypes.MapType) attributeInfo.dataType()).getValueType(); IDataType elementType = ((DataTypes.MapType) attributeInfo.dataType()).getValueType();
String propertyName = GraphHelper.getQualifiedFieldName(typedInstance, attributeInfo); String propertyName = GraphHelper.getQualifiedFieldName(typedInstance, attributeInfo);
List<String> currentElements = new ArrayList<>();
List<String> newElementsCreated = new ArrayList<>(); Map<String, String> currentMap = new HashMap<>();
List<String> newKeysCreated = new ArrayList<>(); Map<String, Object> newMap = new HashMap<>();
List<String> currentKeys = instanceVertex.getProperty(propertyName);
if (currentKeys != null && !currentKeys.isEmpty()) {
for (String key : currentKeys) {
String propertyNameForKey = GraphHelper.getQualifiedNameForMapKey(propertyName, key);
String propertyValueForKey = instanceVertex.getProperty(propertyNameForKey).toString();
currentMap.put(key, propertyValueForKey);
}
}
if (!newAttributeEmpty) { if (!newAttributeEmpty) {
for (Map.Entry entry : newAttribute.entrySet()) { for (Map.Entry entry : newAttribute.entrySet()) {
String propertyNameForKey = GraphHelper.getQualifiedNameForMapKey(propertyName, entry.getKey().toString()); String keyStr = entry.getKey().toString();
newKeysCreated.add(entry.getKey().toString()); String propertyNameForKey = GraphHelper.getQualifiedNameForMapKey(propertyName, keyStr);
String currentEntry = instanceVertex.getProperty(propertyNameForKey); Object newEntry = addOrUpdateCollectionEntry(instanceVertex, attributeInfo, elementType,
if (currentEntry != null) { entry.getValue(), currentMap.get(keyStr), propertyNameForKey, operation);
currentElements.add(currentEntry);
}
String newEntry = addOrUpdateCollectionEntry(instanceVertex, attributeInfo, elementType,
entry.getValue(), currentEntry, propertyNameForKey, operation);
//Add/Update/Remove property value //Add/Update/Remove property value
GraphHelper.setProperty(instanceVertex, propertyNameForKey, newEntry); GraphHelper.setProperty(instanceVertex, propertyNameForKey, newEntry);
newElementsCreated.add(newEntry); newMap.put(keyStr, newEntry);
} }
} }
Map<String, String> additionalMap =
removeUnusedMapEntries(instanceVertex, propertyName, currentMap, newMap, elementType, attributeInfo);
Set<String> newKeys = new HashSet<>(newMap.keySet());
newKeys.addAll(additionalMap.keySet());
// for dereference on way out // for dereference on way out
GraphHelper.setProperty(instanceVertex, propertyName, newKeysCreated); GraphHelper.setProperty(instanceVertex, propertyName, new ArrayList<>(newKeys));
}
//Remove unused entries from map
private Map<String, String> removeUnusedMapEntries(Vertex instanceVertex, String propertyName,
Map<String, String> currentMap,
Map<String, Object> newMap, IDataType elementType,
AttributeInfo attributeInfo)
throws AtlasException {
boolean reference = (elementType.getTypeCategory() == DataTypes.TypeCategory.STRUCT
|| elementType.getTypeCategory() == DataTypes.TypeCategory.CLASS);
Map<String, String> additionalMap = new HashMap<>();
for (String currentKey : currentMap.keySet()) {
boolean shouldDeleteKey = !newMap.containsKey(currentKey);
if (reference) {
String currentEdge = currentMap.get(currentKey);
//Delete the edge reference if its not part of new edges created/updated
if (!newMap.values().contains(currentEdge)) {
String edgeLabel = GraphHelper.getQualifiedNameForMapKey(propertyName, currentKey);
Edge edge = graphHelper.getEdgeByEdgeId(instanceVertex, edgeLabel, currentMap.get(currentKey));
boolean deleted =
deleteHandler.deleteEdgeReference(edge, elementType.getTypeCategory(), attributeInfo.isComposite, true);
if (!deleted) {
additionalMap.put(currentKey, currentEdge);
shouldDeleteKey = false;
}
}
}
removeUnusedEntries(currentElements, newElementsCreated, elementType, attributeInfo); if (shouldDeleteKey) {
String propertyNameForKey = GraphHelper.getQualifiedNameForMapKey(propertyName, currentKey);
graphHelper.setProperty(instanceVertex, propertyNameForKey, null);
}
}
return additionalMap;
} }
/******************************************** ARRAY & MAP **************************************************/ /******************************************** ARRAY & MAP **************************************************/
private String addOrUpdateCollectionEntry(Vertex instanceVertex, AttributeInfo attributeInfo, private Object addOrUpdateCollectionEntry(Vertex instanceVertex, AttributeInfo attributeInfo,
IDataType elementType, Object newAttributeValue, String currentValue, IDataType elementType, Object newAttributeValue, String currentValue,
String propertyName, Operation operation) String propertyName, Operation operation)
throws AtlasException { throws AtlasException {
...@@ -460,7 +481,7 @@ public final class TypedInstanceToGraphMapper { ...@@ -460,7 +481,7 @@ public final class TypedInstanceToGraphMapper {
switch (elementType.getTypeCategory()) { switch (elementType.getTypeCategory()) {
case PRIMITIVE: case PRIMITIVE:
case ENUM: case ENUM:
return newAttributeValue != null ? newAttributeValue.toString() : null; return newAttributeValue != null ? newAttributeValue : null;
case ARRAY: case ARRAY:
case MAP: case MAP:
...@@ -471,7 +492,7 @@ public final class TypedInstanceToGraphMapper { ...@@ -471,7 +492,7 @@ public final class TypedInstanceToGraphMapper {
case STRUCT: case STRUCT:
case CLASS: case CLASS:
final String edgeLabel = GraphHelper.EDGE_LABEL_PREFIX + propertyName; final String edgeLabel = GraphHelper.EDGE_LABEL_PREFIX + propertyName;
Edge currentEdge = graphHelper.getEdgeById(currentValue); Edge currentEdge = graphHelper.getEdgeByEdgeId(instanceVertex, edgeLabel, currentValue);
return addOrUpdateReference(instanceVertex, attributeInfo, elementType, newAttributeValue, currentEdge, return addOrUpdateReference(instanceVertex, attributeInfo, elementType, newAttributeValue, currentEdge,
edgeLabel, operation); edgeLabel, operation);
...@@ -526,7 +547,7 @@ public final class TypedInstanceToGraphMapper { ...@@ -526,7 +547,7 @@ public final class TypedInstanceToGraphMapper {
mapInstanceToVertex(structInstance, structInstanceVertex, structInstance.fieldMapping().fields, false, mapInstanceToVertex(structInstance, structInstanceVertex, structInstance.fieldMapping().fields, false,
Operation.CREATE); Operation.CREATE);
// add an edge to the newly created vertex from the parent // add an edge to the newly created vertex from the parent
Edge newEdge = graphHelper.addEdge(instanceVertex, structInstanceVertex, edgeLabel); Edge newEdge = graphHelper.getOrCreateEdge(instanceVertex, structInstanceVertex, edgeLabel);
return newEdge; return newEdge;
} }
...@@ -575,7 +596,7 @@ public final class TypedInstanceToGraphMapper { ...@@ -575,7 +596,7 @@ public final class TypedInstanceToGraphMapper {
private Edge addClassEdge(Vertex instanceVertex, Vertex toVertex, String edgeLabel) throws AtlasException { private Edge addClassEdge(Vertex instanceVertex, Vertex toVertex, String edgeLabel) throws AtlasException {
// add an edge to the class vertex from the instance // add an edge to the class vertex from the instance
return graphHelper.addEdge(instanceVertex, toVertex, edgeLabel); return graphHelper.getOrCreateEdge(instanceVertex, toVertex, edgeLabel);
} }
private Vertex getClassVertex(ITypedReferenceableInstance typedReference) throws EntityNotFoundException { private Vertex getClassVertex(ITypedReferenceableInstance typedReference) throws EntityNotFoundException {
...@@ -644,7 +665,7 @@ public final class TypedInstanceToGraphMapper { ...@@ -644,7 +665,7 @@ public final class TypedInstanceToGraphMapper {
// add an edge to the newly created vertex from the parent // add an edge to the newly created vertex from the parent
String relationshipLabel = GraphHelper.getTraitLabel(entityType.getName(), traitName); String relationshipLabel = GraphHelper.getTraitLabel(entityType.getName(), traitName);
graphHelper.addEdge(parentInstanceVertex, traitInstanceVertex, relationshipLabel); graphHelper.getOrCreateEdge(parentInstanceVertex, traitInstanceVertex, relationshipLabel);
} }
/******************************************** PRIMITIVES **************************************************/ /******************************************** PRIMITIVES **************************************************/
......
...@@ -69,6 +69,8 @@ public class GraphBackedTypeStore implements ITypeStore { ...@@ -69,6 +69,8 @@ public class GraphBackedTypeStore implements ITypeStore {
private final TitanGraph titanGraph; private final TitanGraph titanGraph;
private GraphHelper graphHelper = GraphHelper.getInstance();
@Inject @Inject
public GraphBackedTypeStore(GraphProvider<TitanGraph> graphProvider) { public GraphBackedTypeStore(GraphProvider<TitanGraph> graphProvider) {
titanGraph = graphProvider.get(); titanGraph = graphProvider.get();
...@@ -155,7 +157,7 @@ public class GraphBackedTypeStore implements ITypeStore { ...@@ -155,7 +157,7 @@ public class GraphBackedTypeStore implements ITypeStore {
for (String superTypeName : superTypes) { for (String superTypeName : superTypes) {
HierarchicalType superType = typeSystem.getDataType(HierarchicalType.class, superTypeName); HierarchicalType superType = typeSystem.getDataType(HierarchicalType.class, superTypeName);
Vertex superVertex = createVertex(superType.getTypeCategory(), superTypeName, superType.getDescription()); Vertex superVertex = createVertex(superType.getTypeCategory(), superTypeName, superType.getDescription());
addEdge(vertex, superVertex, SUPERTYPE_EDGE_LABEL); graphHelper.getOrCreateEdge(vertex, superVertex, SUPERTYPE_EDGE_LABEL);
} }
} }
} }
...@@ -200,26 +202,11 @@ public class GraphBackedTypeStore implements ITypeStore { ...@@ -200,26 +202,11 @@ public class GraphBackedTypeStore implements ITypeStore {
if (!coreTypes.contains(attrType.getName())) { if (!coreTypes.contains(attrType.getName())) {
Vertex attrVertex = createVertex(attrType.getTypeCategory(), attrType.getName(), attrType.getDescription()); Vertex attrVertex = createVertex(attrType.getTypeCategory(), attrType.getName(), attrType.getDescription());
String label = getEdgeLabel(vertexTypeName, attribute.name); String label = getEdgeLabel(vertexTypeName, attribute.name);
addEdge(vertex, attrVertex, label); graphHelper.getOrCreateEdge(vertex, attrVertex, label);
} }
} }
} }
private void addEdge(Vertex fromVertex, Vertex toVertex, String label) {
Iterator<Edge> edges = GraphHelper.getOutGoingEdgesByLabel(fromVertex, label);
// ATLAS-474: Check if this type system edge already exists, to avoid duplicates.
while (edges.hasNext()) {
Edge edge = edges.next();
if (edge.getVertex(Direction.IN).equals(toVertex)) {
LOG.debug("Edge from {} to {} with label {} already exists",
toString(fromVertex), toString(toVertex), label);
return;
}
}
LOG.debug("Adding edge from {} to {} with label {}", toString(fromVertex), toString(toVertex), label);
titanGraph.addEdge(null, fromVertex, toVertex, label);
}
@Override @Override
@GraphTransaction @GraphTransaction
public TypesDef restore() throws AtlasException { public TypesDef restore() throws AtlasException {
......
...@@ -26,6 +26,7 @@ import org.apache.atlas.ApplicationProperties; ...@@ -26,6 +26,7 @@ import org.apache.atlas.ApplicationProperties;
import org.apache.atlas.AtlasClient; import org.apache.atlas.AtlasClient;
import org.apache.atlas.AtlasException; import org.apache.atlas.AtlasException;
import org.apache.atlas.EntityAuditEvent; import org.apache.atlas.EntityAuditEvent;
import org.apache.atlas.RequestContext;
import org.apache.atlas.classification.InterfaceAudience; import org.apache.atlas.classification.InterfaceAudience;
import org.apache.atlas.ha.HAConfiguration; import org.apache.atlas.ha.HAConfiguration;
import org.apache.atlas.listener.ActiveStateChangeHandler; import org.apache.atlas.listener.ActiveStateChangeHandler;
...@@ -58,8 +59,6 @@ import org.apache.atlas.typesystem.types.Multiplicity; ...@@ -58,8 +59,6 @@ import org.apache.atlas.typesystem.types.Multiplicity;
import org.apache.atlas.typesystem.types.StructTypeDefinition; import org.apache.atlas.typesystem.types.StructTypeDefinition;
import org.apache.atlas.typesystem.types.TraitType; import org.apache.atlas.typesystem.types.TraitType;
import org.apache.atlas.typesystem.types.TypeSystem; import org.apache.atlas.typesystem.types.TypeSystem;
import org.apache.atlas.typesystem.types.TypeUtils;
import org.apache.atlas.typesystem.types.TypeUtils.Pair;
import org.apache.atlas.typesystem.types.ValueConversionException; import org.apache.atlas.typesystem.types.ValueConversionException;
import org.apache.atlas.typesystem.types.utils.TypesUtil; import org.apache.atlas.typesystem.types.utils.TypesUtil;
import org.apache.atlas.utils.ParamChecker; import org.apache.atlas.utils.ParamChecker;
...@@ -305,16 +304,15 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang ...@@ -305,16 +304,15 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang
* Creates an entity, instance of the type. * Creates an entity, instance of the type.
* *
* @param entityInstanceDefinition json array of entity definitions * @param entityInstanceDefinition json array of entity definitions
* @return guids - json array of guids * @return guids - list of guids
*/ */
@Override @Override
public String createEntities(String entityInstanceDefinition) throws AtlasException { public List<String> createEntities(String entityInstanceDefinition) throws AtlasException {
ParamChecker.notEmpty(entityInstanceDefinition, "Entity instance definition"); ParamChecker.notEmpty(entityInstanceDefinition, "Entity instance definition");
ITypedReferenceableInstance[] typedInstances = deserializeClassInstances(entityInstanceDefinition); ITypedReferenceableInstance[] typedInstances = deserializeClassInstances(entityInstanceDefinition);
List<String> guids = createEntities(typedInstances); return createEntities(typedInstances);
return new JSONArray(guids).toString();
} }
public List<String> createEntities(ITypedReferenceableInstance[] typedInstances) throws AtlasException { public List<String> createEntities(ITypedReferenceableInstance[] typedInstances) throws AtlasException {
...@@ -422,25 +420,26 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang ...@@ -422,25 +420,26 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang
* @return guids - json array of guids * @return guids - json array of guids
*/ */
@Override @Override
public String updateEntities(String entityInstanceDefinition) throws AtlasException { public AtlasClient.EntityResult updateEntities(String entityInstanceDefinition) throws AtlasException {
ParamChecker.notEmpty(entityInstanceDefinition, "Entity instance definition"); ParamChecker.notEmpty(entityInstanceDefinition, "Entity instance definition");
ITypedReferenceableInstance[] typedInstances = deserializeClassInstances(entityInstanceDefinition); ITypedReferenceableInstance[] typedInstances = deserializeClassInstances(entityInstanceDefinition);
TypeUtils.Pair<List<String>, List<String>> guids = repository.updateEntities(typedInstances); AtlasClient.EntityResult entityResult = repository.updateEntities(typedInstances);
return onEntitiesAddedUpdated(guids); onEntitiesAddedUpdated(entityResult);
return entityResult;
} }
private String onEntitiesAddedUpdated(TypeUtils.Pair<List<String>, List<String>> guids) throws AtlasException { private void onEntitiesAddedUpdated(AtlasClient.EntityResult entityResult) throws AtlasException {
onEntitiesAdded(guids.left); onEntitiesAdded(entityResult.getCreatedEntities());
onEntitiesUpdated(guids.right); onEntitiesUpdated(entityResult.getUpdateEntities());
//Note: doesn't access deletedEntities from entityResult
guids.left.addAll(guids.right); onEntitiesDeleted(RequestContext.get().getDeletedEntities());
return new JSONArray(guids.left).toString();
} }
@Override @Override
public String updateEntityAttributeByGuid(final String guid, String attributeName, String value) throws AtlasException { public AtlasClient.EntityResult updateEntityAttributeByGuid(final String guid, String attributeName,
String value) throws AtlasException {
ParamChecker.notEmpty(guid, "entity id"); ParamChecker.notEmpty(guid, "entity id");
ParamChecker.notEmpty(attributeName, "attribute name"); ParamChecker.notEmpty(attributeName, "attribute name");
ParamChecker.notEmpty(value, "attribute value"); ParamChecker.notEmpty(value, "attribute value");
...@@ -469,8 +468,9 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang ...@@ -469,8 +468,9 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang
} }
((ReferenceableInstance)newInstance).replaceWithNewId(new Id(guid, 0, newInstance.getTypeName())); ((ReferenceableInstance)newInstance).replaceWithNewId(new Id(guid, 0, newInstance.getTypeName()));
TypeUtils.Pair<List<String>, List<String>> guids = repository.updatePartial(newInstance); AtlasClient.EntityResult entityResult = repository.updatePartial(newInstance);
return onEntitiesAddedUpdated(guids); onEntitiesAddedUpdated(entityResult);
return entityResult;
} }
private ITypedReferenceableInstance validateEntityExists(String guid) private ITypedReferenceableInstance validateEntityExists(String guid)
...@@ -483,7 +483,8 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang ...@@ -483,7 +483,8 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang
} }
@Override @Override
public String updateEntityPartialByGuid(final String guid, Referenceable newEntity) throws AtlasException { public AtlasClient.EntityResult updateEntityPartialByGuid(final String guid, Referenceable newEntity)
throws AtlasException {
ParamChecker.notEmpty(guid, "guid cannot be null"); ParamChecker.notEmpty(guid, "guid cannot be null");
ParamChecker.notNull(newEntity, "updatedEntity cannot be null"); ParamChecker.notNull(newEntity, "updatedEntity cannot be null");
ITypedReferenceableInstance existInstance = validateEntityExists(guid); ITypedReferenceableInstance existInstance = validateEntityExists(guid);
...@@ -491,11 +492,13 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang ...@@ -491,11 +492,13 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang
ITypedReferenceableInstance newInstance = convertToTypedInstance(newEntity, existInstance.getTypeName()); ITypedReferenceableInstance newInstance = convertToTypedInstance(newEntity, existInstance.getTypeName());
((ReferenceableInstance)newInstance).replaceWithNewId(new Id(guid, 0, newInstance.getTypeName())); ((ReferenceableInstance)newInstance).replaceWithNewId(new Id(guid, 0, newInstance.getTypeName()));
TypeUtils.Pair<List<String>, List<String>> guids = repository.updatePartial(newInstance); AtlasClient.EntityResult entityResult = repository.updatePartial(newInstance);
return onEntitiesAddedUpdated(guids); onEntitiesAddedUpdated(entityResult);
return entityResult;
} }
private ITypedReferenceableInstance convertToTypedInstance(Referenceable updatedEntity, String typeName) throws AtlasException { private ITypedReferenceableInstance convertToTypedInstance(Referenceable updatedEntity, String typeName)
throws AtlasException {
ClassType type = typeSystem.getDataType(ClassType.class, typeName); ClassType type = typeSystem.getDataType(ClassType.class, typeName);
ITypedReferenceableInstance newInstance = type.createInstance(); ITypedReferenceableInstance newInstance = type.createInstance();
...@@ -538,8 +541,9 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang ...@@ -538,8 +541,9 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang
} }
@Override @Override
public String updateEntityByUniqueAttribute(String typeName, String uniqueAttributeName, String attrValue, public AtlasClient.EntityResult updateEntityByUniqueAttribute(String typeName, String uniqueAttributeName,
Referenceable updatedEntity) throws AtlasException { String attrValue,
Referenceable updatedEntity) throws AtlasException {
ParamChecker.notEmpty(typeName, "typeName"); ParamChecker.notEmpty(typeName, "typeName");
ParamChecker.notEmpty(uniqueAttributeName, "uniqueAttributeName"); ParamChecker.notEmpty(uniqueAttributeName, "uniqueAttributeName");
ParamChecker.notNull(attrValue, "unique attribute value"); ParamChecker.notNull(attrValue, "unique attribute value");
...@@ -550,8 +554,9 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang ...@@ -550,8 +554,9 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang
final ITypedReferenceableInstance newInstance = convertToTypedInstance(updatedEntity, typeName); final ITypedReferenceableInstance newInstance = convertToTypedInstance(updatedEntity, typeName);
((ReferenceableInstance)newInstance).replaceWithNewId(oldInstance.getId()); ((ReferenceableInstance)newInstance).replaceWithNewId(oldInstance.getId());
TypeUtils.Pair<List<String>, List<String>> guids = repository.updatePartial(newInstance); AtlasClient.EntityResult entityResult = repository.updatePartial(newInstance);
return onEntitiesAddedUpdated(guids); onEntitiesAddedUpdated(entityResult);
return entityResult;
} }
private void validateTypeExists(String entityType) throws AtlasException { private void validateTypeExists(String entityType) throws AtlasException {
...@@ -726,13 +731,14 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang ...@@ -726,13 +731,14 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang
* @see org.apache.atlas.services.MetadataService#deleteEntities(java.lang.String) * @see org.apache.atlas.services.MetadataService#deleteEntities(java.lang.String)
*/ */
@Override @Override
public List<String> deleteEntities(List<String> deleteCandidateGuids) throws AtlasException { public AtlasClient.EntityResult deleteEntities(List<String> deleteCandidateGuids) throws AtlasException {
ParamChecker.notEmpty(deleteCandidateGuids, "delete candidate guids"); ParamChecker.notEmpty(deleteCandidateGuids, "delete candidate guids");
return deleteGuids(deleteCandidateGuids); return deleteGuids(deleteCandidateGuids);
} }
@Override @Override
public List<String> deleteEntityByUniqueAttribute(String typeName, String uniqueAttributeName, String attrValue) throws AtlasException { public AtlasClient.EntityResult deleteEntityByUniqueAttribute(String typeName, String uniqueAttributeName,
String attrValue) throws AtlasException {
ParamChecker.notEmpty(typeName, "delete candidate typeName"); ParamChecker.notEmpty(typeName, "delete candidate typeName");
ParamChecker.notEmpty(uniqueAttributeName, "delete candidate unique attribute name"); ParamChecker.notEmpty(uniqueAttributeName, "delete candidate unique attribute name");
ParamChecker.notEmpty(attrValue, "delete candidate unique attribute value"); ParamChecker.notEmpty(attrValue, "delete candidate unique attribute value");
...@@ -745,12 +751,10 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang ...@@ -745,12 +751,10 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang
return deleteGuids(deleteCandidateGuids); return deleteGuids(deleteCandidateGuids);
} }
private List<String> deleteGuids(List<String> deleteCandidateGuids) throws AtlasException { private AtlasClient.EntityResult deleteGuids(List<String> deleteCandidateGuids) throws AtlasException {
Pair<List<String>, List<ITypedReferenceableInstance>> deleteEntitiesResult = repository.deleteEntities(deleteCandidateGuids); AtlasClient.EntityResult entityResult = repository.deleteEntities(deleteCandidateGuids);
if (deleteEntitiesResult.right.size() > 0) { onEntitiesAddedUpdated(entityResult);
onEntitiesDeleted(deleteEntitiesResult.right); return entityResult;
}
return deleteEntitiesResult.left;
} }
private void onEntitiesDeleted(List<ITypedReferenceableInstance> entities) throws AtlasException { private void onEntitiesDeleted(List<ITypedReferenceableInstance> entities) throws AtlasException {
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
package org.apache.atlas.repository.audit; package org.apache.atlas.repository.audit;
import org.apache.atlas.EntityAuditEvent; import org.apache.atlas.EntityAuditEvent;
import org.apache.atlas.typesystem.Referenceable;
import org.apache.commons.lang.RandomStringUtils; import org.apache.commons.lang.RandomStringUtils;
import org.testng.annotations.Test; import org.testng.annotations.Test;
...@@ -38,7 +39,7 @@ public class AuditRepositoryTestBase { ...@@ -38,7 +39,7 @@ public class AuditRepositoryTestBase {
@Test @Test
public void testAddEvents() throws Exception { public void testAddEvents() throws Exception {
EntityAuditEvent event = new EntityAuditEvent(rand(), System.currentTimeMillis(), "u1", EntityAuditEvent event = new EntityAuditEvent(rand(), System.currentTimeMillis(), "u1",
EntityAuditEvent.EntityAuditAction.ENTITY_CREATE, "d1"); EntityAuditEvent.EntityAuditAction.ENTITY_CREATE, "d1", new Referenceable(rand()));
eventRepository.putEvents(event); eventRepository.putEvents(event);
...@@ -54,17 +55,18 @@ public class AuditRepositoryTestBase { ...@@ -54,17 +55,18 @@ public class AuditRepositoryTestBase {
String id2 = "id2" + rand(); String id2 = "id2" + rand();
String id3 = "id3" + rand(); String id3 = "id3" + rand();
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
Referenceable entity = new Referenceable(rand());
List<EntityAuditEvent> expectedEvents = new ArrayList<>(3); List<EntityAuditEvent> expectedEvents = new ArrayList<>(3);
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
//Add events for both ids //Add events for both ids
EntityAuditEvent event = new EntityAuditEvent(id2, ts - i, "user" + i, EntityAuditEvent event = new EntityAuditEvent(id2, ts - i, "user" + i,
EntityAuditEvent.EntityAuditAction.ENTITY_UPDATE, "details" + i); EntityAuditEvent.EntityAuditAction.ENTITY_UPDATE, "details" + i, entity);
eventRepository.putEvents(event); eventRepository.putEvents(event);
expectedEvents.add(event); expectedEvents.add(event);
eventRepository.putEvents(new EntityAuditEvent(id1, ts - i, "user" + i, eventRepository.putEvents(new EntityAuditEvent(id1, ts - i, "user" + i,
EntityAuditEvent.EntityAuditAction.TAG_DELETE, "details" + i)); EntityAuditEvent.EntityAuditAction.TAG_DELETE, "details" + i, entity));
eventRepository.putEvents(new EntityAuditEvent(id3, ts - i, "user" + i, eventRepository.putEvents(new EntityAuditEvent(id3, ts - i, "user" + i,
EntityAuditEvent.EntityAuditAction.TAG_ADD, "details" + i)); EntityAuditEvent.EntityAuditAction.TAG_ADD, "details" + i, entity));
} }
//Use ts for which there is no event - ts + 2 //Use ts for which there is no event - ts + 2
......
...@@ -23,7 +23,6 @@ import com.google.common.collect.ImmutableSet; ...@@ -23,7 +23,6 @@ import com.google.common.collect.ImmutableSet;
import com.thinkaurelius.titan.core.TitanGraph; import com.thinkaurelius.titan.core.TitanGraph;
import com.thinkaurelius.titan.core.util.TitanCleanup; import com.thinkaurelius.titan.core.util.TitanCleanup;
import com.tinkerpop.blueprints.Vertex; import com.tinkerpop.blueprints.Vertex;
import org.apache.atlas.AtlasClient; import org.apache.atlas.AtlasClient;
import org.apache.atlas.AtlasException; import org.apache.atlas.AtlasException;
import org.apache.atlas.RepositoryMetadataModule; import org.apache.atlas.RepositoryMetadataModule;
...@@ -50,7 +49,6 @@ import org.apache.atlas.typesystem.types.Multiplicity; ...@@ -50,7 +49,6 @@ import org.apache.atlas.typesystem.types.Multiplicity;
import org.apache.atlas.typesystem.types.StructTypeDefinition; import org.apache.atlas.typesystem.types.StructTypeDefinition;
import org.apache.atlas.typesystem.types.TraitType; import org.apache.atlas.typesystem.types.TraitType;
import org.apache.atlas.typesystem.types.TypeSystem; import org.apache.atlas.typesystem.types.TypeSystem;
import org.apache.atlas.typesystem.types.TypeUtils;
import org.apache.atlas.typesystem.types.utils.TypesUtil; import org.apache.atlas.typesystem.types.utils.TypesUtil;
import org.testng.Assert; import org.testng.Assert;
import org.testng.annotations.AfterClass; import org.testng.annotations.AfterClass;
...@@ -60,7 +58,6 @@ import org.testng.annotations.Guice; ...@@ -60,7 +58,6 @@ import org.testng.annotations.Guice;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
...@@ -71,6 +68,7 @@ import java.util.Map; ...@@ -71,6 +68,7 @@ import java.util.Map;
import static org.apache.atlas.TestUtils.COLUMNS_ATTR_NAME; import static org.apache.atlas.TestUtils.COLUMNS_ATTR_NAME;
import static org.apache.atlas.TestUtils.COLUMN_TYPE; import static org.apache.atlas.TestUtils.COLUMN_TYPE;
import static org.apache.atlas.TestUtils.NAME; import static org.apache.atlas.TestUtils.NAME;
import static org.apache.atlas.TestUtils.PII;
import static org.apache.atlas.TestUtils.PROCESS_TYPE; import static org.apache.atlas.TestUtils.PROCESS_TYPE;
import static org.apache.atlas.TestUtils.TABLE_TYPE; import static org.apache.atlas.TestUtils.TABLE_TYPE;
import static org.apache.atlas.TestUtils.createColumnEntity; import static org.apache.atlas.TestUtils.createColumnEntity;
...@@ -78,8 +76,6 @@ import static org.apache.atlas.TestUtils.createDBEntity; ...@@ -78,8 +76,6 @@ import static org.apache.atlas.TestUtils.createDBEntity;
import static org.apache.atlas.TestUtils.createTableEntity; import static org.apache.atlas.TestUtils.createTableEntity;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotEquals; import static org.testng.Assert.assertNotEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue; import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail; import static org.testng.Assert.fail;
...@@ -145,7 +141,7 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase { ...@@ -145,7 +141,7 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase {
assertEquals(instance.getId()._getId(), id); assertEquals(instance.getId()._getId(), id);
//delete entity should mark it as deleted //delete entity should mark it as deleted
List<String> results = deleteEntities(id); List<String> results = deleteEntities(id).getDeletedEntities();
assertEquals(results.get(0), id); assertEquals(results.get(0), id);
assertEntityDeleted(id); assertEntityDeleted(id);
...@@ -167,6 +163,26 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase { ...@@ -167,6 +163,26 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase {
} }
@Test @Test
public void testDeleteEntityWithTraits() throws Exception {
Referenceable entity = createDBEntity();
String id = createInstance(entity);
TraitType dataType = typeSystem.getDataType(TraitType.class, PII);
ITypedStruct trait = dataType.convert(new Struct(TestUtils.PII), Multiplicity.REQUIRED);
repositoryService.addTrait(id, trait);
ITypedReferenceableInstance instance = repositoryService.getEntityDefinition(id);
assertTrue(instance.getTraits().contains(PII));
deleteEntities(id);
assertEntityDeleted(id);
assertTestDeleteEntityWithTraits(id);
}
protected abstract void assertTestDeleteEntityWithTraits(String guid)
throws EntityNotFoundException, RepositoryException, Exception;
@Test
public void testDeleteReference() throws Exception { public void testDeleteReference() throws Exception {
//Deleting column should update table //Deleting column should update table
Referenceable db = createDBEntity(); Referenceable db = createDBEntity();
...@@ -179,13 +195,16 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase { ...@@ -179,13 +195,16 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase {
table.set(COLUMNS_ATTR_NAME, Arrays.asList(new Id(colId, 0, COLUMN_TYPE))); table.set(COLUMNS_ATTR_NAME, Arrays.asList(new Id(colId, 0, COLUMN_TYPE)));
String tableId = createInstance(table); String tableId = createInstance(table);
deleteEntities(colId); AtlasClient.EntityResult entityResult = deleteEntities(colId);
assertEquals(entityResult.getDeletedEntities().size(), 1);
assertEquals(entityResult.getDeletedEntities().get(0), colId);
assertEquals(entityResult.getUpdateEntities().size(), 1);
assertEquals(entityResult.getUpdateEntities().get(0), tableId);
assertEntityDeleted(colId); assertEntityDeleted(colId);
ITypedReferenceableInstance tableInstance = repositoryService.getEntityDefinition(tableId); ITypedReferenceableInstance tableInstance = repositoryService.getEntityDefinition(tableId);
List<ITypedReferenceableInstance> columns = assertColumnForTestDeleteReference(tableInstance);
(List<ITypedReferenceableInstance>) tableInstance.get(COLUMNS_ATTR_NAME);
assertNull(columns);
//Deleting table should update process //Deleting table should update process
Referenceable process = new Referenceable(PROCESS_TYPE); Referenceable process = new Referenceable(PROCESS_TYPE);
...@@ -195,18 +214,23 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase { ...@@ -195,18 +214,23 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase {
deleteEntities(tableId); deleteEntities(tableId);
assertEntityDeleted(tableId); assertEntityDeleted(tableId);
assertTestDeleteReference(processInstance);
assertTableForTestDeleteReference(tableId);
assertProcessForTestDeleteReference(processInstance);
} }
protected abstract void assertTestDeleteReference(ITypedReferenceableInstance processInstance) throws Exception; protected abstract void assertTableForTestDeleteReference(String tableId) throws Exception;
protected abstract void assertColumnForTestDeleteReference(ITypedReferenceableInstance tableInstance)
throws AtlasException;
protected abstract void assertProcessForTestDeleteReference(ITypedReferenceableInstance processInstance) throws Exception;
protected abstract void assertEntityDeleted(String id) throws Exception; protected abstract void assertEntityDeleted(String id) throws Exception;
private List<String> deleteEntities(String... id) throws Exception { private AtlasClient.EntityResult deleteEntities(String... id) throws Exception {
RequestContext.createContext(); RequestContext.createContext();
List<String> response = repositoryService.deleteEntities(Arrays.asList(id)).left; return repositoryService.deleteEntities(Arrays.asList(id));
assertNotNull(response);
return response;
} }
private String createInstance(Referenceable entity) throws Exception { private String createInstance(Referenceable entity) throws Exception {
...@@ -228,21 +252,41 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase { ...@@ -228,21 +252,41 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase {
table1Entity.set(COLUMNS_ATTR_NAME, ImmutableList.of(col1, col2, col3)); table1Entity.set(COLUMNS_ATTR_NAME, ImmutableList.of(col1, col2, col3));
createInstance(table1Entity); createInstance(table1Entity);
// Retrieve the table entities from the auditRepository, // Retrieve the table entities from the Repository, to get their guids and the composite column guids.
// to get their guids and the composite column guids.
ITypedReferenceableInstance tableInstance = repositoryService.getEntityDefinition(TestUtils.TABLE_TYPE, ITypedReferenceableInstance tableInstance = repositoryService.getEntityDefinition(TestUtils.TABLE_TYPE,
NAME, table1Entity.get(NAME)); NAME, table1Entity.get(NAME));
List<IReferenceableInstance> table1Columns = (List<IReferenceableInstance>) tableInstance.get(COLUMNS_ATTR_NAME); List<IReferenceableInstance> columns = (List<IReferenceableInstance>) tableInstance.get(COLUMNS_ATTR_NAME);
//Delete column
String colId = columns.get(0).getId()._getId();
String tableId = tableInstance.getId()._getId();
AtlasClient.EntityResult entityResult = deleteEntities(colId);
assertEquals(entityResult.getDeletedEntities().size(), 1);
assertEquals(entityResult.getDeletedEntities().get(0), colId);
assertEquals(entityResult.getUpdateEntities().size(), 1);
assertEquals(entityResult.getUpdateEntities().get(0), tableId);
assertEntityDeleted(colId);
tableInstance = repositoryService.getEntityDefinition(TestUtils.TABLE_TYPE, NAME, table1Entity.get(NAME));
assertDeletedColumn(tableInstance);
// Delete the table entities. The deletion should cascade //update by removing a column
// to their composite columns. tableInstance.set(COLUMNS_ATTR_NAME, ImmutableList.of(col3));
List<String> deletedGuids = deleteEntities(tableInstance.getId()._getId()); entityResult = updatePartial(tableInstance);
colId = columns.get(1).getId()._getId();
assertEquals(entityResult.getDeletedEntities().size(), 1);
assertEquals(entityResult.getDeletedEntities().get(0), colId);
assertEntityDeleted(colId);
// Delete the table entities. The deletion should cascade to their composite columns.
tableInstance = repositoryService.getEntityDefinition(TestUtils.TABLE_TYPE, NAME, table1Entity.get(NAME));
List<String> deletedGuids = deleteEntities(tableInstance.getId()._getId()).getDeletedEntities();
assertEquals(deletedGuids.size(), 2);
// Verify that deleteEntities() response has guids for tables and their composite columns. // Verify that deleteEntities() response has guids for tables and their composite columns.
Assert.assertTrue(deletedGuids.contains(tableInstance.getId()._getId())); Assert.assertTrue(deletedGuids.contains(tableInstance.getId()._getId()));
for (IReferenceableInstance column : table1Columns) { Assert.assertTrue(deletedGuids.contains(columns.get(2).getId()._getId()));
Assert.assertTrue(deletedGuids.contains(column.getId()._getId()));
}
// Verify that tables and their composite columns have been deleted from the graph Repository. // Verify that tables and their composite columns have been deleted from the graph Repository.
for (String guid : deletedGuids) { for (String guid : deletedGuids) {
...@@ -251,6 +295,8 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase { ...@@ -251,6 +295,8 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase {
assertTestDeleteEntities(tableInstance); assertTestDeleteEntities(tableInstance);
} }
protected abstract void assertDeletedColumn(ITypedReferenceableInstance tableInstance) throws AtlasException;
protected abstract void assertTestDeleteEntities(ITypedReferenceableInstance tableInstance) throws Exception; protected abstract void assertTestDeleteEntities(ITypedReferenceableInstance tableInstance) throws Exception;
/** /**
...@@ -276,12 +322,13 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase { ...@@ -276,12 +322,13 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase {
vertexCount = getVertices(Constants.ENTITY_TYPE_PROPERTY_KEY, "SecurityClearance").size(); vertexCount = getVertices(Constants.ENTITY_TYPE_PROPERTY_KEY, "SecurityClearance").size();
Assert.assertEquals(vertexCount, 1); Assert.assertEquals(vertexCount, 1);
List<String> deletedEntities = deleteEntities(hrDeptGuid); List<String> deletedEntities = deleteEntities(hrDeptGuid).getDeletedEntities();
assertTrue(deletedEntities.contains(hrDeptGuid)); assertTrue(deletedEntities.contains(hrDeptGuid));
assertEntityDeleted(hrDeptGuid);
// Verify Department entity and its contained Person entities were deleted. // Verify Department entity and its contained Person entities were deleted.
assertEntityDeleted(hrDeptGuid);
for (String employeeGuid : employeeGuids) { for (String employeeGuid : employeeGuids) {
assertTrue(deletedEntities.contains(employeeGuid));
assertEntityDeleted(employeeGuid); assertEntityDeleted(employeeGuid);
} }
...@@ -341,15 +388,16 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase { ...@@ -341,15 +388,16 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase {
object = mapOwnerVertex.getProperty(atlasEdgeLabel.getQualifiedMapKey()); object = mapOwnerVertex.getProperty(atlasEdgeLabel.getQualifiedMapKey());
Assert.assertNotNull(object); Assert.assertNotNull(object);
List<String> deletedEntities = deleteEntities(mapOwnerGuid); List<String> deletedEntities = deleteEntities(mapOwnerGuid).getDeletedEntities();
Assert.assertEquals(deletedEntities.size(), 2); Assert.assertEquals(deletedEntities.size(), 2);
Assert.assertTrue(deletedEntities.containsAll(guids)); Assert.assertTrue(deletedEntities.contains(mapOwnerGuid));
Assert.assertTrue(deletedEntities.contains(mapValueGuid));
assertEntityDeleted(mapOwnerGuid); assertEntityDeleted(mapOwnerGuid);
assertEntityDeleted(mapValueGuid); assertEntityDeleted(mapValueGuid);
} }
private TypeUtils.Pair<List<String>, List<String>> updatePartial(ITypedReferenceableInstance entity) throws RepositoryException { private AtlasClient.EntityResult updatePartial(ITypedReferenceableInstance entity) throws RepositoryException {
RequestContext.createContext(); RequestContext.createContext();
return repositoryService.updatePartial(entity); return repositoryService.updatePartial(entity);
} }
...@@ -379,7 +427,9 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase { ...@@ -379,7 +427,9 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase {
ClassType personType = typeSystem.getDataType(ClassType.class, "Person"); ClassType personType = typeSystem.getDataType(ClassType.class, "Person");
ITypedReferenceableInstance maxEntity = personType.createInstance(max.getId()); ITypedReferenceableInstance maxEntity = personType.createInstance(max.getId());
maxEntity.set("mentor", johnGuid); maxEntity.set("mentor", johnGuid);
updatePartial(maxEntity); AtlasClient.EntityResult entityResult = updatePartial(maxEntity);
assertEquals(entityResult.getUpdateEntities().size(), 1);
assertTrue(entityResult.getUpdateEntities().contains(maxGuid));
// Verify the update was applied correctly - john should now be max's mentor. // Verify the update was applied correctly - john should now be max's mentor.
max = repositoryService.getEntityDefinition(maxGuid); max = repositoryService.getEntityDefinition(maxGuid);
...@@ -394,7 +444,9 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase { ...@@ -394,7 +444,9 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase {
// Update max's mentor reference to jane. // Update max's mentor reference to jane.
maxEntity.set("mentor", janeGuid); maxEntity.set("mentor", janeGuid);
updatePartial(maxEntity); entityResult = updatePartial(maxEntity);
assertEquals(entityResult.getUpdateEntities().size(), 1);
assertTrue(entityResult.getUpdateEntities().contains(maxGuid));
// Verify the update was applied correctly - jane should now be max's mentor. // Verify the update was applied correctly - jane should now be max's mentor.
max = repositoryService.getEntityDefinition(maxGuid); max = repositoryService.getEntityDefinition(maxGuid);
...@@ -411,7 +463,11 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase { ...@@ -411,7 +463,11 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase {
Id juliusGuid = julius.getId(); Id juliusGuid = julius.getId();
maxEntity = personType.createInstance(max.getId()); maxEntity = personType.createInstance(max.getId());
maxEntity.set("manager", juliusGuid); maxEntity.set("manager", juliusGuid);
updatePartial(maxEntity); entityResult = updatePartial(maxEntity);
//TODO ATLAS-499 should have updated julius' subordinates
assertEquals(entityResult.getUpdateEntities().size(), 2);
assertTrue(entityResult.getUpdateEntities().contains(maxGuid));
assertTrue(entityResult.getUpdateEntities().contains(janeGuid._getId()));
// Verify the update was applied correctly - julius should now be max's manager. // Verify the update was applied correctly - julius should now be max's manager.
max = repositoryService.getEntityDefinition(maxGuid); max = repositoryService.getEntityDefinition(maxGuid);
...@@ -456,41 +512,38 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase { ...@@ -456,41 +512,38 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase {
} }
Assert.assertTrue(subordinateIds.contains(maxGuid)); Assert.assertTrue(subordinateIds.contains(maxGuid));
List<String> deletedEntities = deleteEntities(maxGuid);
Assert.assertTrue(deletedEntities.contains(maxGuid));
assertEntityDeleted(maxGuid);
// Verify that the Department.employees reference to the deleted employee AtlasClient.EntityResult entityResult = deleteEntities(maxGuid);
// was disconnected. ITypedReferenceableInstance john = repositoryService.getEntityDefinition("Manager", "name", "John");
hrDept = repositoryService.getEntityDefinition(hrDeptGuid);
refValue = hrDept.get("employees");
Assert.assertTrue(refValue instanceof List);
List<Object> employees = (List<Object>)refValue;
Assert.assertEquals(employees.size(), 3);
for (Object listValue : employees) {
Assert.assertTrue(listValue instanceof ITypedReferenceableInstance);
ITypedReferenceableInstance employee = (ITypedReferenceableInstance) listValue;
Assert.assertNotEquals(employee.getId()._getId(), maxGuid);
}
// Verify that max's Person.mentor unidirectional reference to john was disconnected. assertEquals(entityResult.getDeletedEntities().size(), 1);
ITypedReferenceableInstance john = repositoryService.getEntityDefinition(johnGuid); assertTrue(entityResult.getDeletedEntities().contains(maxGuid));
refValue = john.get("mentor"); assertEquals(entityResult.getUpdateEntities().size(), 3);
Assert.assertNull(refValue); assertTrue(entityResult.getUpdateEntities().containsAll(Arrays.asList(jane.getId()._getId(), hrDeptGuid,
john.getId()._getId())));
assertEntityDeleted(maxGuid);
assertTestDisconnectBidirectionalReferences(janeGuid); assertMaxForTestDisconnectBidirectionalReferences(nameGuidMap);
// Now delete jane - this should disconnect the manager reference from her // Now delete jane - this should disconnect the manager reference from her
// subordinate. // subordinate.
deletedEntities = deleteEntities(janeGuid); entityResult = deleteEntities(janeGuid);
Assert.assertTrue(deletedEntities.contains(janeGuid)); assertEquals(entityResult.getDeletedEntities().size(), 1);
assertTrue(entityResult.getDeletedEntities().contains(janeGuid));
assertEquals(entityResult.getUpdateEntities().size(), 2);
assertTrue(entityResult.getUpdateEntities().containsAll(Arrays.asList(hrDeptGuid, john.getId()._getId())));
assertEntityDeleted(janeGuid); assertEntityDeleted(janeGuid);
john = repositoryService.getEntityDefinition(johnGuid); john = repositoryService.getEntityDefinition("Person", "name", "John");
Assert.assertNull(john.get("manager")); assertJohnForTestDisconnectBidirectionalReferences(john, janeGuid);
} }
protected abstract void assertTestDisconnectBidirectionalReferences(String janeGuid) throws Exception; protected abstract void assertJohnForTestDisconnectBidirectionalReferences(ITypedReferenceableInstance john,
String janeGuid) throws Exception;
protected abstract void assertMaxForTestDisconnectBidirectionalReferences(Map<String, String> nameGuidMap)
throws Exception;
/** /**
* Verify deleting entity that is the target of a unidirectional class array reference * Verify deleting entity that is the target of a unidirectional class array reference
...@@ -503,30 +556,27 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase { ...@@ -503,30 +556,27 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase {
// Get the guid for one of the table's columns. // Get the guid for one of the table's columns.
ITypedReferenceableInstance table = repositoryService.getEntityDefinition(TestUtils.TABLE_TYPE, "name", TestUtils.TABLE_NAME); ITypedReferenceableInstance table = repositoryService.getEntityDefinition(TestUtils.TABLE_TYPE, "name", TestUtils.TABLE_NAME);
String tableGuid = table.getId()._getId(); String tableGuid = table.getId()._getId();
Object refValues = table.get("columns"); List<ITypedReferenceableInstance> columns = (List<ITypedReferenceableInstance>) table.get("columns");
Assert.assertTrue(refValues instanceof List); Assert.assertEquals(columns.size(), 5);
List<Object> refList = (List<Object>) refValues; String columnGuid = columns.get(0).getId()._getId();
Assert.assertEquals(refList.size(), 5);
Assert.assertTrue(refList.get(0) instanceof ITypedReferenceableInstance);
ITypedReferenceableInstance column = (ITypedReferenceableInstance) refList.get(0);
String columnGuid = column.getId()._getId();
// Delete the column. // Delete the column.
List<String> deletedEntities = deleteEntities(columnGuid); AtlasClient.EntityResult entityResult = deleteEntities(columnGuid);
Assert.assertTrue(deletedEntities.contains(columnGuid)); assertEquals(entityResult.getDeletedEntities().size(), 1);
Assert.assertTrue(entityResult.getDeletedEntities().contains(columnGuid));
assertEquals(entityResult.getUpdateEntities().size(), 1);
Assert.assertTrue(entityResult.getUpdateEntities().contains(tableGuid));
assertEntityDeleted(columnGuid); assertEntityDeleted(columnGuid);
// Verify table.columns reference to the deleted column has been disconnected. // Verify table.columns reference to the deleted column has been disconnected.
table = repositoryService.getEntityDefinition(tableGuid); table = repositoryService.getEntityDefinition(tableGuid);
refList = (List<Object>) table.get("columns"); assertTestDisconnectUnidirectionalArrayReferenceFromClassType(
Assert.assertEquals(refList.size(), 4); (List<ITypedReferenceableInstance>) table.get("columns"), columnGuid);
for (Object refValue : refList) {
Assert.assertTrue(refValue instanceof ITypedReferenceableInstance);
column = (ITypedReferenceableInstance)refValue;
Assert.assertFalse(column.getId()._getId().equals(columnGuid));
}
} }
protected abstract void assertTestDisconnectUnidirectionalArrayReferenceFromClassType(
List<ITypedReferenceableInstance> columns, String columnGuid);
/** /**
* Verify deleting entities that are the target of a unidirectional class array reference * Verify deleting entities that are the target of a unidirectional class array reference
* from a struct or trait instance. * from a struct or trait instance.
...@@ -559,8 +609,8 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase { ...@@ -559,8 +609,8 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase {
Referenceable structTargetEntity = new Referenceable("StructTarget"); Referenceable structTargetEntity = new Referenceable("StructTarget");
Referenceable traitTargetEntity = new Referenceable("TraitTarget"); Referenceable traitTargetEntity = new Referenceable("TraitTarget");
Referenceable structContainerEntity = new Referenceable("StructContainer"); Referenceable structContainerEntity = new Referenceable("StructContainer");
Referenceable structInstance = new Referenceable("TestStruct"); Struct structInstance = new Struct("TestStruct");
Referenceable nestedStructInstance = new Referenceable("NestedStruct"); Struct nestedStructInstance = new Struct("NestedStruct");
Referenceable traitInstance = new Referenceable("TestTrait"); Referenceable traitInstance = new Referenceable("TestTrait");
structContainerEntity.set("struct", structInstance); structContainerEntity.set("struct", structInstance);
structInstance.set("target", ImmutableList.of(structTargetEntity)); structInstance.set("target", ImmutableList.of(structTargetEntity));
...@@ -623,19 +673,19 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase { ...@@ -623,19 +673,19 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase {
Assert.assertEquals(refList.get(0).getId()._getId(), traitTargetGuid); Assert.assertEquals(refList.get(0).getId()._getId(), traitTargetGuid);
// Delete the entities that are targets of the struct and trait instances. // Delete the entities that are targets of the struct and trait instances.
List<String> deletedEntities = deleteEntities(structTargetGuid, traitTargetGuid); AtlasClient.EntityResult entityResult = deleteEntities(structTargetGuid, traitTargetGuid);
Assert.assertEquals(entityResult.getDeletedEntities().size(), 2);
Assert.assertTrue(entityResult.getDeletedEntities().containsAll(Arrays.asList(structTargetGuid, traitTargetGuid)));
assertEntityDeleted(structTargetGuid); assertEntityDeleted(structTargetGuid);
assertEntityDeleted(traitTargetGuid); assertEntityDeleted(traitTargetGuid);
Assert.assertEquals(deletedEntities.size(), 2);
Assert.assertTrue(deletedEntities.containsAll(Arrays.asList(structTargetGuid, traitTargetGuid)));
assertTestDisconnectUnidirectionalArrayReferenceFromStructAndTraitTypes(structContainerGuid); assertTestDisconnectUnidirectionalArrayReferenceFromStructAndTraitTypes(structContainerGuid);
// Delete the entity which contains nested structs and has the TestTrait trait. // Delete the entity which contains nested structs and has the TestTrait trait.
deletedEntities = deleteEntities(structContainerGuid); entityResult = deleteEntities(structContainerGuid);
Assert.assertEquals(entityResult.getDeletedEntities().size(), 1);
Assert.assertTrue(entityResult.getDeletedEntities().contains(structContainerGuid));
assertEntityDeleted(structContainerGuid); assertEntityDeleted(structContainerGuid);
Assert.assertEquals(deletedEntities.size(), 1);
Assert.assertTrue(deletedEntities.contains(structContainerGuid));
// Verify all TestStruct struct vertices were removed. // Verify all TestStruct struct vertices were removed.
assertVerticesDeleted(getVertices(Constants.ENTITY_TYPE_PROPERTY_KEY, "TestStruct")); assertVerticesDeleted(getVertices(Constants.ENTITY_TYPE_PROPERTY_KEY, "TestStruct"));
...@@ -890,13 +940,14 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase { ...@@ -890,13 +940,14 @@ public abstract class GraphBackedMetadataRepositoryDeleteTestBase {
return list; return list;
} }
private Map<String, String> getEmployeeNameGuidMap(ITypedReferenceableInstance hrDept) throws AtlasException { private Map<String, String> getEmployeeNameGuidMap(final ITypedReferenceableInstance hrDept) throws AtlasException {
Object refValue = hrDept.get("employees"); Object refValue = hrDept.get("employees");
Assert.assertTrue(refValue instanceof List); Assert.assertTrue(refValue instanceof List);
List<Object> employees = (List<Object>)refValue; List<Object> employees = (List<Object>)refValue;
Assert.assertEquals(employees.size(), 4); Assert.assertEquals(employees.size(), 4);
Map<String, String> nameGuidMap = new HashMap<String, String>(); Map<String, String> nameGuidMap = new HashMap<String, String>() {{
put("hr", hrDept.getId()._getId());
}};
for (Object listValue : employees) { for (Object listValue : employees) {
Assert.assertTrue(listValue instanceof ITypedReferenceableInstance); Assert.assertTrue(listValue instanceof ITypedReferenceableInstance);
......
...@@ -21,8 +21,10 @@ package org.apache.atlas.repository.graph; ...@@ -21,8 +21,10 @@ package org.apache.atlas.repository.graph;
import com.tinkerpop.blueprints.Vertex; import com.tinkerpop.blueprints.Vertex;
import org.apache.atlas.AtlasClient; import org.apache.atlas.AtlasClient;
import org.apache.atlas.AtlasException;
import org.apache.atlas.TestUtils; import org.apache.atlas.TestUtils;
import org.apache.atlas.repository.Constants; import org.apache.atlas.repository.Constants;
import org.apache.atlas.typesystem.IReferenceableInstance;
import org.apache.atlas.typesystem.IStruct; import org.apache.atlas.typesystem.IStruct;
import org.apache.atlas.typesystem.ITypedReferenceableInstance; import org.apache.atlas.typesystem.ITypedReferenceableInstance;
import org.apache.atlas.typesystem.ITypedStruct; import org.apache.atlas.typesystem.ITypedStruct;
...@@ -32,8 +34,11 @@ import org.apache.atlas.typesystem.types.TypeSystem; ...@@ -32,8 +34,11 @@ import org.apache.atlas.typesystem.types.TypeSystem;
import org.testng.Assert; import org.testng.Assert;
import java.util.List; import java.util.List;
import java.util.Map;
import static org.apache.atlas.TestUtils.COLUMNS_ATTR_NAME;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNull; import static org.testng.Assert.assertNull;
import static org.testng.Assert.fail; import static org.testng.Assert.fail;
import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNotNull;
...@@ -45,7 +50,24 @@ public class GraphBackedRepositoryHardDeleteTest extends GraphBackedMetadataRepo ...@@ -45,7 +50,24 @@ public class GraphBackedRepositoryHardDeleteTest extends GraphBackedMetadataRepo
} }
@Override @Override
protected void assertTestDeleteReference(ITypedReferenceableInstance processInstance) throws Exception { protected void assertTestDeleteEntityWithTraits(String guid) {
//entity is deleted. So, no assertions
}
@Override
protected void assertTableForTestDeleteReference(String tableId) {
//entity is deleted. So, no assertions
}
@Override
protected void assertColumnForTestDeleteReference(ITypedReferenceableInstance tableInstance) throws AtlasException {
List<ITypedReferenceableInstance> columns =
(List<ITypedReferenceableInstance>) tableInstance.get(COLUMNS_ATTR_NAME);
assertNull(columns);
}
@Override
protected void assertProcessForTestDeleteReference(ITypedReferenceableInstance processInstance) throws Exception {
//assert that outputs is empty //assert that outputs is empty
ITypedReferenceableInstance newProcess = ITypedReferenceableInstance newProcess =
repositoryService.getEntityDefinition(processInstance.getId()._getId()); repositoryService.getEntityDefinition(processInstance.getId()._getId());
...@@ -63,6 +85,11 @@ public class GraphBackedRepositoryHardDeleteTest extends GraphBackedMetadataRepo ...@@ -63,6 +85,11 @@ public class GraphBackedRepositoryHardDeleteTest extends GraphBackedMetadataRepo
} }
@Override @Override
protected void assertDeletedColumn(ITypedReferenceableInstance tableInstance) throws AtlasException {
assertEquals(((List<IReferenceableInstance>) tableInstance.get(COLUMNS_ATTR_NAME)).size(), 2);
}
@Override
protected void assertTestDeleteEntities(ITypedReferenceableInstance tableInstance) { protected void assertTestDeleteEntities(ITypedReferenceableInstance tableInstance) {
int vertexCount = getVertices(Constants.ENTITY_TYPE_PROPERTY_KEY, TestUtils.TABLE_TYPE).size(); int vertexCount = getVertices(Constants.ENTITY_TYPE_PROPERTY_KEY, TestUtils.TABLE_TYPE).size();
assertEquals(vertexCount, 0); assertEquals(vertexCount, 0);
...@@ -85,12 +112,42 @@ public class GraphBackedRepositoryHardDeleteTest extends GraphBackedMetadataRepo ...@@ -85,12 +112,42 @@ public class GraphBackedRepositoryHardDeleteTest extends GraphBackedMetadataRepo
} }
@Override @Override
protected void assertTestDisconnectBidirectionalReferences(String janeGuid) throws Exception { protected void assertJohnForTestDisconnectBidirectionalReferences(ITypedReferenceableInstance john,
String janeGuid) throws Exception {
assertNull(john.get("manager"));
}
@Override
protected void assertMaxForTestDisconnectBidirectionalReferences(Map<String, String> nameGuidMap)
throws Exception {
// Verify that the Department.employees reference to the deleted employee
// was disconnected.
ITypedReferenceableInstance hrDept = repositoryService.getEntityDefinition(nameGuidMap.get("hr"));
List<ITypedReferenceableInstance> employees = (List<ITypedReferenceableInstance>) hrDept.get("employees");
Assert.assertEquals(employees.size(), 3);
String maxGuid = nameGuidMap.get("Max");
for (ITypedReferenceableInstance employee : employees) {
Assert.assertNotEquals(employee.getId()._getId(), maxGuid);
}
// Verify that the Manager.subordinates reference to the deleted employee // Verify that the Manager.subordinates reference to the deleted employee
// Max was disconnected. // Max was disconnected.
ITypedReferenceableInstance jane = repositoryService.getEntityDefinition(janeGuid); ITypedReferenceableInstance jane = repositoryService.getEntityDefinition(nameGuidMap.get("Jane"));
List<ITypedReferenceableInstance> subordinates = (List<ITypedReferenceableInstance>) jane.get("subordinates"); List<ITypedReferenceableInstance> subordinates = (List<ITypedReferenceableInstance>) jane.get("subordinates");
assertEquals(subordinates.size(), 1); assertEquals(subordinates.size(), 1);
// Verify that max's Person.mentor unidirectional reference to john was disconnected.
ITypedReferenceableInstance john = repositoryService.getEntityDefinition(nameGuidMap.get("John"));
assertNull(john.get("mentor"));
}
@Override
protected void assertTestDisconnectUnidirectionalArrayReferenceFromClassType(
List<ITypedReferenceableInstance> columns, String columnGuid) {
assertEquals(columns.size(), 4);
for (ITypedReferenceableInstance column : columns) {
assertFalse(column.getId()._getId().equals(columnGuid));
}
} }
@Override @Override
......
...@@ -19,10 +19,11 @@ ...@@ -19,10 +19,11 @@
package org.apache.atlas.repository.graph; package org.apache.atlas.repository.graph;
import com.tinkerpop.blueprints.Vertex; import com.tinkerpop.blueprints.Vertex;
import org.apache.atlas.AtlasClient; import org.apache.atlas.AtlasClient;
import org.apache.atlas.AtlasException;
import org.apache.atlas.TestUtils; import org.apache.atlas.TestUtils;
import org.apache.atlas.repository.Constants; import org.apache.atlas.repository.Constants;
import org.apache.atlas.typesystem.IReferenceableInstance;
import org.apache.atlas.typesystem.IStruct; import org.apache.atlas.typesystem.IStruct;
import org.apache.atlas.typesystem.ITypedReferenceableInstance; import org.apache.atlas.typesystem.ITypedReferenceableInstance;
import org.apache.atlas.typesystem.ITypedStruct; import org.apache.atlas.typesystem.ITypedStruct;
...@@ -33,8 +34,12 @@ import org.testng.Assert; ...@@ -33,8 +34,12 @@ import org.testng.Assert;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static org.apache.atlas.TestUtils.COLUMNS_ATTR_NAME;
import static org.apache.atlas.TestUtils.NAME;
import static org.apache.atlas.TestUtils.PII;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
public class GraphBackedRepositorySoftDeleteTest extends GraphBackedMetadataRepositoryDeleteTestBase { public class GraphBackedRepositorySoftDeleteTest extends GraphBackedMetadataRepositoryDeleteTestBase {
@Override @Override
...@@ -43,7 +48,38 @@ public class GraphBackedRepositorySoftDeleteTest extends GraphBackedMetadataRepo ...@@ -43,7 +48,38 @@ public class GraphBackedRepositorySoftDeleteTest extends GraphBackedMetadataRepo
} }
@Override @Override
protected void assertTestDeleteReference(ITypedReferenceableInstance expected) throws Exception { protected void assertTestDeleteEntityWithTraits(String guid) throws Exception {
ITypedReferenceableInstance instance = repositoryService.getEntityDefinition(guid);
assertTrue(instance.getTraits().contains(PII));
}
@Override
protected void assertTableForTestDeleteReference(String tableId) throws Exception {
ITypedReferenceableInstance table = repositoryService.getEntityDefinition(tableId);
assertNotNull(table.get(NAME));
assertNotNull(table.get("description"));
assertNotNull(table.get("type"));
assertNotNull(table.get("tableType"));
assertNotNull(table.get("created"));
Id dbId = (Id) table.get("database");
assertNotNull(dbId);
ITypedReferenceableInstance db = repositoryService.getEntityDefinition(dbId.getId()._getId());
assertNotNull(db);
assertEquals(db.getId().getState(), Id.EntityState.ACTIVE);
}
@Override
protected void assertColumnForTestDeleteReference(ITypedReferenceableInstance tableInstance) throws AtlasException {
List<ITypedReferenceableInstance> columns =
(List<ITypedReferenceableInstance>) tableInstance.get(COLUMNS_ATTR_NAME);
assertEquals(columns.size(), 1);
assertEquals(columns.get(0).getId().getState(), Id.EntityState.DELETED);
}
@Override
protected void assertProcessForTestDeleteReference(ITypedReferenceableInstance expected) throws Exception {
ITypedReferenceableInstance process = repositoryService.getEntityDefinition(expected.getId()._getId()); ITypedReferenceableInstance process = repositoryService.getEntityDefinition(expected.getId()._getId());
List<ITypedReferenceableInstance> outputs = List<ITypedReferenceableInstance> outputs =
(List<ITypedReferenceableInstance>) process.get(AtlasClient.PROCESS_ATTRIBUTE_OUTPUTS); (List<ITypedReferenceableInstance>) process.get(AtlasClient.PROCESS_ATTRIBUTE_OUTPUTS);
...@@ -59,6 +95,13 @@ public class GraphBackedRepositorySoftDeleteTest extends GraphBackedMetadataRepo ...@@ -59,6 +95,13 @@ public class GraphBackedRepositorySoftDeleteTest extends GraphBackedMetadataRepo
} }
@Override @Override
protected void assertDeletedColumn(ITypedReferenceableInstance tableInstance) throws AtlasException {
List<IReferenceableInstance> columns = (List<IReferenceableInstance>) tableInstance.get(COLUMNS_ATTR_NAME);
assertEquals(columns.size(), 3);
assertEquals(columns.get(0).getId().getState(), Id.EntityState.DELETED);
}
@Override
protected void assertTestDeleteEntities(ITypedReferenceableInstance expected) throws Exception { protected void assertTestDeleteEntities(ITypedReferenceableInstance expected) throws Exception {
//Assert that the deleted table can be fully constructed back //Assert that the deleted table can be fully constructed back
ITypedReferenceableInstance table = repositoryService.getEntityDefinition(expected.getId()._getId()); ITypedReferenceableInstance table = repositoryService.getEntityDefinition(expected.getId()._getId());
...@@ -67,6 +110,7 @@ public class GraphBackedRepositorySoftDeleteTest extends GraphBackedMetadataRepo ...@@ -67,6 +110,7 @@ public class GraphBackedRepositorySoftDeleteTest extends GraphBackedMetadataRepo
List<ITypedReferenceableInstance> expectedColumns = List<ITypedReferenceableInstance> expectedColumns =
(List<ITypedReferenceableInstance>) table.get(TestUtils.COLUMNS_ATTR_NAME); (List<ITypedReferenceableInstance>) table.get(TestUtils.COLUMNS_ATTR_NAME);
assertEquals(columns.size(), expectedColumns.size()); assertEquals(columns.size(), expectedColumns.size());
assertNotNull(table.get("database"));
} }
@Override @Override
...@@ -85,11 +129,57 @@ public class GraphBackedRepositorySoftDeleteTest extends GraphBackedMetadataRepo ...@@ -85,11 +129,57 @@ public class GraphBackedRepositorySoftDeleteTest extends GraphBackedMetadataRepo
} }
@Override @Override
protected void assertTestDisconnectBidirectionalReferences(String janeGuid) throws Exception { protected void assertJohnForTestDisconnectBidirectionalReferences(ITypedReferenceableInstance john, String janeGuid)
throws Exception {
Id mgr = (Id) john.get("manager");
assertNotNull(mgr);
assertEquals(mgr._getId(), janeGuid);
assertEquals(mgr.getState(), Id.EntityState.DELETED);
}
@Override
protected void assertMaxForTestDisconnectBidirectionalReferences(Map<String, String> nameGuidMap) throws Exception {
// Verify that the Department.employees reference to the deleted employee
// was disconnected.
ITypedReferenceableInstance hrDept = repositoryService.getEntityDefinition(nameGuidMap.get("hr"));
List<ITypedReferenceableInstance> employees = (List<ITypedReferenceableInstance>) hrDept.get("employees");
Assert.assertEquals(employees.size(), 4);
String maxGuid = nameGuidMap.get("Max");
for (ITypedReferenceableInstance employee : employees) {
if (employee.getId()._getId().equals(maxGuid)) {
assertEquals(employee.getId().getState(), Id.EntityState.DELETED);
}
}
// Verify that the Manager.subordinates still references deleted employee // Verify that the Manager.subordinates still references deleted employee
ITypedReferenceableInstance jane = repositoryService.getEntityDefinition(janeGuid); ITypedReferenceableInstance jane = repositoryService.getEntityDefinition(nameGuidMap.get("Jane"));
List<ITypedReferenceableInstance> subordinates = (List<ITypedReferenceableInstance>) jane.get("subordinates"); List<ITypedReferenceableInstance> subordinates = (List<ITypedReferenceableInstance>) jane.get("subordinates");
assertEquals(subordinates.size(), 2); assertEquals(subordinates.size(), 2);
for (ITypedReferenceableInstance subordinate : subordinates) {
if (subordinate.getId()._getId().equals(maxGuid)) {
assertEquals(subordinate.getId().getState(), Id.EntityState.DELETED);
}
}
// Verify that max's Person.mentor unidirectional reference to john was disconnected.
ITypedReferenceableInstance john = repositoryService.getEntityDefinition(nameGuidMap.get("John"));
Id mentor = (Id) john.get("mentor");
assertEquals(mentor._getId(), maxGuid);
assertEquals(mentor.getState(), Id.EntityState.DELETED);
}
@Override
protected void assertTestDisconnectUnidirectionalArrayReferenceFromClassType(
List<ITypedReferenceableInstance> columns, String columnGuid) {
Assert.assertEquals(columns.size(), 5);
for (ITypedReferenceableInstance column : columns) {
if (column.getId()._getId().equals(columnGuid)) {
assertEquals(column.getId().getState(), Id.EntityState.DELETED);
} else {
assertEquals(column.getId().getState(), Id.EntityState.ACTIVE);
}
}
} }
@Override @Override
...@@ -122,7 +212,6 @@ public class GraphBackedRepositorySoftDeleteTest extends GraphBackedMetadataRepo ...@@ -122,7 +212,6 @@ public class GraphBackedRepositorySoftDeleteTest extends GraphBackedMetadataRepo
@Override @Override
protected void assertTestDeleteTargetOfMultiplicityRequiredReference() throws Exception { protected void assertTestDeleteTargetOfMultiplicityRequiredReference() throws Exception {
// No-op - it's ok that no exception was thrown if soft deletes are enabled. // No-op - it's ok that no exception was thrown if soft deletes are enabled.
} }
} }
...@@ -73,6 +73,7 @@ import java.util.Map; ...@@ -73,6 +73,7 @@ import java.util.Map;
import static org.apache.atlas.TestUtils.COLUMNS_ATTR_NAME; import static org.apache.atlas.TestUtils.COLUMNS_ATTR_NAME;
import static org.apache.atlas.TestUtils.COLUMN_TYPE; import static org.apache.atlas.TestUtils.COLUMN_TYPE;
import static org.apache.atlas.TestUtils.PII;
import static org.apache.atlas.TestUtils.TABLE_TYPE; import static org.apache.atlas.TestUtils.TABLE_TYPE;
import static org.apache.atlas.TestUtils.createColumnEntity; import static org.apache.atlas.TestUtils.createColumnEntity;
import static org.apache.atlas.TestUtils.createDBEntity; import static org.apache.atlas.TestUtils.createDBEntity;
...@@ -80,6 +81,7 @@ import static org.apache.atlas.TestUtils.createTableEntity; ...@@ -80,6 +81,7 @@ import static org.apache.atlas.TestUtils.createTableEntity;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull; import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail; import static org.testng.Assert.fail;
@Guice(modules = RepositoryMetadataModule.class) @Guice(modules = RepositoryMetadataModule.class)
...@@ -154,23 +156,21 @@ public class DefaultMetadataServiceTest { ...@@ -154,23 +156,21 @@ public class DefaultMetadataServiceTest {
String entityjson = InstanceSerialization.toJson(entity, true); String entityjson = InstanceSerialization.toJson(entity, true);
JSONArray entitiesJson = new JSONArray(); JSONArray entitiesJson = new JSONArray();
entitiesJson.put(entityjson); entitiesJson.put(entityjson);
String response = metadataService.createEntities(entitiesJson.toString()); List<String> guids = metadataService.createEntities(entitiesJson.toString());
JSONArray guids = new JSONArray(response); if (guids != null && guids.size() > 0) {
if (guids != null && guids.length() > 0) { return guids.get(guids.size() - 1);
return guids.getString(guids.length() - 1);
} }
return null; return null;
} }
private String updateInstance(Referenceable entity) throws Exception { private AtlasClient.EntityResult updateInstance(Referenceable entity) throws Exception {
RequestContext.createContext(); RequestContext.createContext();
ParamChecker.notNull(entity, "Entity"); ParamChecker.notNull(entity, "Entity");
ParamChecker.notNull(entity.getId(), "Entity"); ParamChecker.notNull(entity.getId(), "Entity");
String entityjson = InstanceSerialization.toJson(entity, true); String entityjson = InstanceSerialization.toJson(entity, true);
JSONArray entitiesJson = new JSONArray(); JSONArray entitiesJson = new JSONArray();
entitiesJson.put(entityjson); entitiesJson.put(entityjson);
String response = metadataService.updateEntities(entitiesJson.toString()); return metadataService.updateEntities(entitiesJson.toString());
return new JSONArray(response).getString(0);
} }
@Test(expectedExceptions = TypeNotFoundException.class) @Test(expectedExceptions = TypeNotFoundException.class)
...@@ -201,6 +201,32 @@ public class DefaultMetadataServiceTest { ...@@ -201,6 +201,32 @@ public class DefaultMetadataServiceTest {
} }
@Test @Test
public void testAddDeleteTrait() throws Exception {
Referenceable entity = createDBEntity();
String id = createInstance(entity);
//add trait
Struct tag = new Struct(TestUtils.PII);
metadataService.addTrait(id, InstanceSerialization.toJson(tag, true));
List<String> traits = metadataService.getTraitNames(id);
assertEquals(traits.size(), 1);
assertEquals(traits.get(0), PII);
//delete trait
metadataService.deleteTrait(id, PII);
traits = metadataService.getTraitNames(id);
assertEquals(traits.size(), 0);
//add trait again
metadataService.addTrait(id, InstanceSerialization.toJson(tag, true));
traits = metadataService.getTraitNames(id);
assertEquals(traits.size(), 1);
assertEquals(traits.get(0), PII);
}
@Test
public void testEntityAudit() throws Exception { public void testEntityAudit() throws Exception {
//create entity //create entity
Referenceable entity = createDBEntity(); Referenceable entity = createDBEntity();
...@@ -221,7 +247,7 @@ public class DefaultMetadataServiceTest { ...@@ -221,7 +247,7 @@ public class DefaultMetadataServiceTest {
assertAuditEvents(id, EntityAuditEvent.EntityAuditAction.ENTITY_DELETE); assertAuditEvents(id, EntityAuditEvent.EntityAuditAction.ENTITY_DELETE);
} }
private List<String> deleteEntities(String... guids) throws AtlasException { private AtlasClient.EntityResult deleteEntities(String... guids) throws AtlasException {
RequestContext.createContext(); RequestContext.createContext();
return metadataService.deleteEntities(Arrays.asList(guids)); return metadataService.deleteEntities(Arrays.asList(guids));
} }
...@@ -350,7 +376,7 @@ public class DefaultMetadataServiceTest { ...@@ -350,7 +376,7 @@ public class DefaultMetadataServiceTest {
Assert.assertTrue(partsMap.get("part2").equalsContents(((Map<String, Struct>)tableDefinition.get("partitionsMap")).get("part2"))); Assert.assertTrue(partsMap.get("part2").equalsContents(((Map<String, Struct>)tableDefinition.get("partitionsMap")).get("part2")));
//Test map pointing to a class //Test map pointing to a class
final Map<String, Struct> columnsMap = new HashMap<>(); final Map<String, Referenceable> columnsMap = new HashMap<>();
Referenceable col0Type = new Referenceable(TestUtils.COLUMN_TYPE, Referenceable col0Type = new Referenceable(TestUtils.COLUMN_TYPE,
new HashMap<String, Object>() {{ new HashMap<String, Object>() {{
put(NAME, "test1"); put(NAME, "test1");
...@@ -393,17 +419,33 @@ public class DefaultMetadataServiceTest { ...@@ -393,17 +419,33 @@ public class DefaultMetadataServiceTest {
verifyMapUpdates(TestUtils.TABLE_TYPE, NAME, (String) table.get(NAME), null, TestUtils.COLUMNS_MAP); verifyMapUpdates(TestUtils.TABLE_TYPE, NAME, (String) table.get(NAME), null, TestUtils.COLUMNS_MAP);
} }
private void verifyMapUpdates(String typeName, String uniqAttrName, String uniqAttrValue, Map<String, Struct> expectedMap, String mapAttrName) throws AtlasException { private void verifyMapUpdates(String typeName, String uniqAttrName, String uniqAttrValue,
Map<String, Referenceable> expectedMap, String mapAttrName) throws AtlasException {
String json = String json =
metadataService.getEntityDefinition(typeName, uniqAttrName, uniqAttrValue); metadataService.getEntityDefinition(typeName, uniqAttrName, uniqAttrValue);
Referenceable tableDefinition = InstanceSerialization.fromJsonReferenceable(json, true); Referenceable tableDefinition = InstanceSerialization.fromJsonReferenceable(json, true);
Map<String, Referenceable> actualMap = (Map<String, Referenceable>) tableDefinition.get(mapAttrName);
if(expectedMap == null) { if (expectedMap == null && actualMap != null) {
Assert.assertNull(tableDefinition.get(TestUtils.COLUMNS_MAP)); //all are marked as deleted in case of soft delete
for (String key : actualMap.keySet()) {
assertEquals(actualMap.get(key).getId().state, Id.EntityState.DELETED);
}
} else if(expectedMap == null) {
//hard delete case
assertNull(actualMap);
} else { } else {
Assert.assertEquals(((Map<String, Referenceable>)tableDefinition.get(mapAttrName)).size(), expectedMap.size()); assertTrue(actualMap.size() >= expectedMap.size());
for (String key : expectedMap.keySet()) { for (String key : expectedMap.keySet()) {
Assert.assertTrue(((Map<String, Referenceable>) tableDefinition.get(mapAttrName)).get(key).equalsContents(expectedMap.get(key))); assertTrue(actualMap.get(key).equalsContents(expectedMap.get(key)));
}
//rest of the keys are marked as deleted
List<String> extraKeys = new ArrayList<>(actualMap.keySet());
extraKeys.removeAll(expectedMap.keySet());
for (String key : extraKeys) {
assertEquals(actualMap.get(key).getId().getState(), Id.EntityState.DELETED);
} }
} }
} }
...@@ -438,14 +480,13 @@ public class DefaultMetadataServiceTest { ...@@ -438,14 +480,13 @@ public class DefaultMetadataServiceTest {
Assert.assertEquals(actualColumns, updatedColNameList); Assert.assertEquals(actualColumns, updatedColNameList);
} }
private void updateEntityPartial(String guid, Referenceable entity) throws AtlasException { private AtlasClient.EntityResult updateEntityPartial(String guid, Referenceable entity) throws AtlasException {
RequestContext.createContext(); RequestContext.createContext();
metadataService.updateEntityPartialByGuid(guid, entity); return metadataService.updateEntityPartialByGuid(guid, entity);
} }
@Test @Test
public void testUpdateEntityArrayOfClass() throws Exception { public void testUpdateEntityArrayOfClass() throws Exception {
//test array of class with id //test array of class with id
final List<Referenceable> columns = new ArrayList<>(); final List<Referenceable> columns = new ArrayList<>();
Map<String, Object> values = new HashMap<>(); Map<String, Object> values = new HashMap<>();
...@@ -456,63 +497,67 @@ public class DefaultMetadataServiceTest { ...@@ -456,63 +497,67 @@ public class DefaultMetadataServiceTest {
Referenceable tableUpdated = new Referenceable(TestUtils.TABLE_TYPE, new HashMap<String, Object>() {{ Referenceable tableUpdated = new Referenceable(TestUtils.TABLE_TYPE, new HashMap<String, Object>() {{
put(COLUMNS_ATTR_NAME, columns); put(COLUMNS_ATTR_NAME, columns);
}}); }});
updateEntityPartial(tableId._getId(), tableUpdated);
AtlasClient.EntityResult entityResult = updateEntityPartial(tableId._getId(), tableUpdated);
assertEquals(entityResult.getCreatedEntities().size(), 1); //col1 created
assertEquals(entityResult.getUpdateEntities().size(), 1); //table updated
assertEquals(entityResult.getUpdateEntities().get(0), tableId._getId());
verifyArrayUpdates(TestUtils.TABLE_TYPE, NAME, (String) table.get(NAME), columns, COLUMNS_ATTR_NAME); verifyArrayUpdates(TestUtils.TABLE_TYPE, NAME, (String) table.get(NAME), columns, COLUMNS_ATTR_NAME);
//Partial update. Add col5 But also update col1 //Partial update. Add col2 But also update col1
Map<String, Object> valuesCol5 = new HashMap<>(); Map<String, Object> valuesCol5 = new HashMap<>();
valuesCol5.put(NAME, "col5"); valuesCol5.put(NAME, "col2");
valuesCol5.put("type", "type"); valuesCol5.put("type", "type");
Referenceable col2 = new Referenceable(TestUtils.COLUMN_TYPE, valuesCol5); Referenceable col2 = new Referenceable(TestUtils.COLUMN_TYPE, valuesCol5);
//update col1 //update col1
col1.set("type", "type1"); col1.set("type", "type1");
columns.add(col2);
//add col5
final List<Referenceable> updateColumns = Arrays.asList(col1, col2);
tableUpdated = new Referenceable(TestUtils.TABLE_TYPE, new HashMap<String, Object>() {{ tableUpdated = new Referenceable(TestUtils.TABLE_TYPE, new HashMap<String, Object>() {{
put(COLUMNS_ATTR_NAME, updateColumns); put(COLUMNS_ATTR_NAME, columns);
}}); }});
updateEntityPartial(tableId._getId(), tableUpdated); entityResult = updateEntityPartial(tableId._getId(), tableUpdated);
assertEquals(entityResult.getCreatedEntities().size(), 1); //col2 created
assertEquals(entityResult.getUpdateEntities().size(), 2); //table, col1 updated
verifyArrayUpdates(TestUtils.TABLE_TYPE, NAME, (String) table.get(NAME), updateColumns, COLUMNS_ATTR_NAME); verifyArrayUpdates(TestUtils.TABLE_TYPE, NAME, (String) table.get(NAME), columns, COLUMNS_ATTR_NAME);
//Complete update. Add array elements - col3,4 //Complete update. Add array elements - col3,col4
Map<String, Object> values1 = new HashMap<>(); Map<String, Object> values1 = new HashMap<>();
values1.put(NAME, "col3"); values1.put(NAME, "col3");
values1.put("type", "type"); values1.put("type", "type");
Referenceable ref1 = new Referenceable(TestUtils.COLUMN_TYPE, values1); Referenceable col3 = new Referenceable(TestUtils.COLUMN_TYPE, values1);
columns.add(ref1); columns.add(col3);
Map<String, Object> values2 = new HashMap<>(); Map<String, Object> values2 = new HashMap<>();
values2.put(NAME, "col4"); values2.put(NAME, "col4");
values2.put("type", "type"); values2.put("type", "type");
Referenceable ref2 = new Referenceable(TestUtils.COLUMN_TYPE, values2); Referenceable col4 = new Referenceable(TestUtils.COLUMN_TYPE, values2);
columns.add(ref2); columns.add(col4);
table.set(COLUMNS_ATTR_NAME, columns); table.set(COLUMNS_ATTR_NAME, columns);
updateInstance(table); entityResult = updateInstance(table);
assertEquals(entityResult.getCreatedEntities().size(), 2); //col3, col4 created
verifyArrayUpdates(TestUtils.TABLE_TYPE, NAME, (String) table.get(NAME), columns, COLUMNS_ATTR_NAME); verifyArrayUpdates(TestUtils.TABLE_TYPE, NAME, (String) table.get(NAME), columns, COLUMNS_ATTR_NAME);
//Swap elements //Swap elements
columns.clear(); columns.clear();
columns.add(ref2); columns.add(col4);
columns.add(ref1); columns.add(col3);
table.set(COLUMNS_ATTR_NAME, columns); table.set(COLUMNS_ATTR_NAME, columns);
updateInstance(table); entityResult = updateInstance(table);
assertEquals(entityResult.getDeletedEntities().size(), 2); //col1, col2 are deleted
verifyArrayUpdates(TestUtils.TABLE_TYPE, NAME, (String) table.get(NAME), columns, COLUMNS_ATTR_NAME); verifyArrayUpdates(TestUtils.TABLE_TYPE, NAME, (String) table.get(NAME), columns, COLUMNS_ATTR_NAME);
//drop a single column //drop a single column
columns.clear(); columns.clear();
columns.add(ref1); columns.add(col3);
table.set(COLUMNS_ATTR_NAME, columns); table.set(COLUMNS_ATTR_NAME, columns);
updateInstance(table); entityResult = updateInstance(table);
assertEquals(entityResult.getDeletedEntities().size(), 1); //col4 deleted
verifyArrayUpdates(TestUtils.TABLE_TYPE, NAME, (String) table.get(NAME), columns, COLUMNS_ATTR_NAME); verifyArrayUpdates(TestUtils.TABLE_TYPE, NAME, (String) table.get(NAME), columns, COLUMNS_ATTR_NAME);
//Remove a class reference/Id and insert another reference //Remove a class reference/Id and insert another reference
...@@ -520,34 +565,46 @@ public class DefaultMetadataServiceTest { ...@@ -520,34 +565,46 @@ public class DefaultMetadataServiceTest {
values.clear(); values.clear();
columns.clear(); columns.clear();
values.put(NAME, "col2"); values.put(NAME, "col5");
values.put("type", "type"); values.put("type", "type");
col1 = new Referenceable(TestUtils.COLUMN_TYPE, values); Referenceable col5 = new Referenceable(TestUtils.COLUMN_TYPE, values);
columns.add(col1); columns.add(col5);
table.set(COLUMNS_ATTR_NAME, columns); table.set(COLUMNS_ATTR_NAME, columns);
updateInstance(table); entityResult = updateInstance(table);
assertEquals(entityResult.getCreatedEntities().size(), 1); //col5 created
assertEquals(entityResult.getDeletedEntities().size(), 1); //col3 deleted
verifyArrayUpdates(TestUtils.TABLE_TYPE, NAME, (String) table.get(NAME), columns, COLUMNS_ATTR_NAME); verifyArrayUpdates(TestUtils.TABLE_TYPE, NAME, (String) table.get(NAME), columns, COLUMNS_ATTR_NAME);
//Update array column to null //Update array column to null
table.setNull(COLUMNS_ATTR_NAME); table.setNull(COLUMNS_ATTR_NAME);
String newtableId = updateInstance(table); entityResult = updateInstance(table);
Assert.assertEquals(newtableId, tableId._getId()); assertEquals(entityResult.getDeletedEntities().size(), 1);
verifyArrayUpdates(TestUtils.TABLE_TYPE, NAME, (String) table.get(NAME), null, COLUMNS_ATTR_NAME); verifyArrayUpdates(TestUtils.TABLE_TYPE, NAME, (String) table.get(NAME), null, COLUMNS_ATTR_NAME);
} }
private void verifyArrayUpdates(String typeName, String uniqAttrName, String uniqAttrValue, List<? extends Struct> expectedArray, String arrAttrName) throws AtlasException { private void verifyArrayUpdates(String typeName, String uniqAttrName, String uniqAttrValue,
String json = List<Referenceable> expectedArray, String arrAttrName) throws AtlasException {
metadataService.getEntityDefinition(typeName, uniqAttrName, uniqAttrValue); String json = metadataService.getEntityDefinition(typeName, uniqAttrName, uniqAttrValue);
Referenceable entityDefinition = InstanceSerialization.fromJsonReferenceable(json, true); Referenceable entityDefinition = InstanceSerialization.fromJsonReferenceable(json, true);
List<Referenceable> actualArray = (List<Referenceable>) entityDefinition.get(arrAttrName);
if (expectedArray == null) { if (expectedArray == null && actualArray != null) {
Assert.assertNull(entityDefinition.get(arrAttrName)); //all are marked as deleted in case of soft delete
for (int index = 0; index < actualArray.size(); index++) {
assertEquals(actualArray.get(index).getId().state, Id.EntityState.DELETED);
}
} else if(expectedArray == null) {
//hard delete case
assertNull(actualArray);
} else { } else {
Assert.assertEquals(((List<Referenceable>)entityDefinition.get(arrAttrName)).size(), expectedArray.size()); int index;
for (int index = 0; index < expectedArray.size(); index++) { for (index = 0; index < expectedArray.size(); index++) {
Assert.assertTrue(((List<Referenceable>) entityDefinition.get(arrAttrName)).get(index).equalsContents(expectedArray.get(index))); Assert.assertTrue(actualArray.get(index).equalsContents(expectedArray.get(index)));
}
//Rest of the entities in the list are marked as deleted
for (; index < actualArray.size(); index++) {
assertEquals(actualArray.get(index).getId().state, Id.EntityState.DELETED);
} }
} }
} }
...@@ -560,7 +617,7 @@ public class DefaultMetadataServiceTest { ...@@ -560,7 +617,7 @@ public class DefaultMetadataServiceTest {
serdeInstance.set("description", "testDesc"); serdeInstance.set("description", "testDesc");
table.set("serde1", serdeInstance); table.set("serde1", serdeInstance);
String newtableId = updateInstance(table); String newtableId = updateInstance(table).getUpdateEntities().get(0);
Assert.assertEquals(newtableId, tableId._getId()); Assert.assertEquals(newtableId, tableId._getId());
String tableDefinitionJson = String tableDefinitionJson =
...@@ -664,7 +721,7 @@ public class DefaultMetadataServiceTest { ...@@ -664,7 +721,7 @@ public class DefaultMetadataServiceTest {
List<Struct> partitions = new ArrayList<Struct>(){{ add(partition1); add(partition2); }}; List<Struct> partitions = new ArrayList<Struct>(){{ add(partition1); add(partition2); }};
table.set("partitions", partitions); table.set("partitions", partitions);
String newtableId = updateInstance(table); String newtableId = updateInstance(table).getUpdateEntities().get(0);
Assert.assertEquals(newtableId, tableId._getId()); Assert.assertEquals(newtableId, tableId._getId());
String tableDefinitionJson = String tableDefinitionJson =
...@@ -673,15 +730,14 @@ public class DefaultMetadataServiceTest { ...@@ -673,15 +730,14 @@ public class DefaultMetadataServiceTest {
Assert.assertNotNull(tableDefinition.get("partitions")); Assert.assertNotNull(tableDefinition.get("partitions"));
List<Struct> partitionsActual = (List<Struct>) tableDefinition.get("partitions"); List<Struct> partitionsActual = (List<Struct>) tableDefinition.get("partitions");
Assert.assertEquals(partitionsActual.size(), 2); assertPartitions(partitionsActual, partitions);
Assert.assertTrue(partitions.get(0).equalsContents(partitionsActual.get(0)));
//add a new element to array of struct //add a new element to array of struct
final Struct partition3 = new Struct(TestUtils.PARTITION_STRUCT_TYPE); final Struct partition3 = new Struct(TestUtils.PARTITION_STRUCT_TYPE);
partition3.set(NAME, "part3"); partition3.set(NAME, "part3");
partitions.add(partition3); partitions.add(partition3);
table.set("partitions", partitions); table.set("partitions", partitions);
newtableId = updateInstance(table); newtableId = updateInstance(table).getUpdateEntities().get(0);
Assert.assertEquals(newtableId, tableId._getId()); Assert.assertEquals(newtableId, tableId._getId());
tableDefinitionJson = tableDefinitionJson =
...@@ -690,13 +746,12 @@ public class DefaultMetadataServiceTest { ...@@ -690,13 +746,12 @@ public class DefaultMetadataServiceTest {
Assert.assertNotNull(tableDefinition.get("partitions")); Assert.assertNotNull(tableDefinition.get("partitions"));
partitionsActual = (List<Struct>) tableDefinition.get("partitions"); partitionsActual = (List<Struct>) tableDefinition.get("partitions");
Assert.assertEquals(partitionsActual.size(), 3); assertPartitions(partitionsActual, partitions);
Assert.assertTrue(partitions.get(2).equalsContents(partitionsActual.get(2)));
//remove one of the struct values //remove one of the struct values
partitions.remove(1); partitions.remove(1);
table.set("partitions", partitions); table.set("partitions", partitions);
newtableId = updateInstance(table); newtableId = updateInstance(table).getUpdateEntities().get(0);
Assert.assertEquals(newtableId, tableId._getId()); Assert.assertEquals(newtableId, tableId._getId());
tableDefinitionJson = tableDefinitionJson =
...@@ -705,13 +760,11 @@ public class DefaultMetadataServiceTest { ...@@ -705,13 +760,11 @@ public class DefaultMetadataServiceTest {
Assert.assertNotNull(tableDefinition.get("partitions")); Assert.assertNotNull(tableDefinition.get("partitions"));
partitionsActual = (List<Struct>) tableDefinition.get("partitions"); partitionsActual = (List<Struct>) tableDefinition.get("partitions");
Assert.assertEquals(partitionsActual.size(), 2); assertPartitions(partitionsActual, partitions);
Assert.assertTrue(partitions.get(0).equalsContents(partitionsActual.get(0)));
Assert.assertTrue(partitions.get(1).equalsContents(partitionsActual.get(1)));
//Update struct value within array of struct //Update struct value within array of struct
partition1.set(NAME, "part4"); partitions.get(0).set(NAME, "part4");
newtableId = updateInstance(table); newtableId = updateInstance(table).getUpdateEntities().get(0);
Assert.assertEquals(newtableId, tableId._getId()); Assert.assertEquals(newtableId, tableId._getId());
tableDefinitionJson = tableDefinitionJson =
...@@ -720,15 +773,14 @@ public class DefaultMetadataServiceTest { ...@@ -720,15 +773,14 @@ public class DefaultMetadataServiceTest {
Assert.assertNotNull(tableDefinition.get("partitions")); Assert.assertNotNull(tableDefinition.get("partitions"));
partitionsActual = (List<Struct>) tableDefinition.get("partitions"); partitionsActual = (List<Struct>) tableDefinition.get("partitions");
Assert.assertEquals(partitionsActual.size(), 2); assertPartitions(partitionsActual, partitions);
Assert.assertTrue(partitions.get(0).equalsContents(partitionsActual.get(0)));
//add a repeated element to array of struct //add a repeated element to array of struct
final Struct partition4 = new Struct(TestUtils.PARTITION_STRUCT_TYPE); final Struct partition4 = new Struct(TestUtils.PARTITION_STRUCT_TYPE);
partition4.set(NAME, "part4"); partition4.set(NAME, "part4");
partitions.add(partition4); partitions.add(partition4);
table.set("partitions", partitions); table.set("partitions", partitions);
newtableId = updateInstance(table); newtableId = updateInstance(table).getUpdateEntities().get(0);
Assert.assertEquals(newtableId, tableId._getId()); Assert.assertEquals(newtableId, tableId._getId());
tableDefinitionJson = tableDefinitionJson =
...@@ -737,15 +789,12 @@ public class DefaultMetadataServiceTest { ...@@ -737,15 +789,12 @@ public class DefaultMetadataServiceTest {
Assert.assertNotNull(tableDefinition.get("partitions")); Assert.assertNotNull(tableDefinition.get("partitions"));
partitionsActual = (List<Struct>) tableDefinition.get("partitions"); partitionsActual = (List<Struct>) tableDefinition.get("partitions");
Assert.assertEquals(partitionsActual.size(), 3); assertPartitions(partitionsActual, partitions);
Assert.assertEquals(partitionsActual.get(2).get(NAME), "part4");
Assert.assertEquals(partitionsActual.get(0).get(NAME), "part4");
Assert.assertTrue(partitions.get(2).equalsContents(partitionsActual.get(2)));
// Remove all elements. Should set array attribute to null // Remove all elements. Should set array attribute to null
partitions.clear(); partitions.clear();
newtableId = updateInstance(table); newtableId = updateInstance(table).getUpdateEntities().get(0);
Assert.assertEquals(newtableId, tableId._getId()); Assert.assertEquals(newtableId, tableId._getId());
tableDefinitionJson = tableDefinitionJson =
...@@ -755,6 +804,13 @@ public class DefaultMetadataServiceTest { ...@@ -755,6 +804,13 @@ public class DefaultMetadataServiceTest {
Assert.assertNull(tableDefinition.get("partitions")); Assert.assertNull(tableDefinition.get("partitions"));
} }
private void assertPartitions(List<Struct> partitionsActual, List<Struct> partitions) {
assertEquals(partitionsActual.size(), partitions.size());
for (int index = 0; index < partitions.size(); index++) {
assertTrue(partitionsActual.get(index).equalsContents(partitions.get(index)));
}
}
@Test(expectedExceptions = ValueConversionException.class) @Test(expectedExceptions = ValueConversionException.class)
public void testUpdateRequiredAttrToNull() throws Exception { public void testUpdateRequiredAttrToNull() throws Exception {
...@@ -772,7 +828,6 @@ public class DefaultMetadataServiceTest { ...@@ -772,7 +828,6 @@ public class DefaultMetadataServiceTest {
@Test @Test
public void testUpdateOptionalAttrToNull() throws Exception { public void testUpdateOptionalAttrToNull() throws Exception {
String tableDefinitionJson = String tableDefinitionJson =
metadataService.getEntityDefinition(TestUtils.TABLE_TYPE, NAME, (String) table.get(NAME)); metadataService.getEntityDefinition(TestUtils.TABLE_TYPE, NAME, (String) table.get(NAME));
Referenceable tableDefinition = InstanceSerialization.fromJsonReferenceable(tableDefinitionJson, true); Referenceable tableDefinition = InstanceSerialization.fromJsonReferenceable(tableDefinitionJson, true);
...@@ -782,7 +837,7 @@ public class DefaultMetadataServiceTest { ...@@ -782,7 +837,7 @@ public class DefaultMetadataServiceTest {
//Update optional attribute //Update optional attribute
table.setNull("created"); table.setNull("created");
String newtableId = updateInstance(table); String newtableId = updateInstance(table).getUpdateEntities().get(0);
Assert.assertEquals(newtableId, tableId._getId()); Assert.assertEquals(newtableId, tableId._getId());
tableDefinitionJson = tableDefinitionJson =
...@@ -853,35 +908,39 @@ public class DefaultMetadataServiceTest { ...@@ -853,35 +908,39 @@ public class DefaultMetadataServiceTest {
// Register an EntityChangeListener to verify the notification mechanism // Register an EntityChangeListener to verify the notification mechanism
// is working for deleteEntities(). // is working for deleteEntities().
DeleteEntitiesChangeListener listener = new DeleteEntitiesChangeListener(); EntitiesChangeListener listener = new EntitiesChangeListener();
metadataService.registerListener(listener); metadataService.registerListener(listener);
//Delete one column
String columnId = table1Columns.get(0).getId()._getId();
AtlasClient.EntityResult entityResult = deleteEntities(columnId);
//column is deleted and table is updated
assertEquals(entityResult.getDeletedEntities().get(0), columnId);
assertEquals(entityResult.getUpdateEntities().get(0), table1Entity.getId()._getId());
//verify listener was called for updates and deletes
assertEquals(entityResult.getDeletedEntities(), listener.getDeletedEntities());
assertEquals(entityResult.getUpdateEntities(), listener.getUpdatedEntities());
// Delete the table entities. The deletion should cascade // Delete the table entities. The deletion should cascade
// to their composite columns. // to their composite columns.
List<String> deletedGuids = deleteEntities(table1Entity.getId()._getId()); entityResult = deleteEntities(table1Entity.getId()._getId());
// Verify that deleteEntities() response has guids for tables and their composite columns. // Verify that deleteEntities() response has guids for tables and their composite columns.
Assert.assertTrue(deletedGuids.contains(table1Entity.getId()._getId())); Assert.assertTrue(entityResult.getDeletedEntities().contains(table1Entity.getId()._getId()));
for (IReferenceableInstance column : table1Columns) { Assert.assertTrue(entityResult.getDeletedEntities().contains(table1Columns.get(1).getId()._getId()));
Assert.assertTrue(deletedGuids.contains(column.getId()._getId())); Assert.assertTrue(entityResult.getDeletedEntities().contains(table1Columns.get(2).getId()._getId()));
}
// Verify that tables and their composite columns have been deleted from the repository. // Verify that tables and their composite columns have been deleted from the repository.
assertEntityDeleted(TABLE_TYPE, NAME, table1Entity.get(NAME)); assertEntityDeleted(TABLE_TYPE, NAME, table1Entity.get(NAME));
assertEntityDeleted(COLUMN_TYPE, NAME, col1.get(NAME));
assertEntityDeleted(COLUMN_TYPE, NAME, col2.get(NAME)); assertEntityDeleted(COLUMN_TYPE, NAME, col2.get(NAME));
assertEntityDeleted(COLUMN_TYPE, NAME, col3.get(NAME)); assertEntityDeleted(COLUMN_TYPE, NAME, col3.get(NAME));
// Verify that the listener was notified about the deleted entities. // Verify that the listener was notified about the deleted entities.
Collection<ITypedReferenceableInstance> deletedEntitiesFromListener = listener.getDeletedEntities(); List<String> deletedEntitiesFromListener = listener.getDeletedEntities();
Assert.assertNotNull(deletedEntitiesFromListener); Assert.assertNotNull(deletedEntitiesFromListener);
Assert.assertEquals(deletedEntitiesFromListener.size(), deletedGuids.size()); Assert.assertEquals(deletedEntitiesFromListener.size(), entityResult.getDeletedEntities().size());
List<String> deletedGuidsFromListener = new ArrayList<>(deletedGuids.size()); Assert.assertTrue(deletedEntitiesFromListener.containsAll(entityResult.getDeletedEntities()));
for (ITypedReferenceableInstance deletedEntity : deletedEntitiesFromListener) {
deletedGuidsFromListener.add(deletedEntity.getId()._getId());
}
Assert.assertEquals(deletedGuidsFromListener.size(), deletedGuids.size());
Assert.assertTrue(deletedGuidsFromListener.containsAll(deletedGuids));
} }
private void assertEntityDeleted(String typeName, String attributeName, Object attributeValue) private void assertEntityDeleted(String typeName, String attributeName, Object attributeValue)
...@@ -915,12 +974,13 @@ public class DefaultMetadataServiceTest { ...@@ -915,12 +974,13 @@ public class DefaultMetadataServiceTest {
// Register an EntityChangeListener to verify the notification mechanism // Register an EntityChangeListener to verify the notification mechanism
// is working for deleteEntityByUniqueAttribute(). // is working for deleteEntityByUniqueAttribute().
DeleteEntitiesChangeListener listener = new DeleteEntitiesChangeListener(); EntitiesChangeListener listener = new EntitiesChangeListener();
metadataService.registerListener(listener); metadataService.registerListener(listener);
// Delete the table entities. The deletion should cascade // Delete the table entities. The deletion should cascade
// to their composite columns. // to their composite columns.
List<String> deletedGuids = metadataService.deleteEntityByUniqueAttribute(TestUtils.TABLE_TYPE, NAME, (String) table1Entity.get(NAME)); List<String> deletedGuids = metadataService.deleteEntityByUniqueAttribute(TestUtils.TABLE_TYPE, NAME,
(String) table1Entity.get(NAME)).getDeletedEntities();
// Verify that deleteEntities() response has guids for tables and their composite columns. // Verify that deleteEntities() response has guids for tables and their composite columns.
Assert.assertTrue(deletedGuids.contains(table1Entity.getId()._getId())); Assert.assertTrue(deletedGuids.contains(table1Entity.getId()._getId()));
...@@ -936,15 +996,10 @@ public class DefaultMetadataServiceTest { ...@@ -936,15 +996,10 @@ public class DefaultMetadataServiceTest {
assertEntityDeleted(COLUMN_TYPE, NAME, col3.get(NAME)); assertEntityDeleted(COLUMN_TYPE, NAME, col3.get(NAME));
// Verify that the listener was notified about the deleted entities. // Verify that the listener was notified about the deleted entities.
Collection<ITypedReferenceableInstance> deletedEntitiesFromListener = listener.getDeletedEntities(); List<String> deletedEntitiesFromListener = listener.getDeletedEntities();
Assert.assertNotNull(deletedEntitiesFromListener); Assert.assertNotNull(deletedEntitiesFromListener);
Assert.assertEquals(deletedEntitiesFromListener.size(), deletedGuids.size()); Assert.assertEquals(deletedEntitiesFromListener.size(), deletedGuids.size());
List<String> deletedGuidsFromListener = new ArrayList<>(deletedGuids.size()); Assert.assertTrue(deletedEntitiesFromListener.containsAll(deletedGuids));
for (ITypedReferenceableInstance deletedEntity : deletedEntitiesFromListener) {
deletedGuidsFromListener.add(deletedEntity.getId()._getId());
}
Assert.assertEquals(deletedGuidsFromListener.size(), deletedGuids.size());
Assert.assertTrue(deletedGuidsFromListener.containsAll(deletedGuids));
} }
@Test @Test
...@@ -1024,10 +1079,10 @@ public class DefaultMetadataServiceTest { ...@@ -1024,10 +1079,10 @@ public class DefaultMetadataServiceTest {
} }
} }
private static class DeleteEntitiesChangeListener implements EntityChangeListener { private static class EntitiesChangeListener implements EntityChangeListener {
private List<String> deletedEntities = new ArrayList<>();
private Collection<ITypedReferenceableInstance> deletedEntities_; private List<String> updatedEntities = new ArrayList<>();
@Override @Override
public void onEntitiesAdded(Collection<ITypedReferenceableInstance> entities) public void onEntitiesAdded(Collection<ITypedReferenceableInstance> entities)
throws AtlasException { throws AtlasException {
...@@ -1036,6 +1091,10 @@ public class DefaultMetadataServiceTest { ...@@ -1036,6 +1091,10 @@ public class DefaultMetadataServiceTest {
@Override @Override
public void onEntitiesUpdated(Collection<ITypedReferenceableInstance> entities) public void onEntitiesUpdated(Collection<ITypedReferenceableInstance> entities)
throws AtlasException { throws AtlasException {
updatedEntities.clear();
for (ITypedReferenceableInstance entity : entities) {
updatedEntities.add(entity.getId()._getId());
}
} }
@Override @Override
...@@ -1051,11 +1110,18 @@ public class DefaultMetadataServiceTest { ...@@ -1051,11 +1110,18 @@ public class DefaultMetadataServiceTest {
@Override @Override
public void onEntitiesDeleted(Collection<ITypedReferenceableInstance> entities) public void onEntitiesDeleted(Collection<ITypedReferenceableInstance> entities)
throws AtlasException { throws AtlasException {
deletedEntities_ = entities; deletedEntities.clear();
for (ITypedReferenceableInstance entity : entities) {
deletedEntities.add(entity.getId()._getId());
}
} }
public Collection<ITypedReferenceableInstance> getDeletedEntities() { public List<String> getDeletedEntities() {
return deletedEntities_; return deletedEntities;
}
public List<String> getUpdatedEntities() {
return updatedEntities;
} }
} }
} }
...@@ -49,7 +49,16 @@ public class RequestContext { ...@@ -49,7 +49,16 @@ public class RequestContext {
private RequestContext() { private RequestContext() {
} }
//To handle gets from background threads where createContext() is not called
//createContext called for every request in the filter
public static RequestContext get() { public static RequestContext get() {
if (CURRENT_CONTEXT.get() == null) {
synchronized (RequestContext.class) {
if (CURRENT_CONTEXT.get() == null) {
createContext();
}
}
}
return CURRENT_CONTEXT.get(); return CURRENT_CONTEXT.get();
} }
...@@ -72,15 +81,19 @@ public class RequestContext { ...@@ -72,15 +81,19 @@ public class RequestContext {
this.user = user; this.user = user;
} }
public void recordCreatedEntities(Collection<String> createdEntityIds) { public void recordEntityCreate(Collection<String> createdEntityIds) {
this.createdEntityIds.addAll(createdEntityIds); this.createdEntityIds.addAll(createdEntityIds);
} }
public void recordUpdatedEntities(Collection<String> updatedEntityIds) { public void recordEntityUpdate(Collection<String> updatedEntityIds) {
this.updatedEntityIds.addAll(updatedEntityIds); this.updatedEntityIds.addAll(updatedEntityIds);
} }
public void recordDeletedEntity(String entityId, String typeName) throws AtlasException { public void recordEntityUpdate(String entityId) {
this.updatedEntityIds.add(entityId);
}
public void recordEntityDelete(String entityId, String typeName) throws AtlasException {
ClassType type = typeSystem.getDataType(ClassType.class, typeName); ClassType type = typeSystem.getDataType(ClassType.class, typeName);
ITypedReferenceableInstance entity = type.createInstance(new Id(entityId, 0, typeName)); ITypedReferenceableInstance entity = type.createInstance(new Id(entityId, 0, typeName));
if (deletedEntityIds.add(entityId)) { if (deletedEntityIds.add(entityId)) {
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
package org.apache.atlas.services; package org.apache.atlas.services;
import org.apache.atlas.AtlasClient;
import org.apache.atlas.AtlasException; import org.apache.atlas.AtlasException;
import org.apache.atlas.EntityAuditEvent; import org.apache.atlas.EntityAuditEvent;
import org.apache.atlas.listener.EntityChangeListener; import org.apache.atlas.listener.EntityChangeListener;
...@@ -80,7 +81,7 @@ public interface MetadataService { ...@@ -80,7 +81,7 @@ public interface MetadataService {
* @param entityDefinition definition * @param entityDefinition definition
* @return json array of guids of entities created * @return json array of guids of entities created
*/ */
String createEntities(String entityDefinition) throws AtlasException; List<String> createEntities(String entityDefinition) throws AtlasException;
/** /**
* Get a typed entity instance. * Get a typed entity instance.
...@@ -136,7 +137,7 @@ public interface MetadataService { ...@@ -136,7 +137,7 @@ public interface MetadataService {
* @param value property value * @param value property value
* @return json array of guids of entities created/updated * @return json array of guids of entities created/updated
*/ */
String updateEntityAttributeByGuid(String guid, String attribute, String value) throws AtlasException; AtlasClient.EntityResult updateEntityAttributeByGuid(String guid, String attribute, String value) throws AtlasException;
/** /**
* Supports Partial updates of an entity. Users can update a subset of attributes for an entity identified by its guid * Supports Partial updates of an entity. Users can update a subset of attributes for an entity identified by its guid
...@@ -146,7 +147,7 @@ public interface MetadataService { ...@@ -146,7 +147,7 @@ public interface MetadataService {
* @return json array of guids of entities created/updated * @return json array of guids of entities created/updated
* @throws AtlasException * @throws AtlasException
*/ */
String updateEntityPartialByGuid(String guid, Referenceable entity) throws AtlasException; AtlasClient.EntityResult updateEntityPartialByGuid(String guid, Referenceable entity) throws AtlasException;
/** /**
* Batch API - Adds/Updates the given entity id(guid). * Batch API - Adds/Updates the given entity id(guid).
...@@ -154,7 +155,7 @@ public interface MetadataService { ...@@ -154,7 +155,7 @@ public interface MetadataService {
* @param entityJson entity json * @param entityJson entity json
* @return json array of guids of entities created/updated * @return json array of guids of entities created/updated
*/ */
String updateEntities(String entityJson) throws AtlasException; AtlasClient.EntityResult updateEntities(String entityJson) throws AtlasException;
// Trait management functions // Trait management functions
...@@ -168,8 +169,9 @@ public interface MetadataService { ...@@ -168,8 +169,9 @@ public interface MetadataService {
* @return Guid of updated entity * @return Guid of updated entity
* @throws AtlasException * @throws AtlasException
*/ */
String updateEntityByUniqueAttribute(String typeName, String uniqueAttributeName, String attrValue, AtlasClient.EntityResult updateEntityByUniqueAttribute(String typeName, String uniqueAttributeName,
Referenceable updatedEntity) throws AtlasException; String attrValue,
Referenceable updatedEntity) throws AtlasException;
/** /**
* Gets the list of trait names for a given entity represented by a guid. * Gets the list of trait names for a given entity represented by a guid.
...@@ -207,10 +209,10 @@ public interface MetadataService { ...@@ -207,10 +209,10 @@ public interface MetadataService {
* Delete the specified entities from the repository * Delete the specified entities from the repository
* *
* @param guids entity guids to be deleted * @param guids entity guids to be deleted
* @return List of guids for deleted entities * @return List of guids for deleted entities
* @throws AtlasException * @throws AtlasException
*/ */
List<String> deleteEntities(List<String> guids) throws AtlasException; AtlasClient.EntityResult deleteEntities(List<String> guids) throws AtlasException;
/** /**
* Register a listener for entity change. * Register a listener for entity change.
...@@ -235,7 +237,8 @@ public interface MetadataService { ...@@ -235,7 +237,8 @@ public interface MetadataService {
* @return List of guids for deleted entities (including their composite references) * @return List of guids for deleted entities (including their composite references)
* @throws AtlasException * @throws AtlasException
*/ */
List<String> deleteEntityByUniqueAttribute(String typeName, String uniqueAttributeName, String attrValue) throws AtlasException; AtlasClient.EntityResult deleteEntityByUniqueAttribute(String typeName, String uniqueAttributeName,
String attrValue) throws AtlasException;
/** /**
* Returns entity audit events for entity id in the decreasing order of timestamp * Returns entity audit events for entity id in the decreasing order of timestamp
......
...@@ -100,7 +100,7 @@ public class Referenceable extends Struct implements IReferenceableInstance { ...@@ -100,7 +100,7 @@ public class Referenceable extends Struct implements IReferenceableInstance {
* @throws AtlasException if the referenceable can not be created * @throws AtlasException if the referenceable can not be created
*/ */
public Referenceable(IReferenceableInstance instance) throws AtlasException { public Referenceable(IReferenceableInstance instance) throws AtlasException {
this(instance.getId()._getId(), instance.getTypeName(), instance.getValuesMap(), instance.getTraits(), this(instance.getId(), instance.getTypeName(), instance.getValuesMap(), instance.getTraits(),
getTraits(instance)); getTraits(instance));
} }
......
...@@ -83,13 +83,13 @@ public class LocalAtlasClient extends AtlasClient { ...@@ -83,13 +83,13 @@ public class LocalAtlasClient extends AtlasClient {
} }
}; };
JSONObject response = entityOperation.run(); JSONObject response = entityOperation.run();
List<String> results = extractResults(response, GUID, new ExtractOperation<String, String>()); EntityResult results = extractEntityResult(response);
LOG.debug("Create entities returned results: {}", results); LOG.debug("Create entities returned results: {}", results);
return results; return results.getCreatedEntities();
} }
@Override @Override
protected List<String> updateEntities(final JSONArray entities) throws AtlasServiceException { protected EntityResult updateEntities(final JSONArray entities) throws AtlasServiceException {
LOG.debug("Updating entities: {}", entities); LOG.debug("Updating entities: {}", entities);
EntityOperation entityOperation = new EntityOperation(API.UPDATE_ENTITY) { EntityOperation entityOperation = new EntityOperation(API.UPDATE_ENTITY) {
@Override @Override
...@@ -98,7 +98,7 @@ public class LocalAtlasClient extends AtlasClient { ...@@ -98,7 +98,7 @@ public class LocalAtlasClient extends AtlasClient {
} }
}; };
JSONObject response = entityOperation.run(); JSONObject response = entityOperation.run();
List<String> results = extractResults(response, GUID, new ExtractOperation<String, String>()); EntityResult results = extractEntityResult(response);
LOG.debug("Update entities returned results: {}", results); LOG.debug("Update entities returned results: {}", results);
return results; return results;
} }
...@@ -130,7 +130,7 @@ public class LocalAtlasClient extends AtlasClient { ...@@ -130,7 +130,7 @@ public class LocalAtlasClient extends AtlasClient {
} }
@Override @Override
public String updateEntity(final String entityType, final String uniqueAttributeName, public EntityResult updateEntity(final String entityType, final String uniqueAttributeName,
final String uniqueAttributeValue, Referenceable entity) throws AtlasServiceException { final String uniqueAttributeValue, Referenceable entity) throws AtlasServiceException {
final String entityJson = InstanceSerialization.toJson(entity, true); final String entityJson = InstanceSerialization.toJson(entity, true);
LOG.debug("Updating entity type: {}, attributeName: {}, attributeValue: {}, entity: {}", entityType, LOG.debug("Updating entity type: {}, attributeName: {}, attributeValue: {}, entity: {}", entityType,
...@@ -143,13 +143,13 @@ public class LocalAtlasClient extends AtlasClient { ...@@ -143,13 +143,13 @@ public class LocalAtlasClient extends AtlasClient {
} }
}; };
JSONObject response = entityOperation.run(); JSONObject response = entityOperation.run();
String result = getString(response, GUID); EntityResult result = extractEntityResult(response);
LOG.debug("Update entity returned result: {}", result); LOG.debug("Update entity returned result: {}", result);
return result; return result;
} }
@Override @Override
public List<String> deleteEntity(final String entityType, final String uniqueAttributeName, public EntityResult deleteEntity(final String entityType, final String uniqueAttributeName,
final String uniqueAttributeValue) throws AtlasServiceException { final String uniqueAttributeValue) throws AtlasServiceException {
LOG.debug("Deleting entity type: {}, attributeName: {}, attributeValue: {}", entityType, uniqueAttributeName, LOG.debug("Deleting entity type: {}, attributeName: {}, attributeValue: {}", entityType, uniqueAttributeName,
uniqueAttributeValue); uniqueAttributeValue);
...@@ -160,7 +160,7 @@ public class LocalAtlasClient extends AtlasClient { ...@@ -160,7 +160,7 @@ public class LocalAtlasClient extends AtlasClient {
} }
}; };
JSONObject response = entityOperation.run(); JSONObject response = entityOperation.run();
List<String> results = extractResults(response, GUID, new ExtractOperation<String, String>()); EntityResult results = extractEntityResult(response);
LOG.debug("Delete entities returned results: {}", results); LOG.debug("Delete entities returned results: {}", results);
return results; return results;
} }
...@@ -191,18 +191,18 @@ public class LocalAtlasClient extends AtlasClient { ...@@ -191,18 +191,18 @@ public class LocalAtlasClient extends AtlasClient {
} }
@Override @Override
public void updateEntityAttribute(final String guid, final String attribute, String value) throws AtlasServiceException { public EntityResult updateEntityAttribute(final String guid, final String attribute, String value) throws AtlasServiceException {
throw new IllegalStateException("Not supported in LocalAtlasClient"); throw new IllegalStateException("Not supported in LocalAtlasClient");
} }
@Override @Override
public void updateEntity(String guid, Referenceable entity) throws AtlasServiceException { public EntityResult updateEntity(String guid, Referenceable entity) throws AtlasServiceException {
throw new IllegalStateException("Not supported in LocalAtlasClient"); throw new IllegalStateException("Not supported in LocalAtlasClient");
} }
@Override @Override
public List<String> deleteEntities(final String ... guids) throws AtlasServiceException { public EntityResult deleteEntities(final String ... guids) throws AtlasServiceException {
throw new IllegalStateException("Not supported in LocalAtlasClient"); throw new IllegalStateException("Not supported in LocalAtlasClient");
} }
......
...@@ -15,22 +15,30 @@ ...@@ -15,22 +15,30 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.atlas.notification.entity; package org.apache.atlas.notification;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Inject; import com.google.inject.Inject;
import org.apache.atlas.AtlasException; import org.apache.atlas.AtlasException;
import org.apache.atlas.listener.EntityChangeListener; import org.apache.atlas.listener.EntityChangeListener;
import org.apache.atlas.notification.NotificationInterface; import org.apache.atlas.notification.entity.EntityNotification;
import org.apache.atlas.notification.entity.EntityNotificationImpl;
import org.apache.atlas.typesystem.IReferenceableInstance; import org.apache.atlas.typesystem.IReferenceableInstance;
import org.apache.atlas.typesystem.IStruct; import org.apache.atlas.typesystem.IStruct;
import org.apache.atlas.typesystem.ITypedReferenceableInstance; import org.apache.atlas.typesystem.ITypedReferenceableInstance;
import org.apache.atlas.typesystem.Referenceable; import org.apache.atlas.typesystem.Referenceable;
import org.apache.atlas.typesystem.Struct;
import org.apache.atlas.typesystem.types.FieldMapping;
import org.apache.atlas.typesystem.types.TraitType;
import org.apache.atlas.typesystem.types.TypeSystem; import org.apache.atlas.typesystem.types.TypeSystem;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
/** /**
* Listen to the repository for entity changes and produce entity change notifications. * Listen to the repository for entity changes and produce entity change notifications.
...@@ -87,6 +95,53 @@ public class NotificationEntityChangeListener implements EntityChangeListener { ...@@ -87,6 +95,53 @@ public class NotificationEntityChangeListener implements EntityChangeListener {
// ----- helper methods ------------------------------------------------- // ----- helper methods -------------------------------------------------
// ----- helper methods ----------------------------------------------------
@VisibleForTesting
public static List<IStruct> getAllTraits(IReferenceableInstance entityDefinition,
TypeSystem typeSystem) throws AtlasException {
List<IStruct> traitInfo = new LinkedList<>();
for (String traitName : entityDefinition.getTraits()) {
IStruct trait = entityDefinition.getTrait(traitName);
String typeName = trait.getTypeName();
Map<String, Object> valuesMap = trait.getValuesMap();
traitInfo.add(new Struct(typeName, valuesMap));
traitInfo.addAll(getSuperTraits(typeName, valuesMap, typeSystem));
}
return traitInfo;
}
private static List<IStruct> getSuperTraits(
String typeName, Map<String, Object> values, TypeSystem typeSystem) throws AtlasException {
List<IStruct> superTypes = new LinkedList<>();
TraitType traitDef = typeSystem.getDataType(TraitType.class, typeName);
Set<String> superTypeNames = traitDef.getAllSuperTypeNames();
for (String superTypeName : superTypeNames) {
TraitType superTraitDef = typeSystem.getDataType(TraitType.class, superTypeName);
Map<String, Object> superTypeValues = new HashMap<>();
FieldMapping fieldMapping = superTraitDef.fieldMapping();
if (fieldMapping != null) {
Set<String> superTypeAttributeNames = fieldMapping.fields.keySet();
for (String superTypeAttributeName : superTypeAttributeNames) {
if (values.containsKey(superTypeAttributeName)) {
superTypeValues.put(superTypeAttributeName, values.get(superTypeAttributeName));
}
}
}
IStruct superTrait = new Struct(superTypeName, superTypeValues);
superTypes.add(superTrait);
superTypes.addAll(getSuperTraits(superTypeName, values, typeSystem));
}
return superTypes;
}
// send notification of entity change // send notification of entity change
private void notifyOfEntityEvent(Collection<ITypedReferenceableInstance> entityDefinitions, private void notifyOfEntityEvent(Collection<ITypedReferenceableInstance> entityDefinitions,
EntityNotification.OperationType operationType) throws AtlasException { EntityNotification.OperationType operationType) throws AtlasException {
...@@ -96,7 +151,7 @@ public class NotificationEntityChangeListener implements EntityChangeListener { ...@@ -96,7 +151,7 @@ public class NotificationEntityChangeListener implements EntityChangeListener {
Referenceable entity = new Referenceable(entityDefinition); Referenceable entity = new Referenceable(entityDefinition);
EntityNotificationImpl notification = EntityNotificationImpl notification =
new EntityNotificationImpl(entity, operationType, typeSystem); new EntityNotificationImpl(entity, operationType, getAllTraits(entity, typeSystem));
messages.add(notification); messages.add(notification);
} }
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
package org.apache.atlas.web.resources; package org.apache.atlas.web.resources;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import org.apache.atlas.AtlasClient; import org.apache.atlas.AtlasClient;
import org.apache.atlas.AtlasException; import org.apache.atlas.AtlasException;
import org.apache.atlas.EntityAuditEvent; import org.apache.atlas.EntityAuditEvent;
...@@ -59,9 +58,7 @@ import javax.ws.rs.core.MediaType; ...@@ -59,9 +58,7 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.net.URI; import java.net.URI;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
...@@ -119,19 +116,11 @@ public class EntityResource { ...@@ -119,19 +116,11 @@ public class EntityResource {
LOG.debug("submitting entities {} ", AtlasClient.toString(new JSONArray(entities))); LOG.debug("submitting entities {} ", AtlasClient.toString(new JSONArray(entities)));
final String guids = metadataService.createEntities(entities); final List<String> guids = metadataService.createEntities(entities);
JSONObject response = getResponse(new AtlasClient.EntityResult(guids, null, null));
UriBuilder ub = uriInfo.getAbsolutePathBuilder(); UriBuilder ub = uriInfo.getAbsolutePathBuilder();
URI locationURI = ub.path(guids).build(); URI locationURI = guids.isEmpty() ? null : ub.path(guids.get(0)).build();
JSONObject response = new JSONObject();
response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId());
JSONArray guidArray = new JSONArray(guids);
response.put(AtlasClient.GUID, guidArray);
if (guidArray.length() > 0) {
response.put(AtlasClient.DEFINITION,
new JSONObject(metadataService.getEntityDefinition(new JSONArray(guids).getString(0))));
}
return Response.created(locationURI).entity(response).build(); return Response.created(locationURI).entity(response).build();
...@@ -150,6 +139,18 @@ public class EntityResource { ...@@ -150,6 +139,18 @@ public class EntityResource {
} }
} }
private JSONObject getResponse(AtlasClient.EntityResult entityResult) throws AtlasException, JSONException {
JSONObject response = new JSONObject();
response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId());
response.put(AtlasClient.ENTITIES, new JSONObject(entityResult.toString()).get(AtlasClient.ENTITIES));
String sampleEntityId = getSample(entityResult);
if (sampleEntityId != null) {
String entityDefinition = metadataService.getEntityDefinition(sampleEntityId);
response.put(AtlasClient.DEFINITION, new JSONObject(entityDefinition));
}
return response;
}
/** /**
* Complete update of a set of entities - the values not specified will be replaced with null/removed * Complete update of a set of entities - the values not specified will be replaced with null/removed
* Adds/Updates given entities identified by its GUID or unique attribute * Adds/Updates given entities identified by its GUID or unique attribute
...@@ -163,14 +164,8 @@ public class EntityResource { ...@@ -163,14 +164,8 @@ public class EntityResource {
final String entities = Servlets.getRequestPayload(request); final String entities = Servlets.getRequestPayload(request);
LOG.debug("updating entities {} ", AtlasClient.toString(new JSONArray(entities))); LOG.debug("updating entities {} ", AtlasClient.toString(new JSONArray(entities)));
final String guids = metadataService.updateEntities(entities); AtlasClient.EntityResult entityResult = metadataService.updateEntities(entities);
JSONObject response = getResponse(entityResult);
JSONObject response = new JSONObject();
response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId());
JSONArray guidsArray = new JSONArray(guids);
response.put(AtlasClient.GUID, guidsArray);
String entityDefinition = metadataService.getEntityDefinition(guidsArray.getString(0));
response.put(AtlasClient.DEFINITION, new JSONObject(entityDefinition));
return Response.ok(response).build(); return Response.ok(response).build();
} catch(EntityExistsException e) { } catch(EntityExistsException e) {
LOG.error("Unique constraint violation", e); LOG.error("Unique constraint violation", e);
...@@ -187,6 +182,25 @@ public class EntityResource { ...@@ -187,6 +182,25 @@ public class EntityResource {
} }
} }
private String getSample(AtlasClient.EntityResult entityResult) {
String sample = getSample(entityResult.getCreatedEntities());
if (sample == null) {
sample = getSample(entityResult.getUpdateEntities());
}
if (sample == null) {
sample = getSample(entityResult.getDeletedEntities());
}
return sample;
}
private String getSample(List<String> list) {
if (list != null && list.size() > 0) {
return list.get(0);
}
return null;
}
/** /**
* Adds/Updates given entity identified by its unique attribute( entityType, attributeName and value) * Adds/Updates given entity identified by its unique attribute( entityType, attributeName and value)
* Updates support only partial update of an entity - Adds/updates any new values specified * Updates support only partial update of an entity - Adds/updates any new values specified
...@@ -214,11 +228,10 @@ public class EntityResource { ...@@ -214,11 +228,10 @@ public class EntityResource {
Referenceable updatedEntity = Referenceable updatedEntity =
InstanceSerialization.fromJsonReferenceable(entities, true); InstanceSerialization.fromJsonReferenceable(entities, true);
final String guid = metadataService.updateEntityByUniqueAttribute(entityType, attribute, value, updatedEntity); AtlasClient.EntityResult entityResult =
metadataService.updateEntityByUniqueAttribute(entityType, attribute, value, updatedEntity);
JSONObject response = new JSONObject(); JSONObject response = getResponse(entityResult);
response.put(AtlasClient.REQUEST_ID, Thread.currentThread().getName());
response.put(AtlasClient.GUID, guid);
return Response.ok(response).build(); return Response.ok(response).build();
} catch (ValueConversionException ve) { } catch (ValueConversionException ve) {
LOG.error("Unable to persist entity instance due to a desrialization error ", ve); LOG.error("Unable to persist entity instance due to a desrialization error ", ve);
...@@ -268,10 +281,8 @@ public class EntityResource { ...@@ -268,10 +281,8 @@ public class EntityResource {
Referenceable updatedEntity = Referenceable updatedEntity =
InstanceSerialization.fromJsonReferenceable(entityJson, true); InstanceSerialization.fromJsonReferenceable(entityJson, true);
metadataService.updateEntityPartialByGuid(guid, updatedEntity); AtlasClient.EntityResult entityResult = metadataService.updateEntityPartialByGuid(guid, updatedEntity);
JSONObject response = getResponse(entityResult);
JSONObject response = new JSONObject();
response.put(AtlasClient.REQUEST_ID, Thread.currentThread().getName());
return Response.ok(response).build(); return Response.ok(response).build();
} catch (EntityNotFoundException e) { } catch (EntityNotFoundException e) {
LOG.error("An entity with GUID={} does not exist", guid, e); LOG.error("An entity with GUID={} does not exist", guid, e);
...@@ -301,12 +312,8 @@ public class EntityResource { ...@@ -301,12 +312,8 @@ public class EntityResource {
String value = Servlets.getRequestPayload(request); String value = Servlets.getRequestPayload(request);
Preconditions.checkNotNull(value, "Entity value cannot be null"); Preconditions.checkNotNull(value, "Entity value cannot be null");
metadataService.updateEntityAttributeByGuid(guid, property, value); AtlasClient.EntityResult entityResult = metadataService.updateEntityAttributeByGuid(guid, property, value);
JSONObject response = getResponse(entityResult);
JSONObject response = new JSONObject();
response.put(AtlasClient.REQUEST_ID, Thread.currentThread().getName());
response.put(AtlasClient.GUID, guid);
return Response.ok(response).build(); return Response.ok(response).build();
} catch (EntityNotFoundException e) { } catch (EntityNotFoundException e) {
LOG.error("An entity with GUID={} does not exist", guid, e); LOG.error("An entity with GUID={} does not exist", guid, e);
...@@ -340,19 +347,13 @@ public class EntityResource { ...@@ -340,19 +347,13 @@ public class EntityResource {
@QueryParam("value") String value) { @QueryParam("value") String value) {
try { try {
List<String> deletedGuids = new ArrayList<>(); AtlasClient.EntityResult entityResult;
if (guids != null && !guids.isEmpty()) { if (guids != null && !guids.isEmpty()) {
deletedGuids = metadataService.deleteEntities(guids); entityResult = metadataService.deleteEntities(guids);
} else { } else {
deletedGuids = metadataService.deleteEntityByUniqueAttribute(entityType, attribute, value); entityResult = metadataService.deleteEntityByUniqueAttribute(entityType, attribute, value);
}
JSONObject response = new JSONObject();
response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId());
JSONArray guidArray = new JSONArray(deletedGuids.size());
for (String guid : deletedGuids) {
guidArray.put(guid);
} }
response.put(AtlasClient.GUID, guidArray); JSONObject response = getResponse(entityResult);
return Response.ok(response).build(); return Response.ok(response).build();
} catch (EntityNotFoundException e) { } catch (EntityNotFoundException e) {
if(guids != null || !guids.isEmpty()) { if(guids != null || !guids.isEmpty()) {
...@@ -386,7 +387,6 @@ public class EntityResource { ...@@ -386,7 +387,6 @@ public class EntityResource {
JSONObject response = new JSONObject(); JSONObject response = new JSONObject();
response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId()); response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId());
response.put(AtlasClient.GUID, guid);
Response.Status status = Response.Status.NOT_FOUND; Response.Status status = Response.Status.NOT_FOUND;
if (entityDefinition != null) { if (entityDefinition != null) {
...@@ -518,7 +518,6 @@ public class EntityResource { ...@@ -518,7 +518,6 @@ public class EntityResource {
JSONObject response = new JSONObject(); JSONObject response = new JSONObject();
response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId()); response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId());
response.put(AtlasClient.GUID, guid);
response.put(AtlasClient.RESULTS, new JSONArray(traitNames)); response.put(AtlasClient.RESULTS, new JSONArray(traitNames));
response.put(AtlasClient.COUNT, traitNames.size()); response.put(AtlasClient.COUNT, traitNames.size());
...@@ -555,7 +554,6 @@ public class EntityResource { ...@@ -555,7 +554,6 @@ public class EntityResource {
JSONObject response = new JSONObject(); JSONObject response = new JSONObject();
response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId()); response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId());
response.put(AtlasClient.GUID, guid);
return Response.created(locationURI).entity(response).build(); return Response.created(locationURI).entity(response).build();
} catch (EntityNotFoundException | TypeNotFoundException e) { } catch (EntityNotFoundException | TypeNotFoundException e) {
...@@ -588,7 +586,6 @@ public class EntityResource { ...@@ -588,7 +586,6 @@ public class EntityResource {
JSONObject response = new JSONObject(); JSONObject response = new JSONObject();
response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId()); response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId());
response.put(AtlasClient.GUID, guid);
response.put(TRAIT_NAME, traitName); response.put(TRAIT_NAME, traitName);
return Response.ok(response).build(); return Response.ok(response).build();
......
...@@ -23,7 +23,7 @@ import com.google.inject.multibindings.Multibinder; ...@@ -23,7 +23,7 @@ import com.google.inject.multibindings.Multibinder;
import org.apache.atlas.kafka.KafkaNotification; import org.apache.atlas.kafka.KafkaNotification;
import org.apache.atlas.listener.EntityChangeListener; import org.apache.atlas.listener.EntityChangeListener;
import org.apache.atlas.notification.NotificationHookConsumer; import org.apache.atlas.notification.NotificationHookConsumer;
import org.apache.atlas.notification.entity.NotificationEntityChangeListener; import org.apache.atlas.notification.NotificationEntityChangeListener;
import org.apache.atlas.service.Service; import org.apache.atlas.service.Service;
public class ServiceModule extends AbstractModule { public class ServiceModule extends AbstractModule {
......
...@@ -23,7 +23,6 @@ import org.apache.atlas.typesystem.Referenceable; ...@@ -23,7 +23,6 @@ import org.apache.atlas.typesystem.Referenceable;
import org.apache.atlas.web.resources.EntityResource; import org.apache.atlas.web.resources.EntityResource;
import org.apache.atlas.web.service.ServiceState; import org.apache.atlas.web.service.ServiceState;
import org.apache.commons.lang.RandomStringUtils; import org.apache.commons.lang.RandomStringUtils;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONObject; import org.codehaus.jettison.json.JSONObject;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
...@@ -36,6 +35,7 @@ import javax.ws.rs.core.Response; ...@@ -36,6 +35,7 @@ import javax.ws.rs.core.Response;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import static org.apache.atlas.AtlasClient.ENTITIES;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyListOf; import static org.mockito.Matchers.anyListOf;
import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.anyString;
...@@ -64,7 +64,8 @@ public class LocalAtlasClientTest { ...@@ -64,7 +64,8 @@ public class LocalAtlasClientTest {
when(entityResource.submit(any(HttpServletRequest.class))).thenReturn(response); when(entityResource.submit(any(HttpServletRequest.class))).thenReturn(response);
final String guid = random(); final String guid = random();
when(response.getEntity()).thenReturn(new JSONObject() {{ when(response.getEntity()).thenReturn(new JSONObject() {{
put(AtlasClient.GUID, new JSONArray(Arrays.asList(guid))); put(ENTITIES, new JSONObject(
new AtlasClient.EntityResult(Arrays.asList(guid), null, null).toString()).get(ENTITIES));
}}); }});
LocalAtlasClient atlasClient = new LocalAtlasClient(serviceState, entityResource); LocalAtlasClient atlasClient = new LocalAtlasClient(serviceState, entityResource);
...@@ -119,12 +120,14 @@ public class LocalAtlasClientTest { ...@@ -119,12 +120,14 @@ public class LocalAtlasClientTest {
when(entityResource.updateByUniqueAttribute(anyString(), anyString(), anyString(), when(entityResource.updateByUniqueAttribute(anyString(), anyString(), anyString(),
any(HttpServletRequest.class))).thenReturn(response); any(HttpServletRequest.class))).thenReturn(response);
when(response.getEntity()).thenReturn(new JSONObject() {{ when(response.getEntity()).thenReturn(new JSONObject() {{
put(AtlasClient.GUID, guid); put(ENTITIES, new JSONObject(
new AtlasClient.EntityResult(null, Arrays.asList(guid), null).toString()).get(ENTITIES));
}}); }});
LocalAtlasClient atlasClient = new LocalAtlasClient(serviceState, entityResource); LocalAtlasClient atlasClient = new LocalAtlasClient(serviceState, entityResource);
String actualId = atlasClient.updateEntity(random(), random(), random(), new Referenceable(random())); AtlasClient.EntityResult
assertEquals(actualId, guid); entityResult = atlasClient.updateEntity(random(), random(), random(), new Referenceable(random()));
assertEquals(entityResult.getUpdateEntities(), Arrays.asList(guid));
} }
@Test @Test
...@@ -132,14 +135,14 @@ public class LocalAtlasClientTest { ...@@ -132,14 +135,14 @@ public class LocalAtlasClientTest {
final String guid = random(); final String guid = random();
Response response = mock(Response.class); Response response = mock(Response.class);
when(response.getEntity()).thenReturn(new JSONObject() {{ when(response.getEntity()).thenReturn(new JSONObject() {{
put(AtlasClient.GUID, new JSONArray(Arrays.asList(guid))); put(ENTITIES, new JSONObject(
new AtlasClient.EntityResult(null, null, Arrays.asList(guid)).toString()).get(ENTITIES));
}}); }});
when(entityResource.deleteEntities(anyListOf(String.class), anyString(), anyString(), anyString())).thenReturn(response); when(entityResource.deleteEntities(anyListOf(String.class), anyString(), anyString(), anyString())).thenReturn(response);
LocalAtlasClient atlasClient = new LocalAtlasClient(serviceState, entityResource); LocalAtlasClient atlasClient = new LocalAtlasClient(serviceState, entityResource);
List<String> results = atlasClient.deleteEntity(random(), random(), random()); AtlasClient.EntityResult entityResult = atlasClient.deleteEntity(random(), random(), random());
assertEquals(results.size(), 1); assertEquals(entityResult.getDeletedEntities(), Arrays.asList(guid));
assertEquals(results.get(0), guid);
} }
private String random() { private String random() {
......
...@@ -22,7 +22,6 @@ import com.google.common.collect.ImmutableSet; ...@@ -22,7 +22,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource; import com.sun.jersey.api.client.WebResource;
import org.apache.atlas.notification.entity.EntityNotification; import org.apache.atlas.notification.entity.EntityNotification;
import org.apache.atlas.typesystem.IReferenceableInstance; import org.apache.atlas.typesystem.IReferenceableInstance;
import org.apache.atlas.typesystem.IStruct; import org.apache.atlas.typesystem.IStruct;
...@@ -43,7 +42,6 @@ import org.testng.annotations.Test; ...@@ -43,7 +42,6 @@ import org.testng.annotations.Test;
import javax.ws.rs.HttpMethod; import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
......
/**
* 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.atlas.notification;
import org.apache.atlas.typesystem.IStruct;
import org.apache.atlas.typesystem.Referenceable;
import org.apache.atlas.typesystem.Struct;
import org.apache.atlas.typesystem.types.TraitType;
import org.apache.atlas.typesystem.types.TypeSystem;
import org.testng.annotations.Test;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
public class NotificationEntityChangeListenerTest {
@Test
public void testGetAllTraitsSuperTraits() throws Exception {
TypeSystem typeSystem = mock(TypeSystem.class);
String traitName = "MyTrait";
IStruct myTrait = new Struct(traitName);
String superTraitName = "MySuperTrait";
TraitType traitDef = mock(TraitType.class);
Set<String> superTypeNames = Collections.singleton(superTraitName);
TraitType superTraitDef = mock(TraitType.class);
Set<String> superSuperTypeNames = Collections.emptySet();
Referenceable entity = getEntity("id", myTrait);
when(typeSystem.getDataType(TraitType.class, traitName)).thenReturn(traitDef);
when(typeSystem.getDataType(TraitType.class, superTraitName)).thenReturn(superTraitDef);
when(traitDef.getAllSuperTypeNames()).thenReturn(superTypeNames);
when(superTraitDef.getAllSuperTypeNames()).thenReturn(superSuperTypeNames);
List<IStruct> allTraits = NotificationEntityChangeListener.getAllTraits(entity, typeSystem);
assertEquals(2, allTraits.size());
for (IStruct trait : allTraits) {
String typeName = trait.getTypeName();
assertTrue(typeName.equals(traitName) || typeName.equals(superTraitName));
}
}
private Referenceable getEntity(String id, IStruct... traits) {
String typeName = "typeName";
Map<String, Object> values = new HashMap<>();
List<String> traitNames = new LinkedList<>();
Map<String, IStruct> traitMap = new HashMap<>();
for (IStruct trait : traits) {
String traitName = trait.getTypeName();
traitNames.add(traitName);
traitMap.put(traitName, trait);
}
return new Referenceable(id, typeName, values, traitNames, traitMap);
}
}
...@@ -71,6 +71,7 @@ import java.util.Map; ...@@ -71,6 +71,7 @@ import java.util.Map;
import java.util.UUID; import java.util.UUID;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.fail; import static org.testng.Assert.fail;
...@@ -154,7 +155,10 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -154,7 +155,10 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
JSONObject response = new JSONObject(responseAsString); JSONObject response = new JSONObject(responseAsString);
Assert.assertNotNull(response.get(AtlasClient.REQUEST_ID)); Assert.assertNotNull(response.get(AtlasClient.REQUEST_ID));
Assert.assertNotNull(response.get(AtlasClient.GUID));
AtlasClient.EntityResult entityResult = AtlasClient.EntityResult.fromString(response.toString());
assertEquals(entityResult.getCreatedEntities().size(), 1);
assertNotNull(entityResult.getCreatedEntities().get(0));
} }
@Test @Test
...@@ -376,7 +380,9 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -376,7 +380,9 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
} }
private void addProperty(String guid, String property, String value) throws AtlasServiceException { private void addProperty(String guid, String property, String value) throws AtlasServiceException {
serviceClient.updateEntityAttribute(guid, property, value); AtlasClient.EntityResult entityResult = serviceClient.updateEntityAttribute(guid, property, value);
assertEquals(entityResult.getUpdateEntities().size(), 1);
assertEquals(entityResult.getUpdateEntities().get(0), guid);
} }
private ClientResponse getEntityDefinition(String guid) { private ClientResponse getEntityDefinition(String guid) {
...@@ -482,7 +488,6 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -482,7 +488,6 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
JSONObject response = new JSONObject(responseAsString); JSONObject response = new JSONObject(responseAsString);
Assert.assertNotNull(response.get(AtlasClient.REQUEST_ID)); Assert.assertNotNull(response.get(AtlasClient.REQUEST_ID));
Assert.assertNotNull(response.get("GUID"));
final JSONArray list = response.getJSONArray(AtlasClient.RESULTS); final JSONArray list = response.getJSONArray(AtlasClient.RESULTS);
Assert.assertEquals(list.length(), 7); Assert.assertEquals(list.length(), 7);
...@@ -513,7 +518,6 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -513,7 +518,6 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
JSONObject response = new JSONObject(responseAsString); JSONObject response = new JSONObject(responseAsString);
Assert.assertNotNull(response.get(AtlasClient.REQUEST_ID)); Assert.assertNotNull(response.get(AtlasClient.REQUEST_ID));
Assert.assertNotNull(response.get(AtlasClient.GUID));
assertEntityAudit(guid, EntityAuditEvent.EntityAuditAction.TAG_ADD); assertEntityAudit(guid, EntityAuditEvent.EntityAuditAction.TAG_ADD);
} }
...@@ -561,7 +565,6 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -561,7 +565,6 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
JSONObject response = new JSONObject(responseAsString); JSONObject response = new JSONObject(responseAsString);
Assert.assertNotNull(response.get(AtlasClient.REQUEST_ID)); Assert.assertNotNull(response.get(AtlasClient.REQUEST_ID));
Assert.assertNotNull(response.get(AtlasClient.GUID));
// verify the response // verify the response
clientResponse = getEntityDefinition(guid); clientResponse = getEntityDefinition(guid);
...@@ -612,7 +615,6 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -612,7 +615,6 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
JSONObject response = new JSONObject(responseAsString); JSONObject response = new JSONObject(responseAsString);
Assert.assertNotNull(response.get(AtlasClient.REQUEST_ID)); Assert.assertNotNull(response.get(AtlasClient.REQUEST_ID));
Assert.assertNotNull(response.get("GUID"));
Assert.assertNotNull(response.get("traitName")); Assert.assertNotNull(response.get("traitName"));
assertEntityAudit(guid, EntityAuditEvent.EntityAuditAction.TAG_DELETE); assertEntityAudit(guid, EntityAuditEvent.EntityAuditAction.TAG_DELETE);
} }
...@@ -635,7 +637,8 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -635,7 +637,8 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
"trait=" + traitName + " should be defined in type system before it can be deleted"); "trait=" + traitName + " should be defined in type system before it can be deleted");
Assert.assertNotNull(response.get(AtlasClient.STACKTRACE)); Assert.assertNotNull(response.get(AtlasClient.STACKTRACE));
} }
@Test(dependsOnMethods = "testSubmitEntity()")
@Test(dependsOnMethods = "testSubmitEntity()")
public void testDeleteExistentTraitNonExistentForEntity() throws Exception { public void testDeleteExistentTraitNonExistentForEntity() throws Exception {
final String guid = tableId._getId(); final String guid = tableId._getId();
...@@ -704,7 +707,9 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -704,7 +707,9 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
}}); }});
LOG.debug("Updating entity= " + tableUpdated); LOG.debug("Updating entity= " + tableUpdated);
serviceClient.updateEntity(tableId._getId(), tableUpdated); AtlasClient.EntityResult entityResult = serviceClient.updateEntity(tableId._getId(), tableUpdated);
assertEquals(entityResult.getUpdateEntities().size(), 1);
assertEquals(entityResult.getUpdateEntities().get(0), tableId._getId());
ClientResponse response = getEntityDefinition(tableId._getId()); ClientResponse response = getEntityDefinition(tableId._getId());
String definition = getEntityDefinition(response); String definition = getEntityDefinition(response);
...@@ -722,8 +727,10 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -722,8 +727,10 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
}}); }});
LOG.debug("Updating entity= " + tableUpdated); LOG.debug("Updating entity= " + tableUpdated);
serviceClient.updateEntity(BaseResourceIT.HIVE_TABLE_TYPE, "name", (String) tableInstance.get("name"), entityResult = serviceClient.updateEntity(BaseResourceIT.HIVE_TABLE_TYPE, "name",
tableUpdated); (String) tableInstance.get("name"), tableUpdated);
assertEquals(entityResult.getUpdateEntities().size(), 1);
assertEquals(entityResult.getUpdateEntities().get(0), tableId._getId());
response = getEntityDefinition(tableId._getId()); response = getEntityDefinition(tableId._getId());
definition = getEntityDefinition(response); definition = getEntityDefinition(response);
...@@ -732,7 +739,6 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -732,7 +739,6 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
Assert.assertTrue(refs.get(0).equalsContents(columns.get(0))); Assert.assertTrue(refs.get(0).equalsContents(columns.get(0)));
Assert.assertEquals(refs.get(0).get("dataType"), "int"); Assert.assertEquals(refs.get(0).get("dataType"), "int");
} }
@Test(dependsOnMethods = "testSubmitEntity") @Test(dependsOnMethods = "testSubmitEntity")
...@@ -765,9 +771,8 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -765,9 +771,8 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
// ATLAS-586: verify response entity can be parsed by GSON. // ATLAS-586: verify response entity can be parsed by GSON.
String entity = clientResponse.getEntity(String.class); String entity = clientResponse.getEntity(String.class);
Gson gson = new Gson(); Gson gson = new Gson();
UpdateEntitiesResponse updateEntitiesResponse = null;
try { try {
updateEntitiesResponse = gson.fromJson(entity, UpdateEntitiesResponse.class); UpdateEntitiesResponse updateEntitiesResponse = gson.fromJson(entity, UpdateEntitiesResponse.class);
} }
catch (JsonSyntaxException e) { catch (JsonSyntaxException e) {
Assert.fail("Response entity from " + service.path(ENTITIES).getURI() + " not parseable by GSON", e); Assert.fail("Response entity from " + service.path(ENTITIES).getURI() + " not parseable by GSON", e);
...@@ -785,7 +790,7 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -785,7 +790,7 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
private static class UpdateEntitiesResponse { private static class UpdateEntitiesResponse {
String requestId; String requestId;
String[] GUID; AtlasClient.EntityResult entities;
AtlasEntity definition; AtlasEntity definition;
} }
...@@ -811,15 +816,9 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -811,15 +816,9 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
queryParam(AtlasClient.GUID.toLowerCase(), db1Id._getId()). queryParam(AtlasClient.GUID.toLowerCase(), db1Id._getId()).
queryParam(AtlasClient.GUID.toLowerCase(), db2Id._getId()). queryParam(AtlasClient.GUID.toLowerCase(), db2Id._getId()).
accept(Servlets.JSON_MEDIA_TYPE).type(Servlets.JSON_MEDIA_TYPE).method(HttpMethod.DELETE, ClientResponse.class); accept(Servlets.JSON_MEDIA_TYPE).type(Servlets.JSON_MEDIA_TYPE).method(HttpMethod.DELETE, ClientResponse.class);
JSONObject response = getEntity(clientResponse); JSONObject response = getEntity(clientResponse);
final String deletedGuidsJson = response.getString(AtlasClient.GUID); List<String> deletedGuidsList = AtlasClient.EntityResult.fromString(response.toString()).getDeletedEntities();
Assert.assertNotNull(deletedGuidsJson);
JSONArray guidsArray = new JSONArray(deletedGuidsJson);
Assert.assertEquals(guidsArray.length(), 2);
List<String> deletedGuidsList = new ArrayList<>(2);
for (int index = 0; index < guidsArray.length(); index++) {
deletedGuidsList.add(guidsArray.getString(index));
}
Assert.assertTrue(deletedGuidsList.contains(db1Id._getId())); Assert.assertTrue(deletedGuidsList.contains(db1Id._getId()));
Assert.assertTrue(deletedGuidsList.contains(db2Id._getId())); Assert.assertTrue(deletedGuidsList.contains(db2Id._getId()));
...@@ -843,7 +842,8 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -843,7 +842,8 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
Id db2Id = createInstance(db2); Id db2Id = createInstance(db2);
// Delete the database entities // Delete the database entities
List<String> deletedGuidsList = serviceClient.deleteEntities(db1Id._getId(), db2Id._getId()); List<String> deletedGuidsList =
serviceClient.deleteEntities(db1Id._getId(), db2Id._getId()).getDeletedEntities();
// Verify that deleteEntities() response has database entity guids // Verify that deleteEntities() response has database entity guids
Assert.assertEquals(deletedGuidsList.size(), 2); Assert.assertEquals(deletedGuidsList.size(), 2);
...@@ -867,7 +867,7 @@ public class EntityJerseyResourceIT extends BaseResourceIT { ...@@ -867,7 +867,7 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
Id db1Id = createInstance(db1); Id db1Id = createInstance(db1);
// Delete the database entity // Delete the database entity
List<String> deletedGuidsList = serviceClient.deleteEntity(DATABASE_TYPE, "name", dbName); List<String> deletedGuidsList = serviceClient.deleteEntity(DATABASE_TYPE, "name", dbName).getDeletedEntities();
// Verify that deleteEntities() response has database entity guids // Verify that deleteEntities() response has database entity guids
Assert.assertEquals(deletedGuidsList.size(), 1); Assert.assertEquals(deletedGuidsList.size(), 1);
......
...@@ -23,21 +23,16 @@ import org.apache.atlas.ha.HAConfiguration; ...@@ -23,21 +23,16 @@ import org.apache.atlas.ha.HAConfiguration;
import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.Configuration;
import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.ACLProvider; import org.apache.curator.framework.api.ACLProvider;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.ACL;
import org.mockito.ArgumentMatcher; import org.mockito.ArgumentMatcher;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import static junit.framework.TestCase.assertEquals;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment