| /** |
| * 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.util; |
| |
| import static org.mockito.Matchers.any; |
| import static org.mockito.Matchers.eq; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.when; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.atlas.exception.AtlasBaseException; |
| import org.apache.atlas.model.typedef.AtlasEntityDef; |
| import org.apache.atlas.model.typedef.AtlasStructDef; |
| import org.apache.atlas.model.typedef.AtlasStructDef.AtlasAttributeDef; |
| import org.apache.atlas.model.typedef.AtlasTypesDef; |
| import org.apache.atlas.repository.Constants; |
| import org.apache.atlas.repository.converters.TypeConverterUtil; |
| import org.apache.atlas.repository.graphdb.AtlasVertex; |
| import org.apache.atlas.repository.store.graph.v1.AtlasGraphUtilsV1; |
| import org.apache.atlas.repository.store.graph.v1.AtlasStructDefStoreV1; |
| import org.apache.atlas.repository.store.graph.v1.AtlasTypeDefGraphStoreV1; |
| import org.apache.atlas.type.AtlasEntityType; |
| import org.apache.atlas.type.AtlasStructType; |
| import org.apache.atlas.type.AtlasStructType.AtlasAttribute; |
| import org.apache.atlas.type.AtlasType; |
| import org.apache.atlas.type.AtlasTypeRegistry; |
| import org.apache.atlas.type.AtlasTypeRegistry.AtlasTransientTypeRegistry; |
| import org.apache.atlas.typesystem.TypesDef; |
| import org.apache.atlas.typesystem.json.TypesSerialization; |
| import org.apache.atlas.typesystem.types.AttributeDefinition; |
| import org.apache.atlas.typesystem.types.ClassType; |
| import org.apache.atlas.typesystem.types.DataTypes; |
| import org.apache.atlas.typesystem.types.DataTypes.TypeCategory; |
| import org.apache.atlas.typesystem.types.EnumTypeDefinition; |
| import org.apache.atlas.typesystem.types.HierarchicalTypeDefinition; |
| import org.apache.atlas.typesystem.types.Multiplicity; |
| import org.apache.atlas.typesystem.types.StructTypeDefinition; |
| import org.apache.atlas.typesystem.types.TraitType; |
| import org.apache.atlas.typesystem.types.utils.TypesUtil; |
| import org.testng.Assert; |
| import org.testng.annotations.Test; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableSet; |
| |
| /** |
| * Validates that conversion from V1 to legacy types (and back) is consistent. This also tests |
| * that the conversion logic in AtlasStructDefStoreV1 is consistent with the conversion logic |
| * in RestUtils. This tests particularly focuses on composite attributes, since a defect was |
| * found in that area. |
| */ |
| public class RestUtilsTest { |
| |
| @Test(enabled=false) |
| // FIXME: On conversion back to V1, reverse attribute name |
| // "containingDatabase" |
| // in tables attribute in "database" type is lost. See ATLAS-1528. |
| public void testBidirectonalCompositeMappingConsistent() throws AtlasBaseException { |
| |
| HierarchicalTypeDefinition<ClassType> dbV1Type = TypesUtil.createClassTypeDef("database", |
| ImmutableSet.<String> of(), new AttributeDefinition("tables", DataTypes.arrayTypeName("table"), |
| Multiplicity.OPTIONAL, true, "containingDatabase")); |
| |
| HierarchicalTypeDefinition<ClassType> tableV1Type = TypesUtil.createClassTypeDef("table", |
| ImmutableSet.<String> of(), |
| new AttributeDefinition("containingDatabase", "database", Multiplicity.OPTIONAL, false, "tables")); |
| |
| testV1toV2toV1Conversion(Arrays.asList(dbV1Type, tableV1Type), new boolean[] { true, false }); |
| } |
| |
| @Test(enabled=false) |
| // FIXME: On conversion back to V1, reverse attribute name |
| // "containingDatabase" is lost |
| // in "table" attribute in "database". See ATLAS-1528. |
| public void testBidirectonalNonCompositeMappingConsistent() throws AtlasBaseException { |
| |
| HierarchicalTypeDefinition<ClassType> dbV1Type = TypesUtil.createClassTypeDef("database", |
| ImmutableSet.<String> of(), new AttributeDefinition("tables", DataTypes.arrayTypeName("table"), |
| Multiplicity.OPTIONAL, false, "containingDatabase")); |
| |
| HierarchicalTypeDefinition<ClassType> tableV1Type = TypesUtil.createClassTypeDef("table", |
| ImmutableSet.<String> of(), |
| new AttributeDefinition("containingDatabase", "database", Multiplicity.OPTIONAL, false, "tables")); |
| |
| testV1toV2toV1Conversion(Arrays.asList(dbV1Type, tableV1Type), new boolean[] { false, false }); |
| } |
| |
| private AtlasTypeDefGraphStoreV1 makeTypeStore(AtlasTypeRegistry reg) { |
| |
| AtlasTypeDefGraphStoreV1 result = mock(AtlasTypeDefGraphStoreV1.class); |
| |
| for (AtlasEntityType type : reg.getAllEntityTypes()) { |
| String typeName = type.getTypeName(); |
| AtlasVertex typeVertex = mock(AtlasVertex.class); |
| when(result.isTypeVertex(eq(typeVertex), any(TypeCategory.class))).thenReturn(true); |
| when(typeVertex.getProperty(eq(Constants.TYPE_CATEGORY_PROPERTY_KEY), eq(TypeCategory.class))) |
| .thenReturn(TypeCategory.CLASS); |
| |
| String attributeListPropertyKey = AtlasGraphUtilsV1.getTypeDefPropertyKey(typeName); |
| when(typeVertex.getProperty(eq(attributeListPropertyKey), eq(List.class))) |
| .thenReturn(new ArrayList<>(type.getAllAttributes().keySet())); |
| for (AtlasAttribute attribute : type.getAllAttributes().values()) { |
| String attributeDefPropertyKey = AtlasGraphUtilsV1.getTypeDefPropertyKey(typeName, attribute.getName()); |
| String attributeJson = AtlasStructDefStoreV1.toJsonFromAttribute(attribute); |
| when(typeVertex.getProperty(eq(attributeDefPropertyKey), eq(String.class))).thenReturn(attributeJson); |
| } |
| when(result.findTypeVertexByName(eq(typeName))).thenReturn(typeVertex); |
| } |
| return result; |
| } |
| |
| private AtlasAttributeDef convertToJsonAndBack(AtlasTypeRegistry registry, AtlasStructDef structDef, |
| AtlasAttributeDef attributeDef, boolean compositeExpected) throws AtlasBaseException { |
| |
| AtlasTypeDefGraphStoreV1 typeDefStore = makeTypeStore(registry); |
| AtlasStructType structType = (AtlasStructType) registry.getType(structDef.getName()); |
| AtlasAttribute attribute = structType.getAttribute(attributeDef.getName()); |
| String attribJson = AtlasStructDefStoreV1.toJsonFromAttribute(attribute); |
| |
| Map attrInfo = AtlasType.fromJson(attribJson, Map.class); |
| Assert.assertEquals(attrInfo.get("isComposite"), compositeExpected); |
| return AtlasStructDefStoreV1.toAttributeDefFromJson(structDef, attrInfo, typeDefStore); |
| } |
| |
| private void testV1toV2toV1Conversion(List<HierarchicalTypeDefinition<ClassType>> typesToTest, |
| boolean[] compositeExpected) throws AtlasBaseException { |
| |
| List<AtlasEntityDef> convertedEntityDefs = convertV1toV2(typesToTest); |
| |
| AtlasTypeRegistry registry = createRegistry(convertedEntityDefs); |
| for(int i = 0 ; i < convertedEntityDefs.size(); i++) { |
| AtlasEntityDef def = convertedEntityDefs.get(i); |
| for (AtlasAttributeDef attrDef : def.getAttributeDefs()) { |
| AtlasAttributeDef converted = convertToJsonAndBack(registry, def, attrDef, compositeExpected[i]); |
| Assert.assertEquals(converted, attrDef); |
| } |
| } |
| |
| List<HierarchicalTypeDefinition<ClassType>> convertedBackTypeDefs = convertV2toV1(convertedEntityDefs); |
| |
| for (int i = 0; i < typesToTest.size(); i++) { |
| |
| HierarchicalTypeDefinition<ClassType> convertedBack = convertedBackTypeDefs.get(i); |
| Assert.assertEquals(convertedBack, typesToTest.get(i)); |
| AttributeDefinition[] attributeDefinitions = convertedBack.attributeDefinitions; |
| if (attributeDefinitions.length > 0) { |
| Assert.assertEquals(attributeDefinitions[0].isComposite, compositeExpected[i]); |
| } |
| } |
| |
| } |
| |
| private List<HierarchicalTypeDefinition<ClassType>> convertV2toV1(List<AtlasEntityDef> toConvert) |
| throws AtlasBaseException { |
| |
| AtlasTypeRegistry reg = createRegistry(toConvert); |
| |
| List<HierarchicalTypeDefinition<ClassType>> result = new ArrayList<>(toConvert.size()); |
| for (int i = 0; i < toConvert.size(); i++) { |
| AtlasEntityDef entityDef = toConvert.get(i); |
| AtlasEntityType entity = reg.getEntityTypeByName(entityDef.getName()); |
| HierarchicalTypeDefinition<ClassType> converted = TypeConverterUtil.toTypesDef(entity, reg) |
| .classTypesAsJavaList().get(0); |
| result.add(converted); |
| } |
| return result; |
| } |
| |
| private AtlasTypeRegistry createRegistry(List<AtlasEntityDef> toConvert) throws AtlasBaseException { |
| AtlasTypeRegistry reg = new AtlasTypeRegistry(); |
| AtlasTransientTypeRegistry tmp = reg.lockTypeRegistryForUpdate(); |
| tmp.addTypes(toConvert); |
| reg.releaseTypeRegistryForUpdate(tmp, true); |
| return reg; |
| } |
| |
| private List<AtlasEntityDef> convertV1toV2(List<HierarchicalTypeDefinition<ClassType>> types) |
| throws AtlasBaseException { |
| |
| ImmutableList<HierarchicalTypeDefinition<ClassType>> classTypeList = ImmutableList |
| .<HierarchicalTypeDefinition<ClassType>> builder().addAll(types).build(); |
| |
| TypesDef toConvert = TypesUtil.getTypesDef(ImmutableList.<EnumTypeDefinition> of(), |
| ImmutableList.<StructTypeDefinition> of(), ImmutableList.<HierarchicalTypeDefinition<TraitType>> of(), |
| classTypeList); |
| |
| String json = TypesSerialization.toJson(toConvert); |
| AtlasTypeRegistry emptyRegistry = new AtlasTypeRegistry(); |
| AtlasTypesDef converted = TypeConverterUtil.toAtlasTypesDef(json, emptyRegistry); |
| List<AtlasEntityDef> convertedEntityDefs = converted.getEntityDefs(); |
| return convertedEntityDefs; |
| } |
| } |