diff --git a/src/main/java/net/helenus/core/AbstractAuditedEntityDraft.java b/src/main/java/net/helenus/core/AbstractAuditedEntityDraft.java new file mode 100644 index 0000000..f7796ee --- /dev/null +++ b/src/main/java/net/helenus/core/AbstractAuditedEntityDraft.java @@ -0,0 +1,33 @@ +package net.helenus.core; + +import net.helenus.core.reflect.MapExportable; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; + + +public abstract class AbstractAuditedEntityDraft extends AbstractEntityDraft { + + public AbstractAuditedEntityDraft(MapExportable entity, AuditProvider auditProvider) { + super(entity); + + Date in = new Date(); + LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault()); + Date now = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); + + String who = auditProvider == null ? "unknown" : auditProvider.operatorName(); + + if (entity == null) { + set("createdBy", who); + set("createdAt", now); + } + set("modifiedBy", who); + set("modifiedAt", now); + } + + Date createdAt() { + return (Date) get("createdAt"); + } + +} diff --git a/src/main/java/net/helenus/core/AbstractEntityDraft.java b/src/main/java/net/helenus/core/AbstractEntityDraft.java new file mode 100644 index 0000000..21e1c38 --- /dev/null +++ b/src/main/java/net/helenus/core/AbstractEntityDraft.java @@ -0,0 +1,174 @@ +package net.helenus.core; + +import java.util.*; + +import com.google.common.primitives.Primitives; + +import net.helenus.core.reflect.DefaultPrimitiveTypes; +import net.helenus.core.reflect.Drafted; +import net.helenus.core.reflect.MapExportable; +import net.helenus.mapping.MappingUtil; + + +public abstract class AbstractEntityDraft implements Drafted { + + private final Map backingMap = new HashMap(); + private final Set mutatedSet = new HashSet(); + private final MapExportable entity; + private final Map entityMap; + + + public AbstractEntityDraft(MapExportable entity) { + this.entity = entity; + this.entityMap = entity != null ? entity.toMap() : new HashMap(); + } + + public abstract Class getEntityClass(); + + public E build() { return Helenus.map(getEntityClass(), toMap()); } + + @SuppressWarnings("unchecked") + protected T get(String key) { + T value = (T) entityMap.get(key); + + if (value == null) { + T obj = (T) new Object() { + }; + Class primitiveType = Primitives.unwrap(obj.getClass()); + if (Primitives.allPrimitiveTypes().contains(primitiveType)) { + DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(primitiveType); + if (type == null) { + throw new RuntimeException("unknown primitive type " + primitiveType.getTypeName()); + } + + return (T) type.getDefaultValue(); + } + + } + + return value; + } + + @SuppressWarnings("unchecked") + protected T get(String key, Class returnType) { + T value = (T) entityMap.get(key); + + if (value == null) { + + if (Primitives.allPrimitiveTypes().contains(returnType)) { + + DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType); + if (type == null) { + throw new RuntimeException("unknown primitive type " + returnType); + } + + return (T) type.getDefaultValue(); + } + } + + return value; + } + + protected Object set(String key, Object value) { + + if (key == null || value == null) { + return null; + } + + backingMap.put(key, value); + mutatedSet.add(key); + return value; + } + + protected Object mutate(String key, Object value) { + Objects.requireNonNull(key); + + if (value == null) { + return null; + } + + if (entity != null) { + Map map = entity.toMap(); + + if (map.containsKey(key) && !value.equals(map.get(key))) { + backingMap.put(key, value); + mutatedSet.add(key); + return value; + } + + return map.get(key); + } else { + backingMap.put(key, value); + mutatedSet.add(key); + + return null; + } + } + + private String methodNameFor(Getter getter) { + return MappingUtil.resolveMappingProperty(getter) + .getProperty() + .getPropertyName(); + } + + public Object unset(Getter getter) { + return unset(methodNameFor(getter)); + } + + public Object unset(String key) { + if (key != null) { + Object value = backingMap.get(key); + backingMap.put(key, null); + mutatedSet.add(key); + return value; + } + return null; + } + + public boolean reset(Getter getter, T desiredValue) { + return this.reset(methodNameFor(getter), desiredValue); + } + + public boolean reset(String key, T desiredValue) { + if (key != null && desiredValue != null) { + @SuppressWarnings("unchecked") + T currentValue = (T) backingMap.get(key); + if (currentValue != null && !currentValue.equals(desiredValue)) { + return set(key, desiredValue) != null; + } + } + return false; + } + + @Override + public Map toMap() { + return toMap(entityMap); + } + + public Map toMap(MapentityMap) { + Map combined; + if (entityMap != null && entityMap.size() > 0) { + combined = new HashMap(entityMap.size()); + for (String key : entityMap.keySet()) { + combined.put(key, entityMap.get(key)); + } + } else { + combined = new HashMap(backingMap.size()); + } + for (String key : mutatedSet) { + combined.put(key, backingMap.get(key)); + } + return combined; + } + + @Override + public Set mutated() { + return mutatedSet; + } + + @Override + public String toString() { + return backingMap.toString(); + } + +} diff --git a/src/main/java/net/helenus/core/AuditProvider.java b/src/main/java/net/helenus/core/AuditProvider.java new file mode 100644 index 0000000..43c28bc --- /dev/null +++ b/src/main/java/net/helenus/core/AuditProvider.java @@ -0,0 +1,10 @@ +package net.helenus.core; + +public interface AuditProvider { + + /** + * What to record in the database row as the name of the agent causing the mutation. + * @return a string name that indicates the identity of the operator mutating the data at this time. + */ + public String operatorName(); +} diff --git a/src/main/java/net/helenus/core/HelenusSession.java b/src/main/java/net/helenus/core/HelenusSession.java index a086562..e5f9172 100644 --- a/src/main/java/net/helenus/core/HelenusSession.java +++ b/src/main/java/net/helenus/core/HelenusSession.java @@ -389,11 +389,15 @@ public final class HelenusSession extends AbstractSessionOperations implements C return new UpdateOperation(this); } - public UpdateOperation update(Drafted draft) { - UpdateOperation update = new UpdateOperation(this, draft.build()); + public UpdateOperation update(Drafted drafted) { + if (drafted instanceof AbstractEntityDraft == false) { + throw new HelenusMappingException("update of draft objects that don't inherit from AbstractEntityDraft is not yet supported"); + } + AbstractEntityDraft draft = (AbstractEntityDraft)drafted; + UpdateOperation update = new UpdateOperation(this, draft); Map map = draft.toMap(); - HelenusEntity entity = draft.getEntity(); Set mutatedProperties = draft.mutated(); + HelenusEntity entity = Helenus.entity(draft.getEntityClass()); // Add all the mutated values contained in the draft. entity.getOrderedProperties().forEach(property -> { @@ -464,11 +468,9 @@ public final class HelenusSession extends AbstractSessionOperations implements C } } - public InsertOperation insert(Drafted draft) { - return this.insert((T) draft.build(), draft.mutated()); - } + public InsertOperation insert(Drafted draft) { return insert(draft.build(), draft.mutated()); } - public InsertOperation insert(T pojo, Set mutations) { + private InsertOperation insert(T pojo, Set mutations) { Objects.requireNonNull(pojo, "pojo is empty"); Class iface = MappingUtil.getMappingInterface(pojo); @@ -500,7 +502,7 @@ public final class HelenusSession extends AbstractSessionOperations implements C } } - public InsertOperation upsert(T pojo, Set mutations) { + private InsertOperation upsert(T pojo, Set mutations) { Objects.requireNonNull(pojo, "pojo is empty"); Class iface = MappingUtil.getMappingInterface(pojo); diff --git a/src/main/java/net/helenus/core/operation/InsertOperation.java b/src/main/java/net/helenus/core/operation/InsertOperation.java index 044d74f..17fbef9 100644 --- a/src/main/java/net/helenus/core/operation/InsertOperation.java +++ b/src/main/java/net/helenus/core/operation/InsertOperation.java @@ -73,7 +73,7 @@ public final class InsertOperation extends AbstractOperation properties = entity.getOrderedProperties(); Set keys = (mutations == null) ? null : mutations; @@ -154,10 +154,51 @@ public final class InsertOperation extends AbstractOperation iface = entity.getMappingInterface(); - if (resultType.isAssignableFrom(iface)) { - return TransformGeneric.INSTANCE.transform(sessionOps, pojo, iface, values, entity.getOrderedProperties()); + if (resultType == iface) { + if (values.size() > 0) { + Collection properties = entity.getOrderedProperties(); + Map backingMap = new HashMap(properties.size()); + + // First, add all the inserted values into our new map. + values.forEach(t -> backingMap.put(t._1.getProperty().getPropertyName(), t._2)); + + // Then, fill in all the rest of the properties. + for (HelenusProperty prop : properties) { + String key = prop.getPropertyName(); + if (backingMap.containsKey(key)) { + // Some values man need to be converted (e.g. from String to Enum). This is done + // within the BeanColumnValueProvider below. + Optional> converter = + prop.getReadConverter(sessionOps.getSessionRepository()); + if (converter.isPresent()) { + backingMap.put(key, converter.get().apply(backingMap.get(key))); + } + } else { + // If we started this operation with an instance of this type, use values from that. + if (pojo != null) { + backingMap.put(key, BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop)); + } else { + // Otherwise we'll use default values for the property type if available. + Class propType = prop.getJavaType(); + if (propType.isPrimitive()) { + DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(propType); + if (type == null) { + throw new HelenusException("unknown primitive type " + propType); + } + backingMap.put(key, type.getDefaultValue()); + } + } + } + } + + // Lastly, create a new proxy object for the entity and return the new instance. + return (T) Helenus.map(iface, backingMap); + } + // Oddly, this insert didn't change any value so simply return the pojo. + // TODO(gburd): this pojo is the result of a Draft.build() call which will not preserve object identity (o1 == o2), ... fix me. + return (T) pojo; } - return pojo; + return (T) resultSet; } public InsertOperation usingTtl(int ttl) { diff --git a/src/main/java/net/helenus/core/operation/SelectOperation.java b/src/main/java/net/helenus/core/operation/SelectOperation.java index 32b3fc6..a1a16a7 100644 --- a/src/main/java/net/helenus/core/operation/SelectOperation.java +++ b/src/main/java/net/helenus/core/operation/SelectOperation.java @@ -194,6 +194,10 @@ public final class SelectOperation extends AbstractFilterStreamOperation T transform(AbstractSessionOperations sessionOps, T pojo, Class mappingInterface, List> assignments, Collection properties) { - if (assignments.size() > 0) { - Map backingMap = new HashMap(properties.size()); - - // First, add all the inserted values into our new map. - assignments.forEach(t -> backingMap.put(t._1.getProperty().getPropertyName(), t._2)); - - // Then, fill in all the rest of the properties. - for (HelenusProperty prop : properties) { - String key = prop.getPropertyName(); - if (backingMap.containsKey(key)) { - // Some values man need to be converted (e.g. from String to Enum). This is done - // within the BeanColumnValueProvider below. - Optional> converter = - prop.getReadConverter(sessionOps.getSessionRepository()); - if (converter.isPresent()) { - backingMap.put(key, converter.get().apply(backingMap.get(key))); - } - } else { - // If we started this operation with an instance of this type, use values from that. - if (pojo != null) { - backingMap.put(key, BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop)); - } else { - // Otherwise we'll use default values for the property type if available. - Class propType = prop.getJavaType(); - if (propType.isPrimitive()) { - DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(propType); - if (type == null) { - throw new HelenusException("unknown primitive type " + propType); - } - backingMap.put(key, type.getDefaultValue()); - } - } - } - } - - // Lastly, create a new proxy object for the entity and return the new instance. - return (T) Helenus.map(mappingInterface, backingMap); - } - return (T) pojo; - } -} diff --git a/src/main/java/net/helenus/core/operation/UpdateOperation.java b/src/main/java/net/helenus/core/operation/UpdateOperation.java index 30e4d33..701ca2b 100644 --- a/src/main/java/net/helenus/core/operation/UpdateOperation.java +++ b/src/main/java/net/helenus/core/operation/UpdateOperation.java @@ -15,16 +15,16 @@ */ package net.helenus.core.operation; +import java.util.*; +import java.util.function.Function; + import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.querybuilder.Assignment; import com.datastax.driver.core.querybuilder.BuiltStatement; import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.Update; -import java.util.*; -import java.util.function.Function; -import net.helenus.core.AbstractSessionOperations; -import net.helenus.core.Filter; -import net.helenus.core.Getter; + +import net.helenus.core.*; import net.helenus.core.reflect.HelenusPropertyNode; import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusProperty; @@ -32,29 +32,33 @@ import net.helenus.mapping.MappingUtil; import net.helenus.support.HelenusMappingException; import net.helenus.support.Immutables; -public final class UpdateOperation extends AbstractFilterOperation> { +public final class UpdateOperation extends AbstractFilterOperation> { private HelenusEntity entity = null; private final List assignments = new ArrayList(); - private final T pojo; + private final AbstractEntityDraft draft; + private final Map draftMap; private int[] ttl; private long[] timestamp; public UpdateOperation(AbstractSessionOperations sessionOperations){ super(sessionOperations); - this.pojo = null; + this.draft = null; + this.draftMap = null; } - public UpdateOperation(AbstractSessionOperations sessionOperations, T pojo) { + public UpdateOperation(AbstractSessionOperations sessionOperations, AbstractEntityDraft draft) { super(sessionOperations); - this.pojo = pojo; + this.draft = draft; + this.draftMap = draft.toMap(); } public UpdateOperation(AbstractSessionOperations sessionOperations, HelenusPropertyNode p, Object v) { super(sessionOperations); - this.pojo = null; + this.draft = null; + this.draftMap = null; Object value = sessionOps.getValuePreparer().prepareColumnValue(v, p.getProperty()); assignments.add(QueryBuilder.set(p.getColumnName(), value)); @@ -62,7 +66,7 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation set(Getter getter, V v) { + public UpdateOperation set(Getter getter, V v) { Objects.requireNonNull(getter, "getter is empty"); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(getter); @@ -83,11 +87,11 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation increment(Getter counterGetter) { + public UpdateOperation increment(Getter counterGetter) { return increment(counterGetter, 1L); } - public UpdateOperation increment(Getter counterGetter, long delta) { + public UpdateOperation increment(Getter counterGetter, long delta) { Objects.requireNonNull(counterGetter, "counterGetter is empty"); @@ -96,14 +100,20 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation decrement(Getter counterGetter) { + public UpdateOperation decrement(Getter counterGetter) { return decrement(counterGetter, 1L); } - public UpdateOperation decrement(Getter counterGetter, long delta) { + public UpdateOperation decrement(Getter counterGetter, long delta) { Objects.requireNonNull(counterGetter, "counterGetter is empty"); @@ -112,6 +122,12 @@ public final class UpdateOperation extends AbstractFilterOperation extends AbstractFilterOperation UpdateOperation prepend(Getter> listGetter, V value) { + public UpdateOperation prepend(Getter> listGetter, V value) { Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -133,10 +149,17 @@ public final class UpdateOperation extends AbstractFilterOperation list = (List)draftMap.get(key); + list.add(0, value); + } + return this; } - public UpdateOperation prependAll(Getter> listGetter, List value) { + public UpdateOperation prependAll(Getter> listGetter, List value) { Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -147,10 +170,17 @@ public final class UpdateOperation extends AbstractFilterOperation 0) { + String key = p.getProperty().getPropertyName(); + List list = (List) draftMap.get(key); + list.addAll(0, value); + } + return this; } - public UpdateOperation setIdx(Getter> listGetter, int idx, V value) { + public UpdateOperation setIdx(Getter> listGetter, int idx, V value) { Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -161,10 +191,24 @@ public final class UpdateOperation extends AbstractFilterOperation list = (List)draftMap.get(key); + if (idx < 0) { + list.add(0, value); + } else if (idx > list.size()) { + list.add(list.size(), value); + } else { + list.add(idx, value); + } + list.add(0, value); + } + return this; } - public UpdateOperation append(Getter> listGetter, V value) { + public UpdateOperation append(Getter> listGetter, V value) { Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -175,10 +219,17 @@ public final class UpdateOperation extends AbstractFilterOperation list = (List)draftMap.get(key); + list.add(value); + } + return this; } - public UpdateOperation appendAll(Getter> listGetter, List value) { + public UpdateOperation appendAll(Getter> listGetter, List value) { Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -189,10 +240,17 @@ public final class UpdateOperation extends AbstractFilterOperation 0) { + String key = p.getProperty().getPropertyName(); + List list = (List) draftMap.get(key); + list.addAll(value); + } + return this; } - public UpdateOperation discard(Getter> listGetter, V value) { + public UpdateOperation discard(Getter> listGetter, V value) { Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -203,10 +261,17 @@ public final class UpdateOperation extends AbstractFilterOperation list = (List) draftMap.get(key); + list.remove(value); + } + return this; } - public UpdateOperation discardAll(Getter> listGetter, List value) { + public UpdateOperation discardAll(Getter> listGetter, List value) { Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -217,6 +282,13 @@ public final class UpdateOperation extends AbstractFilterOperation list = (List) draftMap.get(key); + list.removeAll(value); + } + return this; } @@ -258,7 +330,7 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation add(Getter> setGetter, V value) { + public UpdateOperation add(Getter> setGetter, V value) { Objects.requireNonNull(setGetter, "setGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -269,10 +341,17 @@ public final class UpdateOperation extends AbstractFilterOperation set = (Set) draftMap.get(key); + set.add(value); + } + return this; } - public UpdateOperation addAll(Getter> setGetter, Set value) { + public UpdateOperation addAll(Getter> setGetter, Set value) { Objects.requireNonNull(setGetter, "setGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -283,10 +362,17 @@ public final class UpdateOperation extends AbstractFilterOperation set = (Set) draftMap.get(key); + set.addAll(value); + } + return this; } - public UpdateOperation remove(Getter> setGetter, V value) { + public UpdateOperation remove(Getter> setGetter, V value) { Objects.requireNonNull(setGetter, "setGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -297,10 +383,17 @@ public final class UpdateOperation extends AbstractFilterOperation set = (Set) draftMap.get(key); + set.remove(value); + } + return this; } - public UpdateOperation removeAll(Getter> setGetter, Set value) { + public UpdateOperation removeAll(Getter> setGetter, Set value) { Objects.requireNonNull(setGetter, "setGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -311,6 +404,13 @@ public final class UpdateOperation extends AbstractFilterOperation set = (Set) draftMap.get(key); + set.removeAll(value); + } + return this; } @@ -351,7 +451,7 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation put(Getter> mapGetter, K key, V value) { + public UpdateOperation put(Getter> mapGetter, K key, V value) { Objects.requireNonNull(mapGetter, "mapGetter is empty"); Objects.requireNonNull(key, "key is empty"); @@ -372,10 +472,15 @@ public final class UpdateOperation extends AbstractFilterOperation) draftMap.get(prop.getPropertyName())).put(key, value); + } + return this; } - public UpdateOperation putAll(Getter> mapGetter, Map map) { + public UpdateOperation putAll(Getter> mapGetter, Map map) { Objects.requireNonNull(mapGetter, "mapGetter is empty"); Objects.requireNonNull(map, "map is empty"); @@ -393,6 +498,11 @@ public final class UpdateOperation extends AbstractFilterOperation) draftMap.get(prop.getPropertyName())).putAll(map); + } + return this; } @@ -435,17 +545,21 @@ public final class UpdateOperation extends AbstractFilterOperation usingTtl(int ttl) { + public UpdateOperation usingTtl(int ttl) { this.ttl = new int[1]; this.ttl[0] = ttl; return this; } - public UpdateOperation usingTimestamp(long timestamp) { + public UpdateOperation usingTimestamp(long timestamp) { this.timestamp = new long[1]; this.timestamp[0] = timestamp; return this; diff --git a/src/main/java/net/helenus/core/reflect/Drafted.java b/src/main/java/net/helenus/core/reflect/Drafted.java index e714658..3cf8024 100644 --- a/src/main/java/net/helenus/core/reflect/Drafted.java +++ b/src/main/java/net/helenus/core/reflect/Drafted.java @@ -6,8 +6,6 @@ import java.util.Set; public interface Drafted extends MapExportable { - HelenusEntity getEntity(); - Set mutated(); T build(); diff --git a/src/main/java/net/helenus/mapping/value/BeanColumnValueProvider.java b/src/main/java/net/helenus/mapping/value/BeanColumnValueProvider.java index 9efd610..2381514 100644 --- a/src/main/java/net/helenus/mapping/value/BeanColumnValueProvider.java +++ b/src/main/java/net/helenus/mapping/value/BeanColumnValueProvider.java @@ -15,8 +15,10 @@ */ package net.helenus.mapping.value; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import net.helenus.mapping.HelenusProperty; +import net.helenus.support.HelenusException; import net.helenus.support.HelenusMappingException; public enum BeanColumnValueProvider implements ColumnValueProvider { @@ -30,6 +32,10 @@ public enum BeanColumnValueProvider implements ColumnValueProvider { Object value = null; try { value = getter.invoke(bean, new Object[] {}); + } catch (InvocationTargetException e) { + if (e.getCause() != null ) { + throw new HelenusException("getter threw an exception", e.getCause()); + } } catch (ReflectiveOperationException e) { throw new HelenusMappingException("fail to call getter " + getter, e); } catch (IllegalArgumentException e) { diff --git a/src/test/java/net/helenus/test/integration/core/draft/EntityDraftBuilderTest.java b/src/test/java/net/helenus/test/integration/core/draft/EntityDraftBuilderTest.java new file mode 100644 index 0000000..2be5381 --- /dev/null +++ b/src/test/java/net/helenus/test/integration/core/draft/EntityDraftBuilderTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2015 The Helenus Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.helenus.test.integration.core.draft; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import net.helenus.core.Helenus; +import net.helenus.core.HelenusSession; +import net.helenus.test.integration.build.AbstractEmbeddedCassandraTest; + +import static net.helenus.core.Query.eq; + +public class EntityDraftBuilderTest extends AbstractEmbeddedCassandraTest { + + + static Supply supply; + static HelenusSession session; + + + @BeforeClass + public static void beforeTest() { + session = Helenus.init(getSession()) + .showCql() + .add(Supply.class) + .autoCreateDrop() + .get(); + supply = session.dsl(Supply.class); + } + + @Test + public void testFoo() throws Exception { + Supply.Draft draft = null; + + draft = Supply.draft("APAC") + .code("WIDGET-002") + .description("Our second Widget!") + .demand(new HashMap() {{ + put("APAC", 100L); + put("EMEA", 10000L); + put("NORAM", 2000000L); + }}) + .shipments(new HashSet() {{ + add("HMS Puddle in transit to APAC, 100 units."); + add("Frigate Jimmy in transit to EMEA, 10000 units."); + }}) + .suppliers(new ArrayList() {{ + add("Puddle, Inc."); + add("Jimmy Town, LTD."); + }}); + + Supply s1 = session.insert(draft) + .sync(); + + // List + Supply s2 = session.update(s1.update()) + .prepend(supply::suppliers, "Pignose Supply, LLC.") + .sync(); + Assert.assertEquals(s2.suppliers().get(0), "Pignose Supply, LLC."); + + // Set + String shipment = "Pignose, on the way! (1M units)"; + Supply s3 = session.update(s2.update()) + .add(supply::shipments, shipment) + .sync(); + Assert.assertTrue(s3.shipments().contains(shipment)); + + // Map + Supply s4 = session.update(s3.update()) + .put(supply::demand, "NORAM", 10L) + .sync(); + Assert.assertEquals((long)s4.demand().get("NORAM"), 10L); + + } +} diff --git a/src/test/java/net/helenus/test/integration/core/draft/Inventory.java b/src/test/java/net/helenus/test/integration/core/draft/Inventory.java new file mode 100644 index 0000000..d82d48e --- /dev/null +++ b/src/test/java/net/helenus/test/integration/core/draft/Inventory.java @@ -0,0 +1,73 @@ +package net.helenus.test.integration.core.draft; + +import java.util.UUID; + +import net.helenus.core.AbstractAuditedEntityDraft; +import net.helenus.core.reflect.MapExportable; +import net.helenus.mapping.annotation.*; + + +@Table +public interface Inventory { + + @PartitionKey UUID id(); + @Column("emea") @Types.Counter long EMEA(); + @Column("noram") @Types.Counter long NORAM(); + @Column("apac") @Types.Counter long APAC(); + + @Transient static Draft draft(UUID id) { return new Draft(id); } + + @Transient default Draft update() { return new Draft(this); } + + + class Draft extends AbstractAuditedEntityDraft { + + // Entity/Draft pattern-enabling methods: + Draft(UUID id) { + super(null, null); + + // Primary Key: + set("id", id); + } + + Draft(Inventory inventory) { + super((MapExportable) inventory, null); + } + + public Class getEntityClass() { return Inventory.class; } + + // Immutable properties: + public UUID id() { + return this.get("id"); + } + + public long EMEA() { + return this.get("EMEA", long.class); + } + + public Draft EMEA(long count) { + mutate("EMEA", count); + return this; + } + + public long APAC() { + return this.get("APAC", long.class); + } + + public Draft APAC(long count) { + mutate("APAC", count); + return this; + } + + public long NORAM() { + return this.get("NORAM", long.class); + } + + public Draft NORAM(long count) { + mutate("NORAM", count); + return this; + } + + } + +} diff --git a/src/test/java/net/helenus/test/integration/core/draft/Supply.java b/src/test/java/net/helenus/test/integration/core/draft/Supply.java new file mode 100644 index 0000000..ee34909 --- /dev/null +++ b/src/test/java/net/helenus/test/integration/core/draft/Supply.java @@ -0,0 +1,125 @@ +package net.helenus.test.integration.core.draft; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import com.datastax.driver.core.utils.UUIDs; + +import net.helenus.core.AbstractEntityDraft; +import net.helenus.core.reflect.MapExportable; +import net.helenus.mapping.annotation.*; + + +@Table +public interface Supply { + + @PartitionKey UUID id(); + @ClusteringColumn(ordinal=0) default String region() { return "NORAM"; } + + @Index(caseSensitive = false) String code(); + @Index String description(); // @IndexText == lucene index + @Index Map demand(); + @Index List suppliers(); + @Index Set shipments(); + + @Transient static Draft draft(String region) { return new Draft(region); } + + @Transient default Draft update() { return new Draft(this); } + + + class Draft extends AbstractEntityDraft { + + // Entity/Draft pattern-enabling methods: + Draft(String region) { + super(null); + + // Primary Key: + set("id", UUIDs.timeBased()); + set("region", region); + } + + Draft(Supply supply) { + super((MapExportable) supply); + } + + public Class getEntityClass() { return Supply.class; } + + // Immutable properties: + public UUID id() { + return this.get("id"); + } + + public String region() { + return this.get("region"); + } + + // Mutable properties: + public String code() { + return this.get("code"); + } + + public Draft code(String code) { + mutate("code", code); + return this; + } + + public Draft setCode(String code) { + return code(code); + } + + public String description() { + return this.get("description"); + } + + public Draft description(String description) { + mutate("description", description); + return this; + } + + public Draft setDescription(String description) { + return description(description); + } + + public Map demand() { + return this.>get("demand"); + } + + public Draft demand(Map demand) { + mutate("demand", demand); + return this; + } + + public Draft setDemand(Map demand) { + return demand(demand); + } + + public List suppliers() { + return this.>get("suppliers"); + } + + public Draft suppliers(List suppliers) { + mutate("suppliers", suppliers); + return this; + } + + public Draft setSuppliers(List suppliers) { + return suppliers(suppliers); + } + + public Set shipments() { + return this.>get("shipments"); + } + + public Draft shipments(Set shipments) { + mutate("shipments", shipments); + return this; + } + + public Draft setshipments(Set shipments) { + return shipments(shipments); + } + + } +} diff --git a/src/test/java/net/helenus/test/integration/core/simple/SimpleUserTest.java b/src/test/java/net/helenus/test/integration/core/simple/SimpleUserTest.java index 282231e..82c48b4 100644 --- a/src/test/java/net/helenus/test/integration/core/simple/SimpleUserTest.java +++ b/src/test/java/net/helenus/test/integration/core/simple/SimpleUserTest.java @@ -170,34 +170,6 @@ public class SimpleUserTest extends AbstractEmbeddedCassandraTest { // UPDATE - session.update(new Drafted() { - - @Override - public HelenusEntity getEntity() { return Helenus.entity(User.class); } - - @Override - public Map toMap() { - HashMap map = new HashMap(); - map.put("name", "joeseph"); - map.put("age", Integer.valueOf(45)); - map.put("id", 100L); - return map; - } - - @Override - public Set mutated() { - Set set = new HashSet(); - set.add("name"); - set.add("age"); - return set; - } - - @Override - public User build() { - return null; - } - }).sync(); - session .update(user::name, "albert") .set(user::age, 35) @@ -218,15 +190,15 @@ public class SimpleUserTest extends AbstractEmbeddedCassandraTest { Assert.assertEquals("_albert", name); - User u = session.select(user) + User u2 = session.select(user) .where(user::id, eq(100L)) .single() .sync() .orElse(null); - Assert.assertEquals(Long.valueOf(100L), u.id()); - Assert.assertEquals("albert", u.name()); - Assert.assertEquals(Integer.valueOf(35), u.age()); + Assert.assertEquals(Long.valueOf(100L), u2.id()); + Assert.assertEquals("albert", u2.name()); + Assert.assertEquals(Integer.valueOf(35), u2.age()); // User greg = @@ -253,6 +225,7 @@ public class SimpleUserTest extends AbstractEmbeddedCassandraTest { .set(user::age, null) .set(user::type, null) .where(user::id, eq(100L)) + .zipkinContext(null) .sync(); Fun.Tuple3 tuple = diff --git a/src/test/java/net/helenus/test/unit/core/dsl/Account.java b/src/test/java/net/helenus/test/unit/core/dsl/Account.java index 03779f5..2725f16 100644 --- a/src/test/java/net/helenus/test/unit/core/dsl/Account.java +++ b/src/test/java/net/helenus/test/unit/core/dsl/Account.java @@ -42,9 +42,6 @@ public interface Account { class Draft implements Drafted { //TODO - @Override - public HelenusEntity getEntity() { return null; } - @Override public Set mutated() { return null;