package crmdna.interaction;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.cmd.Query;
import crmdna.common.DateUtils;
import crmdna.common.api.APIException;
import crmdna.common.api.APIResponse.Status;
import crmdna.sequence.Sequence;
import crmdna.sequence.Sequence.SequenceType;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import static crmdna.common.AssertUtils.ensure;
import static crmdna.common.AssertUtils.ensureNotNull;
import static crmdna.common.OfyService.ofy;
public class InteractionCore {
public final static long MAX_CONTENT_SIZE = 5 * 1024;
public final static long MAX_SUB_INTERACTIONS = 99;
static InteractionProp createInteraction(String client, long memberId, String content,
String interactionType, Date timestamp, long userId,
Long campaignId, String user) {
ensureNotNull(timestamp, "timestamp is null");
ensureNotNull(interactionType, "interactionType is null");
ensure(interactionType.length() != 0, "interactionType is empty");
ensureNotNull(user, "userEmail is null");
ensure(user.length() != 0, "userEmail is empty");
if (content.length() > MAX_CONTENT_SIZE) {
throw new APIException().status(Status.ERROR_OVERFLOW)
.message("Interaction content cannot have more than ["
+ MAX_CONTENT_SIZE + "] characters");
}
InteractionEntity interactionEntity = new InteractionEntity();
interactionEntity.interactionId = Sequence.getNext(client, SequenceType.INTERACTION);
long us = DateUtils.getMicroSeconds(timestamp);
interactionEntity.subInteractions.put(us, content);
interactionEntity.ms = timestamp.getTime();
interactionEntity.memberId = memberId;
interactionEntity.userId = userId;
interactionEntity.user = user;
interactionEntity.interactionType = interactionType;
interactionEntity.campaignId = campaignId;
ofy(client).save().entity(interactionEntity).now();
return interactionEntity.toProp();
}
static InteractionProp updateInteraction(String client, long interactionId, Long newMemberId,
String newInteractionType, Long newUserId, String newUser) {
ensureNotNull(client, "client is null");
ensure(client.length() != 0, "client is empty");
ensure(interactionId != 0, "interactionId is 0");
InteractionEntity interactionEntity = safeGet(client, interactionId);
if (newMemberId != null) {
ensure(newMemberId != 0, "newMemberId is 0");
interactionEntity.memberId = newMemberId;
}
if ((newInteractionType != null) && (newInteractionType.length() != 0))
interactionEntity.interactionType = newInteractionType;
if (newUserId != null) {
ensure(newUserId != 0, "newUserId is 0");
interactionEntity.userId = newUserId;
}
if (newUser != null) {
ensure(newUser.length() != 0, "newUser is empty");
interactionEntity.user = newUser;
}
ofy(client).save().entity(interactionEntity);
return interactionEntity.toProp();
}
static void deleteInteraction(String client, long interactionId) {
ensureNotNull(client, "client is null");
ensure(client.length() != 0, "client is empty");
ensure(interactionId != 0, "interaction id is 0");
InteractionEntity interactionEntity = safeGet(client, interactionId);
ofy(client).delete().entity(interactionEntity);
}
static void deleteSubInteraction(String client, long interactionId, long subInteractionId) {
ensureNotNull(client, "client is null");
ensure(client.length() != 0, "client is empty");
ensure(interactionId != 0, "interaction id is 0");
ensure(subInteractionId != 0, "subInteraction id is 0");
InteractionEntity interactionEntity = safeGet(client, interactionId);
if (!interactionEntity.subInteractions.containsKey(subInteractionId))
throw new APIException().status(Status.ERROR_RESOURCE_NOT_FOUND).message(
"subInteractionId [" + subInteractionId + "] not found in interaction [" + interactionId
+ "]");
interactionEntity.subInteractions.remove(subInteractionId);
ofy(client).save().entity(interactionEntity);
}
static void createSubInteraction(String client, long interactionId, String content, Date timestamp) {
ensureNotNull(client, "client is null");
ensure(client.length() != 0, "client is empty");
ensureNotNull(content, "content is null");
if (content.length() > MAX_CONTENT_SIZE) {
throw new APIException().status(Status.ERROR_OVERFLOW)
.message("Sub interaction cannot have more than ["
+ MAX_CONTENT_SIZE + "] characters");
}
InteractionEntity interactionEntity = safeGet(client, interactionId);
if (interactionEntity.subInteractions.size() >= MAX_SUB_INTERACTIONS)
throw new APIException().status(Status.ERROR_OVERFLOW).message(
"Cannot add more than [" + MAX_SUB_INTERACTIONS
+ "] sub interactions for a single interaction");
long us = DateUtils.getMicroSeconds(timestamp);
if (interactionEntity.subInteractions.containsKey(us))
throw new APIException().status(Status.ERROR_RESOURCE_ALREADY_EXISTS).message(
"There is already a sub interaction for timestamp [" + timestamp + "]");
interactionEntity.subInteractions.put(us, content);
// update time stamp in interactionEntity if later
interactionEntity.ms = Math.max(interactionEntity.ms, timestamp.getTime());
ofy(client).save().entity(interactionEntity);
}
static void updateSubInteraction(String client, long interactionId, long subInteractionId,
String content, Date timestamp) {
ensureNotNull(client, "client is null");
ensure(client.length() != 0, "client is empty");
ensureNotNull(content, "content is null");
if (content.length() > MAX_CONTENT_SIZE) {
throw new APIException().status(Status.ERROR_OVERFLOW)
.message("Sub interaction cannot have more than ["
+ MAX_CONTENT_SIZE + "] characters");
}
InteractionEntity interactionEntity = safeGet(client, interactionId);
if (!interactionEntity.subInteractions.containsKey(subInteractionId))
throw new APIException().status(Status.ERROR_RESOURCE_NOT_FOUND).message(
"There is no sub interaction with id [" + subInteractionId + "]");
if (timestamp == null) {
// update contents for the same sub interaction
interactionEntity.subInteractions.put(subInteractionId, content);
} else {
interactionEntity.subInteractions.remove(subInteractionId);
long us = DateUtils.getMicroSeconds(timestamp);
interactionEntity.subInteractions.put(us, content);
interactionEntity.ms = Math.max(interactionEntity.ms, timestamp.getTime());
}
ofy(client).save().entity(interactionEntity);
}
static InteractionQueryResult query(String client, InteractionQueryCondition qc) {
List<Key<InteractionEntity>> keys = queryKeys(client, qc);
InteractionQueryResult qr = new InteractionQueryResult();
qr.interactionQueryCondtion = qc;
qr.totalSize = keys.size();
if (qc.startIndex == null) {
ensure(qc.numResults == null,
"numResults should not be specified when startIndex is not specified");
Map<Key<InteractionEntity>, InteractionEntity> map = ofy(client).load().keys(keys);
List<InteractionProp> props = new ArrayList<>(keys.size());
for (Key<InteractionEntity> key : keys) {
props.add(map.get(key).toProp());
}
qr.interactionProps = props;
return qr;
}
// some keys need to be excluded
ensure(qc.startIndex >= 0, "Invalid startIndex [" + qc.startIndex + "]");
ensureNotNull(qc.numResults, "numResults should be specified when startIndex is specified");
ensure(qc.numResults > 0, "Invalid numResults [" + qc.numResults + "]");
if (qc.startIndex + 1 > keys.size()) {
// no results to be fetched
return qr;
}
ensure(keys.size() >= qc.startIndex);
int numResults = Math.min(qc.numResults, keys.size() - qc.startIndex);
ensure(numResults <= keys.size());
ensure(numResults > 0);
int endIndex = qc.startIndex + numResults;
ensure(endIndex <= keys.size());
ensure(endIndex > qc.startIndex);
keys = keys.subList(qc.startIndex, endIndex);
Map<Key<InteractionEntity>, InteractionEntity> map = ofy(client).load().keys(keys);
List<InteractionProp> props = new ArrayList<>(keys.size());
for (Key<InteractionEntity> key : keys) {
props.add(map.get(key).toProp());
}
qr.interactionProps = props;
return qr;
}
static List<Key<InteractionEntity>> queryKeys(String client, InteractionQueryCondition qc) {
ensureNotNull(client, "client is null");
ensure(client.length() != 0, "client is empty");
ensureNotNull(qc, "InteractionQueryCondition is null");
Query<InteractionEntity> q = ofy(client).load().type(InteractionEntity.class);
if (qc.interactionTypes != null)
if (!qc.interactionTypes.isEmpty())
q = q.filter("interactionType in", qc.interactionTypes);
if (qc.memberIds != null)
if (!qc.memberIds.isEmpty())
q = q.filter("memberId in", qc.memberIds);
if (qc.userIds != null)
if (!qc.userIds.isEmpty())
q = q.filter("userId in", qc.userIds);
if (qc.start != null) {
q = q.filter("ms >=", qc.start.getTime());
}
if (qc.end != null) {
q = q.filter("ms <=", qc.end.getTime());
}
if (qc.campaignIds != null && !qc.campaignIds.isEmpty()) {
q = q.filter("campaignId in", qc.campaignIds);
}
List<Key<InteractionEntity>> keys = q.order("-ms").keys().list();
return keys;
}
public static InteractionEntity safeGet(String client, long interactionId) {
ensure(interactionId != 0, "interactionId is 0");
InteractionEntity interactionEntity =
ofy(client).load().type(InteractionEntity.class).id(interactionId).now();
if (null == interactionEntity)
throw new APIException().status(Status.ERROR_RESOURCE_NOT_FOUND).message(
"Interaction id [" + interactionId + "] does not exist");
return interactionEntity;
}
}