package org.rakam.plugin.user.mailbox; import com.facebook.presto.sql.tree.Expression; import com.fasterxml.jackson.annotation.JsonCreator; import io.netty.handler.codec.http.HttpResponseStatus; import org.rakam.collection.SchemaField; import org.rakam.plugin.user.AbstractUserService; import org.rakam.plugin.user.User; import org.rakam.plugin.user.UserActionService; import org.rakam.plugin.user.UserStorage; import org.rakam.plugin.user.UserStorage.EventFilter; import org.rakam.report.QueryResult; import org.rakam.server.http.annotations.Api; import org.rakam.server.http.annotations.ApiOperation; import org.rakam.server.http.annotations.ApiParam; import org.rakam.server.http.annotations.ApiResponse; import org.rakam.server.http.annotations.ApiResponses; import org.rakam.server.http.annotations.Authorization; import org.rakam.server.http.annotations.JsonRequest; import org.rakam.util.RakamException; import org.rakam.util.StringTemplate; import javax.inject.Inject; import javax.inject.Named; import javax.ws.rs.POST; import javax.ws.rs.Path; import java.time.Instant; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import static org.rakam.plugin.user.UserHttpService.parseExpression; @Path("/user/action/mailbox") @Api(value = "/user/action/mailbox", nickname = "userMailboxAction", description = "Mailbox action", tags = "user-action") public class UserMailboxActionService extends UserActionService<UserMailboxActionService.MailAction> { private final AbstractUserService userService; private final UserMailboxStorage mailboxStorage; @Inject public UserMailboxActionService(AbstractUserService userService, UserMailboxStorage mailboxStorage) { this.userService = userService; this.mailboxStorage = mailboxStorage; } @JsonRequest @ApiOperation(value = "Apply batch operation", authorizations = @Authorization(value = "read_key")) @Path("/batch") public CompletableFuture<Long> batchSendMessages(@Named("project") String project, @ApiParam(value = "filter", required = false) String filter, @ApiParam(value = "event_filters", required = false) List<EventFilter> event_filter, @ApiParam("config") MailAction config) { List<String> variables = new StringTemplate(config.message).getVariables(); variables.add(UserStorage.PRIMARY_KEY); Expression expression = parseExpression(filter); CompletableFuture<QueryResult> future = userService.searchUsers(project, variables, expression, event_filter, null, 100000, null); return batch(project, future, config); } @Override public String getName() { return "mailbox"; } @Override public boolean send(String project, User user, MailAction config) { mailboxStorage.send(project, config.fromUser, user.id, null, config.message, Instant.now()); return true; } public static class MailAction { public final String fromUser; public final String message; public final Map<String, String> variables; @JsonCreator public MailAction(@ApiParam("from_user") String fromUser, @ApiParam("message") String message, @ApiParam("variables") Map<String, String> variables) { this.fromUser = fromUser; this.message = message; this.variables = variables; } } @Override public CompletableFuture<Long> batch(String project, CompletableFuture<QueryResult> queryResult, MailAction action) { StringTemplate template = new StringTemplate(action.message); return queryResult.thenApply(result -> { List<SchemaField> metadata = result.getMetadata(); int key = metadata.indexOf(metadata.stream().filter(a -> a.getName().equals(UserStorage.PRIMARY_KEY)).findAny().get()); Map<String, Integer> map = generateColumnMap(template.getVariables(), metadata); for (List<Object> objects : result.getResult()) { final String userId = objects.get(key).toString(); String format = template.format(name -> { Integer index = map.get(name); if(index != null) { Object o = objects.get(index); if (o != null && o instanceof String) { return o.toString(); } } return action.variables.get(name); }); mailboxStorage.send(project, action.fromUser, userId, null, format, Instant.now()); } return (long) result.getResult().size(); }); } @Path("/action/mailbox/single") @POST @JsonRequest @ApiOperation(value = "Send message to user", notes = "Sends a mail to users mailbox", authorizations = @Authorization(value = "write_key") ) @ApiResponses(value = {@ApiResponse(code = 404, message = "User does not exist.")}) public Message sendMail(@Named("project") String project, @ApiParam("from_user") String fromUser, @ApiParam("to_user") String toUser, @ApiParam(value = "parent", description = "Parent message id", required = false) Integer parent, @ApiParam(value = "message", description = "The content of the message", required = false) String message, @ApiParam(value = "timestamp", description = "The timestamp of the message") long datetime) { try { return mailboxStorage.send(project, fromUser, toUser, parent, message, Instant.ofEpochMilli(datetime)); } catch (Exception e) { throw new RakamException("Error while sending message: "+e.getMessage(), HttpResponseStatus.BAD_REQUEST); } } private Map<String, Integer> generateColumnMap(List<String> variables, List<SchemaField> metadata) { HashMap<String, Integer> colMap = new HashMap<>(variables.size()); for (String var : variables) { for (int i = 0; i < metadata.size(); i++) { if(metadata.get(i).getName().equals(var)) { colMap.put(variables.get(i), i); break; } } } return colMap; } }