diff --git a/src/main/java/io/github/oemergenc/hbase/orm/extensions/HBDynamicColumn.java b/src/main/java/io/github/oemergenc/hbase/orm/extensions/HBDynamicColumn.java index 4d5b2e9..e3cce37 100644 --- a/src/main/java/io/github/oemergenc/hbase/orm/extensions/HBDynamicColumn.java +++ b/src/main/java/io/github/oemergenc/hbase/orm/extensions/HBDynamicColumn.java @@ -30,14 +30,16 @@ DynamicQualifier qualifier(); /** - * alias to be used as prefix in the column name. If omitted the value of qualifierField will be used + * alias to be used as prefix in the column name. If not explicitly set no prefix will be set at all and the + * dynamic column family cannot contain any other dynamic field. In this case defining another dynamic + * column will throw an exception. * * @return alias as prefix of the resulting column name */ - String alias(); + String alias() default ""; /** - * Optional separator between the alias and the qualifierField value + * Optional separator between the alias and the qualifierField value. Becomes obsolete if no alias is set explicitly * * @return separator */ diff --git a/src/main/java/io/github/oemergenc/hbase/orm/extensions/HBDynamicColumnObjectMapper.java b/src/main/java/io/github/oemergenc/hbase/orm/extensions/HBDynamicColumnObjectMapper.java index ffa80de..07780db 100644 --- a/src/main/java/io/github/oemergenc/hbase/orm/extensions/HBDynamicColumnObjectMapper.java +++ b/src/main/java/io/github/oemergenc/hbase/orm/extensions/HBDynamicColumnObjectMapper.java @@ -10,6 +10,8 @@ import com.flipkart.hbaseobjectmapper.exceptions.CodecException; import com.flipkart.hbaseobjectmapper.exceptions.RowKeyCantBeComposedException; import com.flipkart.hbaseobjectmapper.exceptions.RowKeyCantBeEmptyException; +import io.github.oemergenc.hbase.orm.extensions.dynamic.processor.alias.AliasHandler; +import io.github.oemergenc.hbase.orm.extensions.dynamic.processor.alias.DynamicAliasFactory; import io.github.oemergenc.hbase.orm.extensions.dynamic.validator.HBDynamicColumnRecordValidator; import io.github.oemergenc.hbase.orm.extensions.exception.InvalidColumnQualifierFieldException; import io.github.oemergenc.hbase.orm.extensions.exception.InvalidDynamicListEntryException; @@ -39,7 +41,6 @@ import java.util.stream.Collectors; import static java.util.Collections.emptyMap; -import static java.util.function.Predicate.not; @Slf4j public class HBDynamicColumnObjectMapper extends HBObjectMapper { @@ -57,6 +58,10 @@ public HBDynamicColumnObjectMapper() { this(CODEC); } + public static T safeCast(Object o, Class clazz) { + return clazz != null && clazz.isInstance(o) ? clazz.cast(o) : null; + } + public , T extends HBRecord> Get getAsGet(Class hbRecordClazz, byte[] rowKey, String family, List qualifierParts) { HBDynamicColumnRecordValidator.validateQualifierParts(qualifierParts); @@ -126,24 +131,17 @@ void convertMapToRecord(NavigableMap { - Serializable serializable = byteArrayToValue(navigableMapEntry.getKey()); - return serializable.toString().startsWith(prefix); - }).collect(Collectors.toList()); + val collect = aliasHandler.getDynamicListFieldEntries(navigableMapNavigableMap); List genericValues = new ArrayList<>(); for (val nav : collect) { val value = nav.getValue(); Collection values = value.values(); for (val theVal : values) { - byteArrayToGenericObject(genericObjectType, theVal) - .ifPresent(genericValues::add); + byteArrayToGenericObject(genericObjectType, theVal).ifPresent(genericValues::add); } } MIRROR.on(record).set().field(dynamicField).withValue(genericValues); @@ -202,9 +200,7 @@ private Map> processDynamicField(Field dynamicFi HBDynamicColumn hbDynamicColumn = MIRROR.on(hbRecordClazz).reflect() .annotation(HBDynamicColumn.class).atField(dynamicField.getName()); String family = hbDynamicColumn.family(); - String alias = Optional.of(hbDynamicColumn.alias()).filter(not(String::isBlank)).orElse(family); - String separator = hbDynamicColumn.separator(); - String prefix = alias.concat(separator); + AliasHandler aliasHandler = DynamicAliasFactory.getHandler(hbDynamicColumn, codec); DynamicQualifier dynamicQualifier = hbDynamicColumn.qualifier(); val familyToQualifierMap = new HashMap>(); Object field = MIRROR.on(record).get().field(dynamicField); @@ -212,7 +208,7 @@ private Map> processDynamicField(Field dynamicFi if (field instanceof List) { List dynamicList = safeCast(field, List.class); if (!dynamicList.isEmpty()) { - val columnQualifierToColumnValueMap = processDynamicListField(family, prefix, dynamicList, dynamicQualifier); + val columnQualifierToColumnValueMap = processDynamicListField(family, aliasHandler, dynamicList, dynamicQualifier); familyToQualifierMap.put(family, columnQualifierToColumnValueMap); } else { log.trace("A dynamic list was empty and will be ignored"); @@ -225,14 +221,14 @@ private Map> processDynamicField(Field dynamicFi } private HashMap processDynamicListField(String family, - String prefix, + AliasHandler aliasHandler, List dynamicList, DynamicQualifier dynamicQualifier) { val columnQualifierToEntryMap = new HashMap(); for (Object dynamicListEntry : dynamicList) { try { String dynamicListFieldEntryColumnName = processDynamicListFieldEntry(dynamicListEntry, dynamicQualifier); - dynamicListFieldEntryColumnName = prefix.concat(dynamicListFieldEntryColumnName); + dynamicListFieldEntryColumnName = aliasHandler.getDynamicListFieldEntryColumnName(dynamicListFieldEntryColumnName); columnQualifierToEntryMap.put(dynamicListFieldEntryColumnName, dynamicListEntry); } catch (InvalidColumnQualifierFieldException ex) { log.error("Invalid part of dynamic value for list entry with dynamic qualifier {}. Entry will be ignored.", dynamicQualifier, ex); @@ -313,10 +309,6 @@ private Optional byteArrayToGenericObject(Type genericObjectType, return Optional.empty(); } - public static T safeCast(Object o, Class clazz) { - return clazz != null && clazz.isInstance(o) ? clazz.cast(o) : null; - } - public > void validate(Class hbRecordClazz) { HBDynamicColumnRecordValidator.validate(hbRecordClazz); } diff --git a/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/processor/DynamicListProcessor.java b/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/processor/DynamicListProcessor.java deleted file mode 100644 index 04bed74..0000000 --- a/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/processor/DynamicListProcessor.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.oemergenc.hbase.orm.extensions.dynamic.processor; - -public class DynamicListProcessor implements Proccessor { - @Override - public void process() { - - } -} diff --git a/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/processor/Proccessor.java b/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/processor/Proccessor.java deleted file mode 100644 index 5d60756..0000000 --- a/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/processor/Proccessor.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.github.oemergenc.hbase.orm.extensions.dynamic.processor; - -public interface Proccessor { - void process(); -} diff --git a/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/processor/alias/AliasHandler.java b/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/processor/alias/AliasHandler.java new file mode 100644 index 0000000..307f68e --- /dev/null +++ b/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/processor/alias/AliasHandler.java @@ -0,0 +1,11 @@ +package io.github.oemergenc.hbase.orm.extensions.dynamic.processor.alias; + +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; + +public interface AliasHandler { + String getDynamicListFieldEntryColumnName(String dynamicListFieldEntryColumnName); + + List>> getDynamicListFieldEntries(NavigableMap> navigableMapNavigableMap); +} diff --git a/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/processor/alias/DynamicAliasFactory.java b/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/processor/alias/DynamicAliasFactory.java new file mode 100644 index 0000000..1cc195d --- /dev/null +++ b/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/processor/alias/DynamicAliasFactory.java @@ -0,0 +1,18 @@ +package io.github.oemergenc.hbase.orm.extensions.dynamic.processor.alias; + +import com.flipkart.hbaseobjectmapper.codec.Codec; +import io.github.oemergenc.hbase.orm.extensions.HBDynamicColumn; +import org.apache.hadoop.hbase.shaded.org.apache.commons.lang3.StringUtils; + +public class DynamicAliasFactory { + + public static AliasHandler getHandler(HBDynamicColumn column, Codec codec) { + String alias = column.alias(); + String separator = column.separator(); + if (StringUtils.isNotBlank(alias)) { + return new DynamicAliasHandler(alias, separator, codec); + } else { + return new DynamicNoAliasHandler(); + } + } +} diff --git a/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/processor/alias/DynamicAliasHandler.java b/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/processor/alias/DynamicAliasHandler.java new file mode 100644 index 0000000..d180a23 --- /dev/null +++ b/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/processor/alias/DynamicAliasHandler.java @@ -0,0 +1,51 @@ +package io.github.oemergenc.hbase.orm.extensions.dynamic.processor.alias; + +import com.flipkart.hbaseobjectmapper.codec.Codec; +import com.flipkart.hbaseobjectmapper.codec.exceptions.DeserializationException; +import com.flipkart.hbaseobjectmapper.exceptions.CodecException; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; +import java.util.stream.Collectors; + +import static java.util.Collections.emptyMap; + +public class DynamicAliasHandler implements AliasHandler { + private final String alias; + private final String prefix; + private String separator; + private Codec codec; + + public DynamicAliasHandler(String alias, + String separator, + Codec codec) { + this.alias = alias; + this.separator = separator; + this.codec = codec; + this.prefix = alias.concat(separator); + } + + @Override + public String getDynamicListFieldEntryColumnName(String dynamicListFieldEntryColumnName) { + return prefix.concat(dynamicListFieldEntryColumnName); + } + + @Override + public List>> getDynamicListFieldEntries(NavigableMap> navigableMapNavigableMap) { + return navigableMapNavigableMap.entrySet().stream() + .filter(navigableMapEntry -> { + Serializable serializable = byteArrayToValue(navigableMapEntry.getKey()); + return serializable.toString().startsWith(prefix); + }).collect(Collectors.toList()); + } + + Serializable byteArrayToValue(byte[] value) { + try { + return codec.deserialize(value, String.class, emptyMap()); + } catch (DeserializationException e) { + throw new CodecException("Couldn't deserialize", e); + } + } +} diff --git a/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/processor/alias/DynamicNoAliasHandler.java b/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/processor/alias/DynamicNoAliasHandler.java new file mode 100644 index 0000000..6df1e0e --- /dev/null +++ b/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/processor/alias/DynamicNoAliasHandler.java @@ -0,0 +1,19 @@ +package io.github.oemergenc.hbase.orm.extensions.dynamic.processor.alias; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; + +public class DynamicNoAliasHandler implements AliasHandler { + + @Override + public String getDynamicListFieldEntryColumnName(String dynamicListFieldEntryColumnName) { + return dynamicListFieldEntryColumnName; + } + + @Override + public List>> getDynamicListFieldEntries(NavigableMap> navigableMapNavigableMap) { + return new ArrayList<>(navigableMapNavigableMap.entrySet()); + } +} diff --git a/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/validator/HBDynamicColumnRecordValidator.java b/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/validator/HBDynamicColumnRecordValidator.java index bccf102..c51b6fc 100644 --- a/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/validator/HBDynamicColumnRecordValidator.java +++ b/src/main/java/io/github/oemergenc/hbase/orm/extensions/dynamic/validator/HBDynamicColumnRecordValidator.java @@ -6,9 +6,13 @@ import com.flipkart.hbaseobjectmapper.HBTable; import io.github.oemergenc.hbase.orm.extensions.HBDynamicColumn; import io.github.oemergenc.hbase.orm.extensions.exception.DuplicateColumnIdentifierException; +import io.github.oemergenc.hbase.orm.extensions.exception.InvalidNoAliasColumnQualifierFieldException; import io.github.oemergenc.hbase.orm.extensions.exception.MissingHbTableAnnotationForFamilyException; +import lombok.extern.slf4j.Slf4j; import lombok.val; import net.vidageek.mirror.dsl.Mirror; +import net.vidageek.mirror.list.dsl.MirrorList; +import org.apache.hadoop.hbase.shaded.org.apache.commons.lang3.StringUtils; import org.apache.hbase.thirdparty.io.netty.util.internal.StringUtil; import java.lang.reflect.Field; @@ -19,9 +23,11 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; +@Slf4j public class HBDynamicColumnRecordValidator { private static Mirror MIRROR = new Mirror(); @@ -34,6 +40,7 @@ static public > void validate(Class hbRecordClazz) { .matching(element -> element.getAnnotation(HBDynamicColumn.class) != null); List columnPrefixList = new ArrayList<>(); + Set validNoAliasDynamicFields = validateNoAliasDynamicFields(hbRecordClazz, hbDynamicColumnFields); for (Field dynamicField : hbDynamicColumnFields) { HBDynamicColumn hbDynamicColumn = MIRROR.on(hbRecordClazz).reflect() .annotation(HBDynamicColumn.class).atField(dynamicField.getName()); @@ -47,13 +54,47 @@ static public > void validate(Class hbRecordClazz) { validateQualifierField(dynamicField, qualifier); validateQualifierParts(Arrays.asList(qualifier.parts())); validateFamily(family, tableFamilyNames); - validateAlias(alias); - validateSeparator(separator); + if (!validNoAliasDynamicFields.contains(family)) { + validateAlias(alias); + validateSeparator(separator); + } columnPrefixList.add(family.concat(separator).concat(alias)); } validateNoDuplicatePrefix(columnPrefixList); } + private static > Set validateNoAliasDynamicFields(Class hbRecordClazz, + MirrorList hbDynamicColumnFields) { + val hbDynamicColumns = hbDynamicColumnFields.stream() + .map(dynamicField -> MIRROR.on(hbRecordClazz).reflect() + .annotation(HBDynamicColumn.class).atField(dynamicField.getName())) + .collect(Collectors.toList()); + + val noAliasColumns = hbDynamicColumns + .stream() + .filter(column -> StringUtils.isBlank(column.alias())) + .map(HBDynamicColumn::family) + .collect(Collectors.toList()); + + Map> noAliasColumnsSummedByFamily = hbDynamicColumns + .stream() + .filter(column -> noAliasColumns.contains(column.family())) + .collect(Collectors.groupingBy(HBDynamicColumn::family)); + + val dynamicColumnsWithMoreThanOneNoAliasField = noAliasColumnsSummedByFamily + .entrySet() + .stream() + .filter(stringListEntry -> stringListEntry.getValue().size() > 1) + .collect(Collectors.toList()); + + if (!dynamicColumnsWithMoreThanOneNoAliasField.isEmpty()) { + dynamicColumnsWithMoreThanOneNoAliasField + .forEach(key -> log.error("The definition of the dynamic column for family {} is invalid. Make sure a no alias dynamic column family is not used more than once", key)); + throw new InvalidNoAliasColumnQualifierFieldException(); + } + return noAliasColumnsSummedByFamily.keySet(); + } + static public void validateNoDuplicatePrefix(List columnPrefixList) { val duplicates = columnPrefixList.stream().collect(Collectors.groupingBy(Function.identity())) .entrySet() diff --git a/src/main/java/io/github/oemergenc/hbase/orm/extensions/exception/InvalidNoAliasColumnQualifierFieldException.java b/src/main/java/io/github/oemergenc/hbase/orm/extensions/exception/InvalidNoAliasColumnQualifierFieldException.java new file mode 100644 index 0000000..d5d5ded --- /dev/null +++ b/src/main/java/io/github/oemergenc/hbase/orm/extensions/exception/InvalidNoAliasColumnQualifierFieldException.java @@ -0,0 +1,8 @@ +package io.github.oemergenc.hbase.orm.extensions.exception; + +public class InvalidNoAliasColumnQualifierFieldException extends IllegalArgumentException { + + public InvalidNoAliasColumnQualifierFieldException() { + super(String.format("The definition of a dynamic column without an alias is invalid. Dynamic columns families without an alias can only have exactly one column")); + } +} diff --git a/src/test/groovy/io/github/oemergenc/hbase/orm/extensions/componenttests/AbstractComponentSpec.groovy b/src/test/groovy/io/github/oemergenc/hbase/orm/extensions/componenttests/AbstractComponentSpec.groovy index 9bc2248..c435c4f 100644 --- a/src/test/groovy/io/github/oemergenc/hbase/orm/extensions/componenttests/AbstractComponentSpec.groovy +++ b/src/test/groovy/io/github/oemergenc/hbase/orm/extensions/componenttests/AbstractComponentSpec.groovy @@ -36,6 +36,9 @@ abstract class AbstractComponentSpec extends Specification { bigTableHelper.createTable("bd_omm_prp_campaigns", ["campaigns", "days", "optional"]) bigTableHelper.createTable("multiple_dynamic_columns_table", ["staticFamily", "dynamicFamily1", "dynamicFamily2", "dynamicFamily3", "dynamicFamily4"]) bigTableHelper.createTable("multiple_dynamic_columns_table_with_same_family", ["staticFamily", "dynamicFamily1"]) + bigTableHelper.createTable("no_alias_column_table", ["staticFamily", "noAliasDynamicFamily"]) + bigTableHelper.createTable("no_alias_mixed_column_table", ["staticFamily", "noAliasDynamicFamily", "dynamicFamily"]) + } def setupSpec() { diff --git a/src/test/groovy/io/github/oemergenc/hbase/orm/extensions/componenttests/NoAliasDynamicColumnsDaoComponentTest.groovy b/src/test/groovy/io/github/oemergenc/hbase/orm/extensions/componenttests/NoAliasDynamicColumnsDaoComponentTest.groovy new file mode 100644 index 0000000..601da2c --- /dev/null +++ b/src/test/groovy/io/github/oemergenc/hbase/orm/extensions/componenttests/NoAliasDynamicColumnsDaoComponentTest.groovy @@ -0,0 +1,38 @@ +package io.github.oemergenc.hbase.orm.extensions.componenttests + + +import io.github.oemergenc.hbase.orm.extensions.dao.NoAliasDynamicColumsDao +import io.github.oemergenc.hbase.orm.extensions.domain.dto.DependentWithMap +import io.github.oemergenc.hbase.orm.extensions.domain.records.NoAliasHBDynamicColumnRecord + +class NoAliasDynamicColumnsDaoComponentTest extends AbstractComponentSpec { + def dao = new NoAliasDynamicColumsDao(bigTableHelper.connect()) + + def "Store and reading dynamic column works"() { + given: + def expectedRowKey = "theRowKey" + def dynamicValues1 = [ + new DependentWithMap("dv1_dp1_1", ["k1": "v1"]), + new DependentWithMap("dv1_dp1_2", ["k2": "v2"]), + ] + + def record = new NoAliasHBDynamicColumnRecord(expectedRowKey, dynamicValues1) + + when: + def rowKey = dao.persist(record) + + then: + rowKey == expectedRowKey + + when: + def recordResult = dao.get(expectedRowKey) + + then: + recordResult + recordResult.staticId == rowKey + + and: + recordResult.noAliasDynamicFamily.collect { it.dynamicId }.containsAll(["dv1_dp1_1", "dv1_dp1_2"]) + recordResult.noAliasDynamicFamily.collect { it.products }.containsAll(["k1": "v1"], ["k2": "v2"]) + } +} diff --git a/src/test/groovy/io/github/oemergenc/hbase/orm/extensions/componenttests/NoAliasMixedDynamicColumnsDaoComponentTest.groovy b/src/test/groovy/io/github/oemergenc/hbase/orm/extensions/componenttests/NoAliasMixedDynamicColumnsDaoComponentTest.groovy new file mode 100644 index 0000000..50c8dc3 --- /dev/null +++ b/src/test/groovy/io/github/oemergenc/hbase/orm/extensions/componenttests/NoAliasMixedDynamicColumnsDaoComponentTest.groovy @@ -0,0 +1,49 @@ +package io.github.oemergenc.hbase.orm.extensions.componenttests + +import io.github.oemergenc.hbase.orm.extensions.dao.NoAliasMixedDynamicColumsDao +import io.github.oemergenc.hbase.orm.extensions.domain.dto.DependentWithMap +import io.github.oemergenc.hbase.orm.extensions.domain.dto.DependentWithPrimitiveTypes +import io.github.oemergenc.hbase.orm.extensions.domain.records.NoAliasMixedHBDynamicColumnRecord + +class NoAliasMixedDynamicColumnsDaoComponentTest extends AbstractComponentSpec { + def dao = new NoAliasMixedDynamicColumsDao(bigTableHelper.connect()) + + def "Store and reading dynamic column with no alias column works"() { + given: + def expectedRowKey = "theRowKey" + def dynamicValues1 = [ + new DependentWithMap("dv1_dp1_1", ["k1": "v1"]), + new DependentWithMap("dv1_dp1_2", ["k2": "v2"]), + ] + def dynamicValues2 = [ + new DependentWithPrimitiveTypes("dv1_dp1_1", "dv1_dp2_1", "dv1_position_1", "dv1_recipeId_1", "dv1_html_1"), + new DependentWithPrimitiveTypes("dv1_dp1_2", "dv1_dp2_2", "dv1_position_2", "dv1_recipeId_2", "dv1_html_2") + ] + + def record = new NoAliasMixedHBDynamicColumnRecord(expectedRowKey, dynamicValues1, dynamicValues2) + + when: + def rowKey = dao.persist(record) + + then: + rowKey == expectedRowKey + + when: + def recordResult = dao.get(expectedRowKey) + + then: + recordResult + recordResult.staticId == rowKey + + and: + recordResult.noAliasDynamicFamily.collect { it.dynamicId }.containsAll(["dv1_dp1_1", "dv1_dp1_2"]) + recordResult.noAliasDynamicFamily.collect { it.products }.containsAll(["k1": "v1"], ["k2": "v2"]) + + and: + recordResult.dynamicValues.collect { it.dynamicPart1 }.containsAll(["dv1_dp1_1", "dv1_dp1_2"]) + recordResult.dynamicValues.collect { it.dynamicPart2 }.containsAll(["dv1_dp2_1", "dv1_dp2_2"]) + recordResult.dynamicValues.collect { it.position }.containsAll(["dv1_position_1", "dv1_position_2"]) + recordResult.dynamicValues.collect { it.recipeId }.containsAll(["dv1_recipeId_1", "dv1_recipeId_2"]) + recordResult.dynamicValues.collect { it.html }.containsAll(["dv1_html_1", "dv1_html_2"]) + } +} diff --git a/src/test/groovy/io/github/oemergenc/hbase/orm/extensions/mapper/InvalidNoAliasHBDynamicColumnRecordSpec.groovy b/src/test/groovy/io/github/oemergenc/hbase/orm/extensions/mapper/InvalidNoAliasHBDynamicColumnRecordSpec.groovy new file mode 100644 index 0000000..619efca --- /dev/null +++ b/src/test/groovy/io/github/oemergenc/hbase/orm/extensions/mapper/InvalidNoAliasHBDynamicColumnRecordSpec.groovy @@ -0,0 +1,41 @@ +package io.github.oemergenc.hbase.orm.extensions.mapper + +import io.github.oemergenc.hbase.orm.extensions.HBDynamicColumnObjectMapper +import io.github.oemergenc.hbase.orm.extensions.domain.invalid.InvalidNoAliasHBDynamicColumnRecord +import io.github.oemergenc.hbase.orm.extensions.domain.invalid.InvalidNoAliasMixedHBDynamicColumnRecord +import io.github.oemergenc.hbase.orm.extensions.domain.records.NoAliasHBDynamicColumnRecord +import io.github.oemergenc.hbase.orm.extensions.exception.InvalidNoAliasColumnQualifierFieldException +import spock.lang.Specification +import spock.lang.Unroll + +class InvalidNoAliasHBDynamicColumnRecordSpec extends Specification { + def mapper = new HBDynamicColumnObjectMapper() + + @Unroll + def "should allow one dynamic entry without alias on a single dynamic column"() { + when: + mapper.validate(clazz) + + then: + noExceptionThrown() + + where: + clazz | _ + NoAliasHBDynamicColumnRecord.class | _ + } + + @Unroll + def "should only allow one dynamic entry without alias on a single dynamic column"() { + when: + mapper.validate(clazz) + + then: + thrown(InvalidNoAliasColumnQualifierFieldException) + + where: + clazz | _ + InvalidNoAliasHBDynamicColumnRecord.class | _ + InvalidNoAliasMixedHBDynamicColumnRecord.class | _ + } +} + diff --git a/src/test/groovy/io/github/oemergenc/hbase/orm/extensions/mapper/NoAliasDynamicColumnMapperSpec.groovy b/src/test/groovy/io/github/oemergenc/hbase/orm/extensions/mapper/NoAliasDynamicColumnMapperSpec.groovy new file mode 100644 index 0000000..34cc1bd --- /dev/null +++ b/src/test/groovy/io/github/oemergenc/hbase/orm/extensions/mapper/NoAliasDynamicColumnMapperSpec.groovy @@ -0,0 +1,38 @@ +package io.github.oemergenc.hbase.orm.extensions.mapper + +import io.github.oemergenc.hbase.orm.extensions.HBDynamicColumnObjectMapper +import io.github.oemergenc.hbase.orm.extensions.domain.dto.DependentWithMap +import io.github.oemergenc.hbase.orm.extensions.domain.records.NoAliasHBDynamicColumnRecord +import spock.lang.Specification + +class NoAliasDynamicColumnMapperSpec extends Specification { + def mapper = new HBDynamicColumnObjectMapper() + + def "Converting a record with a no alias record works"() { + given: + def staticId = "staticId" + def dynamicFamilyList = [ + new DependentWithMap("dynamicId2", ["1sja": "bsjcasla", "12321": "csdckqewwqe"]), + new DependentWithMap("dynamicId1", ["1": "bla", "12321": "qewwqe"]) + ] + def record = new NoAliasHBDynamicColumnRecord(staticId, dynamicFamilyList) + + when: + def result = mapper.writeValueAsResult(record) + + then: + result + result.getFamilyMap("noAliasDynamicFamily".bytes)["dynamicId1".bytes] + result.getFamilyMap("noAliasDynamicFamily".bytes)["dynamicId2".bytes] + result.getFamilyMap("staticFamily".bytes)['staticId'.bytes] + + when: + def recordResult = mapper.readValue(result, NoAliasHBDynamicColumnRecord.class) + + then: + recordResult + recordResult.staticId == staticId + recordResult.noAliasDynamicFamily.size() == 2 + recordResult.noAliasDynamicFamily.collect { it.dynamicId }.containsAll(["dynamicId1", "dynamicId2"]) + } +} diff --git a/src/test/java/io/github/oemergenc/hbase/orm/extensions/dao/NoAliasDynamicColumsDao.java b/src/test/java/io/github/oemergenc/hbase/orm/extensions/dao/NoAliasDynamicColumsDao.java new file mode 100644 index 0000000..fd94a72 --- /dev/null +++ b/src/test/java/io/github/oemergenc/hbase/orm/extensions/dao/NoAliasDynamicColumsDao.java @@ -0,0 +1,13 @@ +package io.github.oemergenc.hbase.orm.extensions.dao; + +import io.github.oemergenc.hbase.orm.extensions.AbstractHBDynamicDAO; +import io.github.oemergenc.hbase.orm.extensions.domain.records.NoAliasHBDynamicColumnRecord; +import lombok.extern.slf4j.Slf4j; +import org.apache.hadoop.hbase.client.Connection; + +@Slf4j +public class NoAliasDynamicColumsDao extends AbstractHBDynamicDAO { + public NoAliasDynamicColumsDao(Connection connection) { + super(connection); + } +} diff --git a/src/test/java/io/github/oemergenc/hbase/orm/extensions/dao/NoAliasMixedDynamicColumsDao.java b/src/test/java/io/github/oemergenc/hbase/orm/extensions/dao/NoAliasMixedDynamicColumsDao.java new file mode 100644 index 0000000..b4e02ed --- /dev/null +++ b/src/test/java/io/github/oemergenc/hbase/orm/extensions/dao/NoAliasMixedDynamicColumsDao.java @@ -0,0 +1,13 @@ +package io.github.oemergenc.hbase.orm.extensions.dao; + +import io.github.oemergenc.hbase.orm.extensions.AbstractHBDynamicDAO; +import io.github.oemergenc.hbase.orm.extensions.domain.records.NoAliasMixedHBDynamicColumnRecord; +import lombok.extern.slf4j.Slf4j; +import org.apache.hadoop.hbase.client.Connection; + +@Slf4j +public class NoAliasMixedDynamicColumsDao extends AbstractHBDynamicDAO { + public NoAliasMixedDynamicColumsDao(Connection connection) { + super(connection); + } +} diff --git a/src/test/java/io/github/oemergenc/hbase/orm/extensions/domain/invalid/InvalidNoAliasHBDynamicColumnRecord.java b/src/test/java/io/github/oemergenc/hbase/orm/extensions/domain/invalid/InvalidNoAliasHBDynamicColumnRecord.java new file mode 100644 index 0000000..2f04372 --- /dev/null +++ b/src/test/java/io/github/oemergenc/hbase/orm/extensions/domain/invalid/InvalidNoAliasHBDynamicColumnRecord.java @@ -0,0 +1,45 @@ +package io.github.oemergenc.hbase.orm.extensions.domain.invalid; + +import com.flipkart.hbaseobjectmapper.DynamicQualifier; +import com.flipkart.hbaseobjectmapper.Family; +import com.flipkart.hbaseobjectmapper.HBColumn; +import com.flipkart.hbaseobjectmapper.HBRecord; +import com.flipkart.hbaseobjectmapper.HBRowKey; +import com.flipkart.hbaseobjectmapper.HBTable; +import io.github.oemergenc.hbase.orm.extensions.HBDynamicColumn; +import io.github.oemergenc.hbase.orm.extensions.domain.dto.DependentWithMap; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@HBTable(name = "invalid_no_alias_column_table", families = { + @Family(name = "staticFamily"), + @Family(name = "invalidNoAliasDynamicFamily") +}) +public class InvalidNoAliasHBDynamicColumnRecord implements HBRecord { + + @HBRowKey + @HBColumn(family = "staticFamily", column = "staticId") + private String staticId; + + @HBDynamicColumn(family = "invalidNoAliasDynamicFamily", qualifier = @DynamicQualifier(parts = {"dynamicId"})) + private List noAliasDynamicFamily1; + + @HBDynamicColumn(family = "invalidNoAliasDynamicFamily", qualifier = @DynamicQualifier(parts = {"dynamicId2"})) + private List noAliasDynamicFamily2; + + @Override + public String composeRowKey() { + return staticId; + } + + @Override + public void parseRowKey(String rowKey) { + this.staticId = rowKey; + } +} diff --git a/src/test/java/io/github/oemergenc/hbase/orm/extensions/domain/invalid/InvalidNoAliasMixedHBDynamicColumnRecord.java b/src/test/java/io/github/oemergenc/hbase/orm/extensions/domain/invalid/InvalidNoAliasMixedHBDynamicColumnRecord.java new file mode 100644 index 0000000..3d04deb --- /dev/null +++ b/src/test/java/io/github/oemergenc/hbase/orm/extensions/domain/invalid/InvalidNoAliasMixedHBDynamicColumnRecord.java @@ -0,0 +1,45 @@ +package io.github.oemergenc.hbase.orm.extensions.domain.invalid; + +import com.flipkart.hbaseobjectmapper.DynamicQualifier; +import com.flipkart.hbaseobjectmapper.Family; +import com.flipkart.hbaseobjectmapper.HBColumn; +import com.flipkart.hbaseobjectmapper.HBRecord; +import com.flipkart.hbaseobjectmapper.HBRowKey; +import com.flipkart.hbaseobjectmapper.HBTable; +import io.github.oemergenc.hbase.orm.extensions.HBDynamicColumn; +import io.github.oemergenc.hbase.orm.extensions.domain.dto.DependentWithMap; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@HBTable(name = "invalid_no_alias_mixed_column_table", families = { + @Family(name = "staticFamily"), + @Family(name = "invalidNoAliasMixedDynamicFamily") +}) +public class InvalidNoAliasMixedHBDynamicColumnRecord implements HBRecord { + + @HBRowKey + @HBColumn(family = "staticFamily", column = "staticId") + private String staticId; + + @HBDynamicColumn(family = "invalidNoAliasMixedDynamicFamily", qualifier = @DynamicQualifier(parts = {"dynamicId"})) + private List noAliasDynamicFamily1; + + @HBDynamicColumn(family = "invalidNoAliasMixedDynamicFamily", alias = "id", qualifier = @DynamicQualifier(parts = {"dynamicId2"})) + private List noAliasDynamicFamily2; + + @Override + public String composeRowKey() { + return staticId; + } + + @Override + public void parseRowKey(String rowKey) { + this.staticId = rowKey; + } +} diff --git a/src/test/java/io/github/oemergenc/hbase/orm/extensions/domain/records/NoAliasHBDynamicColumnRecord.java b/src/test/java/io/github/oemergenc/hbase/orm/extensions/domain/records/NoAliasHBDynamicColumnRecord.java new file mode 100644 index 0000000..a9b1d59 --- /dev/null +++ b/src/test/java/io/github/oemergenc/hbase/orm/extensions/domain/records/NoAliasHBDynamicColumnRecord.java @@ -0,0 +1,42 @@ +package io.github.oemergenc.hbase.orm.extensions.domain.records; + +import com.flipkart.hbaseobjectmapper.DynamicQualifier; +import com.flipkart.hbaseobjectmapper.Family; +import com.flipkart.hbaseobjectmapper.HBColumn; +import com.flipkart.hbaseobjectmapper.HBRecord; +import com.flipkart.hbaseobjectmapper.HBRowKey; +import com.flipkart.hbaseobjectmapper.HBTable; +import io.github.oemergenc.hbase.orm.extensions.HBDynamicColumn; +import io.github.oemergenc.hbase.orm.extensions.domain.dto.DependentWithMap; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@HBTable(name = "no_alias_column_table", families = { + @Family(name = "staticFamily"), + @Family(name = "noAliasDynamicFamily") +}) +public class NoAliasHBDynamicColumnRecord implements HBRecord { + + @HBRowKey + @HBColumn(family = "staticFamily", column = "staticId") + private String staticId; + + @HBDynamicColumn(family = "noAliasDynamicFamily", qualifier = @DynamicQualifier(parts = {"dynamicId"})) + private List noAliasDynamicFamily; + + @Override + public String composeRowKey() { + return staticId; + } + + @Override + public void parseRowKey(String rowKey) { + this.staticId = rowKey; + } +} diff --git a/src/test/java/io/github/oemergenc/hbase/orm/extensions/domain/records/NoAliasMixedHBDynamicColumnRecord.java b/src/test/java/io/github/oemergenc/hbase/orm/extensions/domain/records/NoAliasMixedHBDynamicColumnRecord.java new file mode 100644 index 0000000..6486c69 --- /dev/null +++ b/src/test/java/io/github/oemergenc/hbase/orm/extensions/domain/records/NoAliasMixedHBDynamicColumnRecord.java @@ -0,0 +1,47 @@ +package io.github.oemergenc.hbase.orm.extensions.domain.records; + +import com.flipkart.hbaseobjectmapper.DynamicQualifier; +import com.flipkart.hbaseobjectmapper.Family; +import com.flipkart.hbaseobjectmapper.HBColumn; +import com.flipkart.hbaseobjectmapper.HBRecord; +import com.flipkart.hbaseobjectmapper.HBRowKey; +import com.flipkart.hbaseobjectmapper.HBTable; +import io.github.oemergenc.hbase.orm.extensions.HBDynamicColumn; +import io.github.oemergenc.hbase.orm.extensions.domain.dto.DependentWithMap; +import io.github.oemergenc.hbase.orm.extensions.domain.dto.DependentWithPrimitiveTypes; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@HBTable(name = "no_alias_mixed_column_table", families = { + @Family(name = "staticFamily"), + @Family(name = "noAliasDynamicFamily"), + @Family(name = "dynamicFamily") +}) +public class NoAliasMixedHBDynamicColumnRecord implements HBRecord { + + @HBRowKey + @HBColumn(family = "staticFamily", column = "staticId") + private String staticId; + + @HBDynamicColumn(family = "noAliasDynamicFamily", qualifier = @DynamicQualifier(parts = {"dynamicId"})) + private List noAliasDynamicFamily; + + @HBDynamicColumn(family = "dynamicFamily", alias = "df1", qualifier = @DynamicQualifier(parts = {"dynamicPart1", "dynamicPart2"})) + private List dynamicValues; + + @Override + public String composeRowKey() { + return staticId; + } + + @Override + public void parseRowKey(String rowKey) { + this.staticId = rowKey; + } +}