forked from Qortal/qortal
Merge branch 'master' into q-apps
This commit is contained in:
commit
98b0b1932d
@ -40,6 +40,8 @@ import org.qortal.utils.Base58;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
|
||||
import static org.qortal.data.chat.ChatMessage.Encoding;
|
||||
|
||||
@Path("/chat")
|
||||
@Tag(name = "Chat")
|
||||
public class ChatResource {
|
||||
@ -73,6 +75,7 @@ public class ChatResource {
|
||||
@QueryParam("chatreference") String chatReference,
|
||||
@QueryParam("haschatreference") Boolean hasChatReference,
|
||||
@QueryParam("sender") String sender,
|
||||
@QueryParam("encoding") Encoding encoding,
|
||||
@Parameter(ref = "limit") @QueryParam("limit") Integer limit,
|
||||
@Parameter(ref = "offset") @QueryParam("offset") Integer offset,
|
||||
@Parameter(ref = "reverse") @QueryParam("reverse") Boolean reverse) {
|
||||
@ -109,6 +112,7 @@ public class ChatResource {
|
||||
hasChatReference,
|
||||
involvingAddresses,
|
||||
sender,
|
||||
encoding,
|
||||
limit, offset, reverse);
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
@ -131,7 +135,7 @@ public class ChatResource {
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_CRITERIA, ApiError.INVALID_ADDRESS, ApiError.REPOSITORY_ISSUE})
|
||||
public ChatMessage getMessageBySignature(@PathParam("signature") String signature58) {
|
||||
public ChatMessage getMessageBySignature(@PathParam("signature") String signature58, @QueryParam("encoding") Encoding encoding) {
|
||||
byte[] signature = Base58.decode(signature58);
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
@ -141,7 +145,7 @@ public class ChatResource {
|
||||
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Message not found");
|
||||
}
|
||||
|
||||
return repository.getChatRepository().toChatMessage(chatTransactionData);
|
||||
return repository.getChatRepository().toChatMessage(chatTransactionData, encoding);
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
@ -164,12 +168,12 @@ public class ChatResource {
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_CRITERIA, ApiError.INVALID_ADDRESS, ApiError.REPOSITORY_ISSUE})
|
||||
public ActiveChats getActiveChats(@PathParam("address") String address) {
|
||||
public ActiveChats getActiveChats(@PathParam("address") String address, @QueryParam("encoding") Encoding encoding) {
|
||||
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);
|
||||
return repository.getChatRepository().getActiveChats(address, encoding);
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
|
@ -2,7 +2,9 @@ package org.qortal.api.websocket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
@ -21,6 +23,8 @@ import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.repository.RepositoryManager;
|
||||
|
||||
import static org.qortal.data.chat.ChatMessage.Encoding;
|
||||
|
||||
@WebSocket
|
||||
@SuppressWarnings("serial")
|
||||
public class ActiveChatsWebSocket extends ApiWebSocket {
|
||||
@ -62,7 +66,9 @@ public class ActiveChatsWebSocket extends ApiWebSocket {
|
||||
|
||||
@OnWebSocketMessage
|
||||
public void onWebSocketMessage(Session session, String message) {
|
||||
/* ignored */
|
||||
if (Objects.equals(message, "ping")) {
|
||||
session.getRemote().sendStringByFuture("pong");
|
||||
}
|
||||
}
|
||||
|
||||
private void onNotify(Session session, ChatTransactionData chatTransactionData, String ourAddress, AtomicReference<String> previousOutput) {
|
||||
@ -75,7 +81,7 @@ public class ActiveChatsWebSocket extends ApiWebSocket {
|
||||
}
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
ActiveChats activeChats = repository.getChatRepository().getActiveChats(ourAddress);
|
||||
ActiveChats activeChats = repository.getChatRepository().getActiveChats(ourAddress, getTargetEncoding(session));
|
||||
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
|
||||
@ -93,4 +99,12 @@ public class ActiveChatsWebSocket extends ApiWebSocket {
|
||||
}
|
||||
}
|
||||
|
||||
private Encoding getTargetEncoding(Session session) {
|
||||
// Default to Base58 if not specified, for backwards support
|
||||
Map<String, List<String>> queryParams = session.getUpgradeRequest().getParameterMap();
|
||||
List<String> encodingList = queryParams.get("encoding");
|
||||
String encoding = (encodingList != null && encodingList.size() == 1) ? encodingList.get(0) : "BASE58";
|
||||
return Encoding.valueOf(encoding);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,10 +2,7 @@ package org.qortal.api.websocket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketException;
|
||||
@ -22,6 +19,8 @@ import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.repository.RepositoryManager;
|
||||
|
||||
import static org.qortal.data.chat.ChatMessage.Encoding;
|
||||
|
||||
@WebSocket
|
||||
@SuppressWarnings("serial")
|
||||
public class ChatMessagesWebSocket extends ApiWebSocket {
|
||||
@ -35,6 +34,16 @@ public class ChatMessagesWebSocket extends ApiWebSocket {
|
||||
@Override
|
||||
public void onWebSocketConnect(Session session) {
|
||||
Map<String, List<String>> queryParams = session.getUpgradeRequest().getParameterMap();
|
||||
Encoding encoding = getTargetEncoding(session);
|
||||
|
||||
List<String> limitList = queryParams.get("limit");
|
||||
Integer limit = (limitList != null && limitList.size() == 1) ? Integer.parseInt(limitList.get(0)) : null;
|
||||
|
||||
List<String> offsetList = queryParams.get("offset");
|
||||
Integer offset = (offsetList != null && offsetList.size() == 1) ? Integer.parseInt(offsetList.get(0)) : null;
|
||||
|
||||
List<String> reverseList = queryParams.get("offset");
|
||||
Boolean reverse = (reverseList != null && reverseList.size() == 1) ? Boolean.getBoolean(reverseList.get(0)) : null;
|
||||
|
||||
List<String> txGroupIds = queryParams.get("txGroupId");
|
||||
if (txGroupIds != null && txGroupIds.size() == 1) {
|
||||
@ -50,7 +59,8 @@ public class ChatMessagesWebSocket extends ApiWebSocket {
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null, null, null);
|
||||
encoding,
|
||||
limit, offset, reverse);
|
||||
|
||||
sendMessages(session, chatMessages);
|
||||
} catch (DataException e) {
|
||||
@ -81,7 +91,8 @@ public class ChatMessagesWebSocket extends ApiWebSocket {
|
||||
null,
|
||||
involvingAddresses,
|
||||
null,
|
||||
null, null, null);
|
||||
encoding,
|
||||
limit, offset, reverse);
|
||||
|
||||
sendMessages(session, chatMessages);
|
||||
} catch (DataException e) {
|
||||
@ -107,7 +118,9 @@ public class ChatMessagesWebSocket extends ApiWebSocket {
|
||||
|
||||
@OnWebSocketMessage
|
||||
public void onWebSocketMessage(Session session, String message) {
|
||||
/* ignored */
|
||||
if (Objects.equals(message, "ping")) {
|
||||
session.getRemote().sendStringByFuture("pong");
|
||||
}
|
||||
}
|
||||
|
||||
private void onNotify(Session session, ChatTransactionData chatTransactionData, int txGroupId) {
|
||||
@ -155,7 +168,7 @@ public class ChatMessagesWebSocket extends ApiWebSocket {
|
||||
// Convert ChatTransactionData to ChatMessage
|
||||
ChatMessage chatMessage;
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
chatMessage = repository.getChatRepository().toChatMessage(chatTransactionData);
|
||||
chatMessage = repository.getChatRepository().toChatMessage(chatTransactionData, getTargetEncoding(session));
|
||||
} catch (DataException e) {
|
||||
// No output this time?
|
||||
return;
|
||||
@ -164,4 +177,12 @@ public class ChatMessagesWebSocket extends ApiWebSocket {
|
||||
sendMessages(session, Collections.singletonList(chatMessage));
|
||||
}
|
||||
|
||||
private Encoding getTargetEncoding(Session session) {
|
||||
// Default to Base58 if not specified, for backwards support
|
||||
Map<String, List<String>> queryParams = session.getUpgradeRequest().getParameterMap();
|
||||
List<String> encodingList = queryParams.get("encoding");
|
||||
String encoding = (encodingList != null && encodingList.size() == 1) ? encodingList.get(0) : "BASE58";
|
||||
return Encoding.valueOf(encoding);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,10 +1,15 @@
|
||||
package org.qortal.data.chat;
|
||||
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
import org.qortal.utils.Base58;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import static org.qortal.data.chat.ChatMessage.Encoding;
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class ActiveChats {
|
||||
|
||||
@ -18,20 +23,38 @@ public class ActiveChats {
|
||||
private String sender;
|
||||
private String senderName;
|
||||
private byte[] signature;
|
||||
private byte[] data;
|
||||
private Encoding encoding;
|
||||
private String data;
|
||||
|
||||
protected GroupChat() {
|
||||
/* JAXB */
|
||||
}
|
||||
|
||||
public GroupChat(int groupId, String groupName, Long timestamp, String sender, String senderName, byte[] signature, byte[] data) {
|
||||
public GroupChat(int groupId, String groupName, Long timestamp, String sender, String senderName,
|
||||
byte[] signature, Encoding encoding, byte[] data) {
|
||||
this.groupId = groupId;
|
||||
this.groupName = groupName;
|
||||
this.timestamp = timestamp;
|
||||
this.sender = sender;
|
||||
this.senderName = senderName;
|
||||
this.signature = signature;
|
||||
this.data = data;
|
||||
this.encoding = encoding != null ? encoding : Encoding.BASE58;
|
||||
|
||||
if (data != null) {
|
||||
switch (this.encoding) {
|
||||
case BASE64:
|
||||
this.data = Base64.toBase64String(data);
|
||||
break;
|
||||
|
||||
case BASE58:
|
||||
default:
|
||||
this.data = Base58.encode(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.data = null;
|
||||
}
|
||||
}
|
||||
|
||||
public int getGroupId() {
|
||||
@ -58,7 +81,7 @@ public class ActiveChats {
|
||||
return this.signature;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
public String getData() {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,19 @@
|
||||
package org.qortal.data.chat;
|
||||
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
import org.qortal.utils.Base58;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class ChatMessage {
|
||||
|
||||
public enum Encoding {
|
||||
BASE58,
|
||||
BASE64
|
||||
}
|
||||
|
||||
// Properties
|
||||
|
||||
private long timestamp;
|
||||
@ -29,7 +37,9 @@ public class ChatMessage {
|
||||
|
||||
private byte[] chatReference;
|
||||
|
||||
private byte[] data;
|
||||
private Encoding encoding;
|
||||
|
||||
private String data;
|
||||
|
||||
private boolean isText;
|
||||
private boolean isEncrypted;
|
||||
@ -44,8 +54,8 @@ public class ChatMessage {
|
||||
|
||||
// For repository use
|
||||
public ChatMessage(long timestamp, int txGroupId, byte[] reference, byte[] senderPublicKey, String sender,
|
||||
String senderName, String recipient, String recipientName, byte[] chatReference, byte[] data,
|
||||
boolean isText, boolean isEncrypted, byte[] signature) {
|
||||
String senderName, String recipient, String recipientName, byte[] chatReference,
|
||||
Encoding encoding, byte[] data, boolean isText, boolean isEncrypted, byte[] signature) {
|
||||
this.timestamp = timestamp;
|
||||
this.txGroupId = txGroupId;
|
||||
this.reference = reference;
|
||||
@ -55,7 +65,24 @@ public class ChatMessage {
|
||||
this.recipient = recipient;
|
||||
this.recipientName = recipientName;
|
||||
this.chatReference = chatReference;
|
||||
this.data = data;
|
||||
this.encoding = encoding != null ? encoding : Encoding.BASE58;
|
||||
|
||||
if (data != null) {
|
||||
switch (this.encoding) {
|
||||
case BASE64:
|
||||
this.data = Base64.toBase64String(data);
|
||||
break;
|
||||
|
||||
case BASE58:
|
||||
default:
|
||||
this.data = Base58.encode(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.data = null;
|
||||
}
|
||||
|
||||
this.isText = isText;
|
||||
this.isEncrypted = isEncrypted;
|
||||
this.signature = signature;
|
||||
@ -97,7 +124,7 @@ public class ChatMessage {
|
||||
return this.chatReference;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
public String getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,8 @@ import org.qortal.data.chat.ActiveChats;
|
||||
import org.qortal.data.chat.ChatMessage;
|
||||
import org.qortal.data.transaction.ChatTransactionData;
|
||||
|
||||
import static org.qortal.data.chat.ChatMessage.Encoding;
|
||||
|
||||
public interface ChatRepository {
|
||||
|
||||
/**
|
||||
@ -15,10 +17,11 @@ public interface ChatRepository {
|
||||
*/
|
||||
public List<ChatMessage> getMessagesMatchingCriteria(Long before, Long after,
|
||||
Integer txGroupId, byte[] reference, byte[] chatReferenceBytes, Boolean hasChatReference,
|
||||
List<String> involving, String senderAddress, Integer limit, Integer offset, Boolean reverse) throws DataException;
|
||||
List<String> involving, String senderAddress, Encoding encoding,
|
||||
Integer limit, Integer offset, Boolean reverse) throws DataException;
|
||||
|
||||
public ChatMessage toChatMessage(ChatTransactionData chatTransactionData) throws DataException;
|
||||
public ChatMessage toChatMessage(ChatTransactionData chatTransactionData, Encoding encoding) throws DataException;
|
||||
|
||||
public ActiveChats getActiveChats(String address) throws DataException;
|
||||
public ActiveChats getActiveChats(String address, Encoding encoding) throws DataException;
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,8 @@ import org.qortal.repository.ChatRepository;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.transaction.Transaction.TransactionType;
|
||||
|
||||
import static org.qortal.data.chat.ChatMessage.Encoding;
|
||||
|
||||
public class HSQLDBChatRepository implements ChatRepository {
|
||||
|
||||
protected HSQLDBRepository repository;
|
||||
@ -24,8 +26,8 @@ public class HSQLDBChatRepository implements ChatRepository {
|
||||
|
||||
@Override
|
||||
public List<ChatMessage> getMessagesMatchingCriteria(Long before, Long after, Integer txGroupId, byte[] referenceBytes,
|
||||
byte[] chatReferenceBytes, Boolean hasChatReference, List<String> involving, String senderAddress,
|
||||
Integer limit, Integer offset, Boolean reverse) throws DataException {
|
||||
byte[] chatReferenceBytes, Boolean hasChatReference, List<String> involving, String senderAddress,
|
||||
Encoding encoding, Integer limit, Integer offset, Boolean reverse) throws DataException {
|
||||
// Check args meet expectations
|
||||
if ((txGroupId != null && involving != null && !involving.isEmpty())
|
||||
|| (txGroupId == null && (involving == null || involving.size() != 2)))
|
||||
@ -127,7 +129,7 @@ public class HSQLDBChatRepository implements ChatRepository {
|
||||
byte[] signature = resultSet.getBytes(13);
|
||||
|
||||
ChatMessage chatMessage = new ChatMessage(timestamp, groupId, reference, senderPublicKey, sender,
|
||||
senderName, recipient, recipientName, chatReference, data, isText, isEncrypted, signature);
|
||||
senderName, recipient, recipientName, chatReference, encoding, data, isText, isEncrypted, signature);
|
||||
|
||||
chatMessages.add(chatMessage);
|
||||
} while (resultSet.next());
|
||||
@ -139,7 +141,7 @@ public class HSQLDBChatRepository implements ChatRepository {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatMessage toChatMessage(ChatTransactionData chatTransactionData) throws DataException {
|
||||
public ChatMessage toChatMessage(ChatTransactionData chatTransactionData, Encoding encoding) throws DataException {
|
||||
String sql = "SELECT SenderNames.name, RecipientNames.name "
|
||||
+ "FROM ChatTransactions "
|
||||
+ "LEFT OUTER JOIN Names AS SenderNames ON SenderNames.owner = sender "
|
||||
@ -166,21 +168,22 @@ public class HSQLDBChatRepository implements ChatRepository {
|
||||
byte[] signature = chatTransactionData.getSignature();
|
||||
|
||||
return new ChatMessage(timestamp, groupId, reference, senderPublicKey, sender,
|
||||
senderName, recipient, recipientName, chatReference, data, isText, isEncrypted, signature);
|
||||
senderName, recipient, recipientName, chatReference, encoding, data,
|
||||
isText, isEncrypted, signature);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch convert chat transaction from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActiveChats getActiveChats(String address) throws DataException {
|
||||
List<GroupChat> groupChats = getActiveGroupChats(address);
|
||||
public ActiveChats getActiveChats(String address, Encoding encoding) throws DataException {
|
||||
List<GroupChat> groupChats = getActiveGroupChats(address, encoding);
|
||||
List<DirectChat> directChats = getActiveDirectChats(address);
|
||||
|
||||
return new ActiveChats(groupChats, directChats);
|
||||
}
|
||||
|
||||
private List<GroupChat> getActiveGroupChats(String address) throws DataException {
|
||||
private List<GroupChat> getActiveGroupChats(String address, Encoding encoding) throws DataException {
|
||||
// Find groups where address is a member and potential latest message details
|
||||
String groupsSql = "SELECT group_id, group_name, latest_timestamp, sender, sender_name, signature, data "
|
||||
+ "FROM GroupMembers "
|
||||
@ -213,7 +216,7 @@ public class HSQLDBChatRepository implements ChatRepository {
|
||||
byte[] signature = resultSet.getBytes(6);
|
||||
byte[] data = resultSet.getBytes(7);
|
||||
|
||||
GroupChat groupChat = new GroupChat(groupId, groupName, timestamp, sender, senderName, signature, data);
|
||||
GroupChat groupChat = new GroupChat(groupId, groupName, timestamp, sender, senderName, signature, encoding, data);
|
||||
groupChats.add(groupChat);
|
||||
} while (resultSet.next());
|
||||
}
|
||||
@ -247,7 +250,7 @@ public class HSQLDBChatRepository implements ChatRepository {
|
||||
data = resultSet.getBytes(5);
|
||||
}
|
||||
|
||||
GroupChat groupChat = new GroupChat(0, null, timestamp, sender, senderName, signature, data);
|
||||
GroupChat groupChat = new GroupChat(0, null, timestamp, sender, senderName, signature, encoding, data);
|
||||
groupChats.add(groupChat);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch active group chats from repository", e);
|
||||
|
@ -9,6 +9,7 @@ import org.qortal.crosschain.BitcoinACCTv1;
|
||||
import org.qortal.crypto.Crypto;
|
||||
import org.qortal.data.account.AccountBalanceData;
|
||||
import org.qortal.data.account.AccountData;
|
||||
import org.qortal.data.chat.ChatMessage;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.repository.RepositoryManager;
|
||||
@ -417,7 +418,7 @@ public class RepositoryTests extends Common {
|
||||
try (final HSQLDBRepository hsqldb = (HSQLDBRepository) RepositoryManager.getRepository()) {
|
||||
String address = Crypto.toAddress(new byte[32]);
|
||||
|
||||
hsqldb.getChatRepository().getActiveChats(address);
|
||||
hsqldb.getChatRepository().getActiveChats(address, ChatMessage.Encoding.BASE58);
|
||||
} catch (DataException e) {
|
||||
fail("HSQLDB bug #1580");
|
||||
}
|
||||
|
@ -8,11 +8,11 @@ if [ -z "$*" ]; then
|
||||
echo "Usage:"
|
||||
echo
|
||||
echo "Host/update data:"
|
||||
echo "qdata POST [service] [name] PATH [dirpath] <identifier>"
|
||||
echo "qdata POST [service] [name] STRING [data-string] <identifier>"
|
||||
echo "qdn POST [service] [name] PATH [dirpath] <identifier>"
|
||||
echo "qdn POST [service] [name] STRING [data-string] <identifier>"
|
||||
echo
|
||||
echo "Fetch data:"
|
||||
echo "qdata GET [service] [name] <identifier-or-default> <filepath-or-default> <rebuild>"
|
||||
echo "qdn GET [service] [name] <identifier-or-default> <filepath-or-default> <rebuild>"
|
||||
echo
|
||||
echo "Notes:"
|
||||
echo "- When requesting a resource, please use 'default' to indicate a file with no identifier."
|
||||
|
Loading…
x
Reference in New Issue
Block a user