public static void updateVertexPreCreate(
      AtlasStructDef structDef,
      AtlasStructType structType,
      AtlasVertex vertex,
      AtlasTypeDefGraphStoreV1 typeDefStore) {
    List<String> attrNames = new ArrayList<>(structDef.getAttributeDefs().size());

    for (AtlasAttributeDef attributeDef : structDef.getAttributeDefs()) {
      String propertyKey = AtlasGraphUtilsV1.getPropertyKey(structDef, attributeDef.getName());

      AtlasGraphUtilsV1.setProperty(
          vertex, propertyKey, toJsonFromAttributeDef(attributeDef, structType));

      attrNames.add(attributeDef.getName());
    }
    AtlasGraphUtilsV1.setProperty(vertex, AtlasGraphUtilsV1.getPropertyKey(structDef), attrNames);
  }
  private static void addReferencesForAttribute(
      AtlasVertex vertex, AtlasAttributeDef attributeDef, AtlasTypeDefGraphStoreV1 typeDefStore)
      throws AtlasBaseException {
    Set<String> referencedTypeNames =
        AtlasTypeUtil.getReferencedTypeNames(attributeDef.getTypeName());

    String typeName = vertex.getProperty(Constants.TYPENAME_PROPERTY_KEY, String.class);

    for (String referencedTypeName : referencedTypeNames) {
      if (!AtlasTypeUtil.isBuiltInType(referencedTypeName)) {
        AtlasVertex referencedTypeVertex = typeDefStore.findTypeVertexByName(referencedTypeName);

        if (referencedTypeVertex == null) {
          throw new AtlasBaseException(
              AtlasErrorCode.UNKNOWN_TYPE, referencedTypeName, typeName, attributeDef.getName());
        }

        String label = AtlasGraphUtilsV1.getEdgeLabel(typeName, attributeDef.getName());

        typeDefStore.getOrCreateEdge(vertex, referencedTypeVertex, label);
      }
    }
  }
  public static void updateVertexPreUpdate(
      AtlasStructDef structDef,
      AtlasStructType structType,
      AtlasVertex vertex,
      AtlasTypeDefGraphStoreV1 typeDefStore)
      throws AtlasBaseException {

    List<String> attrNames = new ArrayList<>();
    if (CollectionUtils.isNotEmpty(structDef.getAttributeDefs())) {
      for (AtlasAttributeDef attributeDef : structDef.getAttributeDefs()) {
        attrNames.add(attributeDef.getName());
      }
    }

    List<String> currAttrNames =
        vertex.getProperty(AtlasGraphUtilsV1.getPropertyKey(structDef), List.class);

    // delete attributes that are not present in updated structDef
    if (CollectionUtils.isNotEmpty(currAttrNames)) {
      for (String currAttrName : currAttrNames) {
        if (!attrNames.contains(currAttrName)) {
          throw new AtlasBaseException(
              AtlasErrorCode.ATTRIBUTE_DELETION_NOT_SUPPORTED, structDef.getName(), currAttrName);
        }
      }
    }

    typeDefStore.updateTypeVertex(structDef, vertex);

    // add/update attributes that are present in updated structDef
    if (CollectionUtils.isNotEmpty(structDef.getAttributeDefs())) {
      for (AtlasAttributeDef attributeDef : structDef.getAttributeDefs()) {
        if (CollectionUtils.isEmpty(currAttrNames)
            || !currAttrNames.contains(attributeDef.getName())) {
          // new attribute - only allow if optional
          if (!attributeDef.isOptional()) {
            throw new AtlasBaseException(
                AtlasErrorCode.CANNOT_ADD_MANDATORY_ATTRIBUTE,
                structDef.getName(),
                attributeDef.getName());
          }
        }

        String propertyKey = AtlasGraphUtilsV1.getPropertyKey(structDef, attributeDef.getName());

        AtlasGraphUtilsV1.setProperty(
            vertex, propertyKey, toJsonFromAttributeDef(attributeDef, structType));
      }
    }

    AtlasGraphUtilsV1.setProperty(vertex, AtlasGraphUtilsV1.getPropertyKey(structDef), attrNames);
  }
  private static AtlasAttributeDef toAttributeDefFromJson(
      AtlasStructDef structDef, Map attribInfo, AtlasTypeDefGraphStoreV1 typeDefStore)
      throws AtlasBaseException {
    AtlasAttributeDef ret = new AtlasAttributeDef();

    ret.setName((String) attribInfo.get("name"));
    ret.setTypeName((String) attribInfo.get("dataType"));
    ret.setUnique((Boolean) attribInfo.get("isUnique"));
    ret.setIndexable((Boolean) attribInfo.get("isIndexable"));

    String attrTypeName = ret.getTypeName();

    if (AtlasTypeUtil.isArrayType(attrTypeName)) {
      Set<String> typeNames = AtlasTypeUtil.getReferencedTypeNames(ret.getTypeName());

      if (typeNames.size() > 0) {
        attrTypeName = typeNames.iterator().next();
      }
    }

    if (!AtlasTypeUtil.isBuiltInType(attrTypeName)) {
      AtlasVertex attributeType = typeDefStore.findTypeVertexByName(attrTypeName);

      // check for isComposite/reverseAttributeName for entity types
      if (attributeType != null && typeDefStore.isTypeVertex(attributeType, TypeCategory.CLASS)) {
        String reverseAttribName = (String) attribInfo.get("reverseAttributeName");
        Boolean isComposite = (Boolean) attribInfo.get("isComposite");

        if (StringUtils.isNotBlank(reverseAttribName) || isComposite) {
          if (AtlasTypeUtil.isMapType(attrTypeName)) {
            throw new AtlasBaseException(
                AtlasErrorCode.CONSTRAINT_NOT_SUPPORTED_ON_MAP_TYPE,
                structDef.getName(),
                ret.getName(),
                attrTypeName);
          }

          String refAttributeName = null;
          List<String> attrNames =
              attributeType.getProperty(AtlasGraphUtilsV1.getPropertyKey(attrTypeName), List.class);

          if (CollectionUtils.isNotEmpty(attrNames)) {
            for (String attrName : attrNames) {
              String attribJson =
                  attributeType.getProperty(
                      AtlasGraphUtilsV1.getPropertyKey(attrTypeName, attrName), String.class);

              Map refAttrInfo = AtlasType.fromJson(attribJson, Map.class);
              String refAttribType = (String) refAttrInfo.get("dataType");
              String refAttribRevAttribName = (String) refAttrInfo.get("reverseAttributeName");

              if (StringUtils.equals(refAttribType, structDef.getName())
                  && StringUtils.equals(refAttribRevAttribName, ret.getName())) {
                refAttributeName = (String) refAttrInfo.get("name");

                break;
              }
            }
          }

          if (isComposite) {
            if (StringUtils.isNotBlank(
                refAttributeName)) { // ex: hive_table.columns, hive_column.table
              Map<String, Object> params = new HashMap<>();
              params.put(AtlasConstraintDef.CONSTRAINT_PARAM_REF_ATTRIBUTE, refAttributeName);

              ret.addConstraint(new AtlasConstraintDef(CONSTRAINT_TYPE_MAPPED_FROM_REF, params));
            } else { // ex: hive_table.partitionKeys, with no reverseAttribute-reference
              ret.addConstraint(new AtlasConstraintDef(CONSTRAINT_TYPE_FOREIGN_KEY));
            }
          }

          if (StringUtils.isNotBlank(reverseAttribName)) { // ex: hive_column.table
            Map<String, Object> params = new HashMap<>();
            params.put(CONSTRAINT_PARAM_ON_DELETE, CONSTRAINT_PARAM_VAL_CASCADE);

            ret.addConstraint(new AtlasConstraintDef(CONSTRAINT_TYPE_FOREIGN_KEY, params));
          }
        }
      }
    }

    Map multiplicity = AtlasType.fromJson((String) attribInfo.get("multiplicity"), Map.class);
    Number minCount = (Number) multiplicity.get("lower");
    Number maxCount = (Number) multiplicity.get("upper");
    Boolean isUnique = (Boolean) multiplicity.get("isUnique");

    if (minCount == null || minCount.intValue() == 0) {
      ret.setOptional(true);
      ret.setValuesMinCount(0);
    } else {
      ret.setOptional(false);
      ret.setValuesMinCount(minCount.intValue());
    }

    if (maxCount == null || maxCount.intValue() < 2) {
      ret.setCardinality(AtlasAttributeDef.Cardinality.SINGLE);
      ret.setValuesMaxCount(1);
    } else {
      if (isUnique == null || isUnique == Boolean.FALSE) {
        ret.setCardinality(AtlasAttributeDef.Cardinality.LIST);
      } else {
        ret.setCardinality(AtlasAttributeDef.Cardinality.SET);
      }

      ret.setValuesMaxCount(maxCount.intValue());
    }

    return ret;
  }
  private static String toJsonFromAttributeDef(
      AtlasAttributeDef attributeDef, AtlasStructType structType) {
    boolean isForeignKey = structType.isForeignKeyAttribute(attributeDef.getName());
    boolean isMappedFromRef = structType.isMappedFromRefAttribute(attributeDef.getName());
    String reverseAttribName = null;

    if (isForeignKey) { // check if the referenced entity has foreignKeyRef to this attribute
      AtlasType attribType = structType.getAttributeType(attributeDef.getName());

      if (attribType.getTypeCategory() == AtlasType.TypeCategory.ARRAY) {
        attribType = ((AtlasArrayType) attribType).getElementType();
      }

      if (attribType.getTypeCategory() == AtlasType.TypeCategory.ENTITY) {
        reverseAttribName =
            ((AtlasStructType) attribType)
                .getMappedFromRefAttribute(structType.getTypeName(), attributeDef.getName());
      }
    }

    boolean isComposite =
        isMappedFromRef || (isForeignKey && StringUtils.isBlank(reverseAttribName));

    Map<String, Object> attribInfo = new HashMap<>();

    attribInfo.put("name", attributeDef.getName());
    attribInfo.put("dataType", attributeDef.getTypeName());
    attribInfo.put("isUnique", attributeDef.isUnique());
    attribInfo.put("isIndexable", attributeDef.isIndexable());
    attribInfo.put("isComposite", isComposite);
    attribInfo.put("reverseAttributeName", reverseAttribName);
    Map<String, Object> multiplicity = new HashMap<>();
    multiplicity.put("lower", attributeDef.getValuesMinCount());
    multiplicity.put("upper", attributeDef.getValuesMaxCount());
    multiplicity.put(
        "isUnique", AtlasAttributeDef.Cardinality.SET.equals(attributeDef.getCardinality()));

    attribInfo.put("multiplicity", AtlasType.toJson(multiplicity));

    return AtlasType.toJson(attribInfo);
  }