forked from Qortal/qortal
More work on CHAT API / support.
Renamed GET /chats/search to /chats/messages. Added GET /chats/active/{address} to return lists of group chats and direct chats involving {address}, where a chat message exists.
This commit is contained in:
parent
32470fa641
commit
a9852e5305
@ -15,6 +15,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
@ -24,6 +25,7 @@ import org.qortal.api.ApiErrors;
|
||||
import org.qortal.api.ApiExceptionFactory;
|
||||
import org.qortal.api.Security;
|
||||
import org.qortal.crypto.Crypto;
|
||||
import org.qortal.data.chat.ActiveChats;
|
||||
import org.qortal.data.chat.ChatMessage;
|
||||
import org.qortal.data.transaction.ChatTransactionData;
|
||||
import org.qortal.data.transaction.TransactionData;
|
||||
@ -49,7 +51,7 @@ public class ChatResource {
|
||||
HttpServletRequest request;
|
||||
|
||||
@GET
|
||||
@Path("/search")
|
||||
@Path("/messages")
|
||||
@Operation(
|
||||
summary = "Find chat messages",
|
||||
description = "Returns CHAT messages that match criteria. Must provide EITHER 'txGroupId' OR two 'involving' addresses.",
|
||||
@ -100,6 +102,34 @@ public class ChatResource {
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/active/{address}")
|
||||
@Operation(
|
||||
summary = "Find active chats (group/direct) involving address",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(
|
||||
array = @ArraySchema(
|
||||
schema = @Schema(
|
||||
implementation = ActiveChats.class
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_CRITERIA, ApiError.INVALID_ADDRESS, ApiError.REPOSITORY_ISSUE})
|
||||
public ActiveChats getActiveChats(@PathParam("address") String address) {
|
||||
if (address == null || !Crypto.isValidAddress(address))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS);
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
return repository.getChatRepository().getActiveChats(address);
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Operation(
|
||||
summary = "Build raw, unsigned, CHAT transaction",
|
||||
|
95
src/main/java/org/qortal/data/chat/ActiveChats.java
Normal file
95
src/main/java/org/qortal/data/chat/ActiveChats.java
Normal file
@ -0,0 +1,95 @@
|
||||
package org.qortal.data.chat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class ActiveChats {
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public static class GroupChat {
|
||||
private int groupId;
|
||||
private String groupName;
|
||||
private long timestamp;
|
||||
|
||||
protected GroupChat() {
|
||||
/* JAXB */
|
||||
}
|
||||
|
||||
public GroupChat(int groupId, String groupName, long timestamp) {
|
||||
this.groupId = groupId;
|
||||
this.groupName = groupName;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public int getGroupId() {
|
||||
return this.groupId;
|
||||
}
|
||||
|
||||
public String getGroupName() {
|
||||
return this.groupName;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return this.timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public static class DirectChat {
|
||||
private String address;
|
||||
private String name;
|
||||
private long timestamp;
|
||||
|
||||
protected DirectChat() {
|
||||
/* JAXB */
|
||||
}
|
||||
|
||||
public DirectChat(String address, String name, long timestamp) {
|
||||
this.address = address;
|
||||
this.name = name;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return this.address;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return this.timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
// Properties
|
||||
|
||||
private List<GroupChat> groups;
|
||||
|
||||
private List<DirectChat> direct;
|
||||
|
||||
// Constructors
|
||||
|
||||
protected ActiveChats() {
|
||||
/* For JAXB */
|
||||
}
|
||||
|
||||
// For repository use
|
||||
public ActiveChats(List<GroupChat> groups, List<DirectChat> direct) {
|
||||
this.groups = groups;
|
||||
this.direct = direct;
|
||||
}
|
||||
|
||||
public List<GroupChat> getGroups() {
|
||||
return this.groups;
|
||||
}
|
||||
|
||||
public List<DirectChat> getDirect() {
|
||||
return this.direct;
|
||||
}
|
||||
|
||||
}
|
@ -17,15 +17,9 @@ public class ChatMessage {
|
||||
/* Address of sender */
|
||||
private String sender;
|
||||
|
||||
/* Registered name of sender (if any) */
|
||||
private String senderName; // can be null
|
||||
|
||||
/* Address of recipient (if any) */
|
||||
private String recipient; // can be null
|
||||
|
||||
/* Registered name of recipient (if any) */
|
||||
private String recipientName; // can be null
|
||||
|
||||
private byte[] data;
|
||||
|
||||
private boolean isText;
|
||||
@ -38,15 +32,13 @@ public class ChatMessage {
|
||||
}
|
||||
|
||||
// For repository use
|
||||
public ChatMessage(long timestamp, int txGroupId, byte[] senderPublicKey, String sender, String senderName,
|
||||
String recipient, String recipientName, byte[] data, boolean isText, boolean isEncrypted) {
|
||||
public ChatMessage(long timestamp, int txGroupId, byte[] senderPublicKey, String sender,
|
||||
String recipient, byte[] data, boolean isText, boolean isEncrypted) {
|
||||
this.timestamp = timestamp;
|
||||
this.txGroupId = txGroupId;
|
||||
this.senderPublicKey = senderPublicKey;
|
||||
this.sender = sender;
|
||||
this.senderName = senderName;
|
||||
this.recipient = recipient;
|
||||
this.recipientName = recipientName;
|
||||
this.data = data;
|
||||
this.isText = isText;
|
||||
this.isEncrypted = isEncrypted;
|
||||
@ -68,18 +60,10 @@ public class ChatMessage {
|
||||
return this.sender;
|
||||
}
|
||||
|
||||
public String getSenderName() {
|
||||
return this.senderName;
|
||||
}
|
||||
|
||||
public String getRecipient() {
|
||||
return this.recipient;
|
||||
}
|
||||
|
||||
public String getRecipientName() {
|
||||
return this.recipientName;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package org.qortal.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.qortal.data.chat.ActiveChats;
|
||||
import org.qortal.data.chat.ChatMessage;
|
||||
|
||||
public interface ChatRepository {
|
||||
@ -15,4 +16,6 @@ public interface ChatRepository {
|
||||
Integer txGroupId, List<String> involving,
|
||||
Integer limit, Integer offset, Boolean reverse) throws DataException;
|
||||
|
||||
public ActiveChats getActiveChats(String address) throws DataException;
|
||||
|
||||
}
|
||||
|
@ -5,9 +5,13 @@ import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.qortal.data.chat.ActiveChats;
|
||||
import org.qortal.data.chat.ActiveChats.DirectChat;
|
||||
import org.qortal.data.chat.ActiveChats.GroupChat;
|
||||
import org.qortal.data.chat.ChatMessage;
|
||||
import org.qortal.repository.ChatRepository;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.transaction.Transaction.TransactionType;
|
||||
|
||||
public class HSQLDBChatRepository implements ChatRepository {
|
||||
|
||||
@ -28,12 +32,9 @@ public class HSQLDBChatRepository implements ChatRepository {
|
||||
|
||||
StringBuilder sql = new StringBuilder(1024);
|
||||
|
||||
sql.append("SELECT created_when, tx_group_id, creator, sender, SenderNames.name, "
|
||||
+ "recipient, RecipientNames.name, data, is_text, is_encrypted "
|
||||
sql.append("SELECT created_when, tx_group_id, creator, sender, recipient, data, is_text, is_encrypted "
|
||||
+ "FROM ChatTransactions "
|
||||
+ "JOIN Transactions USING (signature) "
|
||||
+ "LEFT OUTER JOIN Names AS SenderNames ON SenderNames.owner = sender "
|
||||
+ "LEFT OUTER JOIN Names AS RecipientNames ON RecipientNames.owner = recipient ");
|
||||
+ "JOIN Transactions USING (signature) ");
|
||||
|
||||
// WHERE clauses
|
||||
|
||||
@ -88,15 +89,13 @@ public class HSQLDBChatRepository implements ChatRepository {
|
||||
int groupId = resultSet.getInt(2);
|
||||
byte[] senderPublicKey = resultSet.getBytes(3);
|
||||
String sender = resultSet.getString(4);
|
||||
String senderName = resultSet.getString(5);
|
||||
String recipient = resultSet.getString(6);
|
||||
String recipientName = resultSet.getString(7);
|
||||
byte[] data = resultSet.getBytes(8);
|
||||
boolean isText = resultSet.getBoolean(9);
|
||||
boolean isEncrypted = resultSet.getBoolean(10);
|
||||
String recipient = resultSet.getString(5);
|
||||
byte[] data = resultSet.getBytes(6);
|
||||
boolean isText = resultSet.getBoolean(7);
|
||||
boolean isEncrypted = resultSet.getBoolean(8);
|
||||
|
||||
ChatMessage chatMessage = new ChatMessage(timestamp, groupId, senderPublicKey, sender,
|
||||
senderName, recipient, recipientName, data, isText, isEncrypted);
|
||||
recipient, data, isText, isEncrypted);
|
||||
|
||||
chatMessages.add(chatMessage);
|
||||
} while (resultSet.next());
|
||||
@ -107,4 +106,88 @@ public class HSQLDBChatRepository implements ChatRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActiveChats getActiveChats(String address) throws DataException {
|
||||
List<GroupChat> groupChats = getActiveGroupChats(address);
|
||||
List<DirectChat> directChats = getActiveDirectChats(address);
|
||||
|
||||
return new ActiveChats(groupChats, directChats);
|
||||
}
|
||||
|
||||
private List<GroupChat> getActiveGroupChats(String address) throws DataException {
|
||||
// Find groups where address is a member and there is a chat
|
||||
String groupsSql = "SELECT group_id, group_name, latest_timestamp "
|
||||
+ "FROM GroupMembers "
|
||||
+ "JOIN Groups USING (group_id) "
|
||||
+ "CROSS JOIN LATERAL("
|
||||
+ "SELECT created_when "
|
||||
+ "FROM Transactions "
|
||||
+ "WHERE tx_group_id = Groups.group_id AND type = " + TransactionType.CHAT.value + " "
|
||||
+ "ORDER BY created_when DESC "
|
||||
+ "LIMIT 1"
|
||||
+ ") AS LatestMessages (latest_timestamp) "
|
||||
+ "WHERE address = ?";
|
||||
|
||||
List<GroupChat> groupChats = new ArrayList<>();
|
||||
try (ResultSet resultSet = this.repository.checkedExecute(groupsSql, address)) {
|
||||
if (resultSet == null)
|
||||
return groupChats;
|
||||
|
||||
do {
|
||||
int groupId = resultSet.getInt(1);
|
||||
String groupName = resultSet.getString(2);
|
||||
long timestamp = resultSet.getLong(3);
|
||||
|
||||
GroupChat groupChat = new GroupChat(groupId, groupName, timestamp);
|
||||
groupChats.add(groupChat);
|
||||
} while (resultSet.next());
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch active group chats from repository", e);
|
||||
}
|
||||
|
||||
return groupChats;
|
||||
}
|
||||
|
||||
private List<DirectChat> getActiveDirectChats(String address) throws DataException {
|
||||
// Find chat messages involving address
|
||||
String directSql = "SELECT other_address, name, latest_timestamp "
|
||||
+ "FROM ("
|
||||
+ "SELECT recipient FROM ChatTransactions "
|
||||
+ "WHERE sender = ? AND recipient IS NOT NULL "
|
||||
+ "UNION "
|
||||
+ "SELECT sender FROM ChatTransactions "
|
||||
+ "WHERE recipient = ?"
|
||||
+ ") AS OtherParties (other_address) "
|
||||
+ "CROSS JOIN LATERAL("
|
||||
+ "SELECT created_when FROM ChatTransactions "
|
||||
+ "NATURAL JOIN Transactions "
|
||||
+ "WHERE (sender = other_address AND recipient = ?) "
|
||||
+ "OR (sender = ? AND recipient = other_address) "
|
||||
+ "ORDER BY created_when DESC "
|
||||
+ "LIMIT 1"
|
||||
+ ") AS LatestMessages (latest_timestamp) "
|
||||
+ "LEFT OUTER JOIN Names ON owner = other_address";
|
||||
|
||||
Object[] bindParams = new Object[] { address, address, address, address };
|
||||
|
||||
List<DirectChat> directChats = new ArrayList<>();
|
||||
try (ResultSet resultSet = this.repository.checkedExecute(directSql, bindParams)) {
|
||||
if (resultSet == null)
|
||||
return directChats;
|
||||
|
||||
do {
|
||||
String otherAddress = resultSet.getString(1);
|
||||
String name = resultSet.getString(2);
|
||||
long timestamp = resultSet.getLong(3);
|
||||
|
||||
DirectChat directChat = new DirectChat(otherAddress, name, timestamp);
|
||||
directChats.add(directChat);
|
||||
} while (resultSet.next());
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch active direct chats from repository", e);
|
||||
}
|
||||
|
||||
return directChats;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user