From 7f5982734f895bb9ca7c62498e0a06f4120ebbd1 Mon Sep 17 00:00:00 2001 From: spoonman01 Date: Mon, 30 Sep 2024 12:22:11 +0200 Subject: [PATCH 1/5] feat(get-domain-from-backend) Use new Helium API to fetch backend domain * Update Helium to 1.4.1 * Add assertions to major tests * Remove Consts.java from tests --- pom.xml | 3 +- .../wire/bots/hold/HoldMessageResource.java | 2 +- .../com/wire/bots/hold/MessageHandler.java | 71 ++++++++------- .../wire/bots/hold/NotificationProcessor.java | 2 +- src/main/java/com/wire/bots/hold/Service.java | 18 +++- .../bots/hold/healthchecks/SanityCheck.java | 6 +- .../hold/resource/ConversationResource.java | 8 +- .../java/com/wire/bots/hold/utils/Cache.java | 9 +- .../com/wire/bots/hold/utils/Collector.java | 9 +- .../java/com/wire/bots/hold/utils/Helper.java | 7 +- .../wire/bots/hold/utils/HoldClientRepo.java | 7 +- .../wire/bots/hold/utils/HoldWireClient.java | 11 +-- src/test/java/com/wire/bots/hold/Consts.java | 8 -- .../java/com/wire/bots/hold/DatabaseTest.java | 24 ++--- .../wire/bots/hold/MessageTemplateTest.java | 87 ++++++++++--------- ...ialization.java => SerializationTest.java} | 6 +- .../com/wire/bots/hold/utils/TestCache.java | 17 ++-- 17 files changed, 162 insertions(+), 133 deletions(-) delete mode 100644 src/test/java/com/wire/bots/hold/Consts.java rename src/test/java/com/wire/bots/hold/{Serialization.java => SerializationTest.java} (94%) diff --git a/pom.xml b/pom.xml index 7eba379..b5e93dc 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,6 @@ 2.1.2 1.0.10 0.16.0 - 1.3.0 @@ -68,7 +67,7 @@ com.wire helium - ${helium.version} + 1.4.1 com.github.smoketurner diff --git a/src/main/java/com/wire/bots/hold/HoldMessageResource.java b/src/main/java/com/wire/bots/hold/HoldMessageResource.java index d03271f..3e455fd 100644 --- a/src/main/java/com/wire/bots/hold/HoldMessageResource.java +++ b/src/main/java/com/wire/bots/hold/HoldMessageResource.java @@ -20,7 +20,7 @@ public HoldMessageResource(MessageHandlerBase handler, HoldClientRepo repo) { } protected WireClient getWireClient(UUID userId, Payload payload) throws CryptoException { - return repo.getClient(userId, payload.data.recipient, payload.convId); + return repo.getClient(userId, payload.data.recipient, payload.conversation); } public boolean onNewMessage(UUID userId, UUID id, Payload payload) { diff --git a/src/main/java/com/wire/bots/hold/MessageHandler.java b/src/main/java/com/wire/bots/hold/MessageHandler.java index 1d8b854..bf6a0a0 100644 --- a/src/main/java/com/wire/bots/hold/MessageHandler.java +++ b/src/main/java/com/wire/bots/hold/MessageHandler.java @@ -5,6 +5,7 @@ import com.wire.bots.hold.DAO.EventsDAO; import com.wire.xenon.MessageHandlerBase; import com.wire.xenon.WireClient; +import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.backend.models.SystemMessage; import com.wire.xenon.models.*; import com.wire.xenon.tools.Logger; @@ -26,81 +27,81 @@ public class MessageHandler extends MessageHandlerBase { @Override public void onNewConversation(WireClient client, SystemMessage msg) { UUID eventId = msg.id; - UUID convId = msg.convId; + QualifiedId conversationId = msg.conversation.id; UUID userId = client.getId(); String type = msg.type; - persist(eventId, convId, userId, type, msg); + persist(eventId, conversationId.id, userId, type, msg); } @Override public void onConversationRename(WireClient client, SystemMessage msg) { UUID eventId = msg.id; - UUID convId = msg.convId; + QualifiedId conversationId = msg.conversation.id; UUID userId = client.getId(); String type = Const.CONVERSATION_RENAME; - persist(eventId, convId, userId, type, msg); + persist(eventId, conversationId.id, userId, type, msg); } @Override public void onMemberJoin(WireClient client, SystemMessage msg) { UUID eventId = msg.id; - UUID convId = msg.convId; + QualifiedId conversationId = msg.conversation.id; UUID userId = client.getId(); String type = msg.type; - persist(eventId, convId, userId, type, msg); + persist(eventId, conversationId.id, userId, type, msg); } @Override public void onMemberLeave(WireClient client, SystemMessage msg) { UUID eventId = msg.id; - UUID convId = msg.convId; + QualifiedId conversationId = msg.conversation.id; UUID userId = client.getId(); String type = msg.type; - persist(eventId, convId, userId, type, msg); + persist(eventId, conversationId.id, userId, type, msg); } @Override public void onText(WireClient client, TextMessage msg) { UUID eventId = msg.getEventId(); - UUID convId = msg.getConversationId(); + QualifiedId conversationId = msg.getConversationId(); UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_NEW_TEXT; - persist(eventId, convId, userId, type, msg); + persist(eventId, conversationId.id, userId, type, msg); } @Override public void onText(WireClient client, EphemeralTextMessage msg) { UUID eventId = msg.getEventId(); - UUID convId = msg.getConversationId(); + QualifiedId conversationId = msg.getConversationId(); UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_NEW_TEXT; - persist(eventId, convId, userId, type, msg); + persist(eventId, conversationId.id, userId, type, msg); } @Override public void onEditText(WireClient client, EditedTextMessage msg) { UUID eventId = msg.getEventId(); - UUID convId = msg.getConversationId(); + QualifiedId conversationId = msg.getConversationId(); UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_EDIT_TEXT; - persist(eventId, convId, userId, type, msg); + persist(eventId, conversationId.id, userId, type, msg); } @Override public void onPhotoPreview(WireClient client, PhotoPreviewMessage msg) { UUID eventId = msg.getEventId(); - UUID convId = msg.getConversationId(); + QualifiedId conversationId = msg.getConversationId(); UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_IMAGE_PREVIEW; - persist(eventId, convId, userId, type, msg); + persist(eventId, conversationId.id, userId, type, msg); assetsDAO.insert(msg.getMessageId(), msg.getMimeType()); } @@ -108,11 +109,11 @@ public void onPhotoPreview(WireClient client, PhotoPreviewMessage msg) { @Override public void onFilePreview(WireClient client, FilePreviewMessage msg) { UUID eventId = msg.getEventId(); - UUID convId = msg.getConversationId(); + QualifiedId conversationId = msg.getConversationId(); UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_FILE_PREVIEW; - persist(eventId, convId, userId, type, msg); + persist(eventId, conversationId.id, userId, type, msg); assetsDAO.insert(msg.getMessageId(), msg.getMimeType()); } @@ -120,11 +121,11 @@ public void onFilePreview(WireClient client, FilePreviewMessage msg) { @Override public void onAudioPreview(WireClient client, AudioPreviewMessage msg) { UUID eventId = msg.getEventId(); - UUID convId = msg.getConversationId(); + QualifiedId conversationId = msg.getConversationId(); UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_AUDIO_PREVIEW; - persist(eventId, convId, userId, type, msg); + persist(eventId, conversationId.id, userId, type, msg); assetsDAO.insert(msg.getMessageId(), msg.getMimeType()); } @@ -132,11 +133,11 @@ public void onAudioPreview(WireClient client, AudioPreviewMessage msg) { @Override public void onVideoPreview(WireClient client, VideoPreviewMessage msg) { UUID eventId = msg.getEventId(); - UUID convId = msg.getConversationId(); + QualifiedId conversationId = msg.getConversationId(); UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_VIDEO_PREVIEW; - persist(eventId, convId, userId, type, msg); + persist(eventId, conversationId.id, userId, type, msg); assetsDAO.insert(msg.getMessageId(), msg.getMimeType()); } @@ -144,14 +145,20 @@ public void onVideoPreview(WireClient client, VideoPreviewMessage msg) { @Override public void onAssetData(WireClient client, RemoteMessage msg) { UUID eventId = msg.getEventId(); - UUID convId = msg.getConversationId(); + QualifiedId conversationId = msg.getConversationId(); UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_ASSET_DATA; - persist(eventId, convId, userId, type, msg); + persist(eventId, conversationId.id, userId, type, msg); try { - final byte[] assetData = client.downloadAsset(msg.getAssetId(), msg.getAssetToken(), msg.getSha256(), msg.getOtrKey()); + final byte[] assetData = client.downloadAsset( + msg.getAssetId(), + null, // TODO(WPB-11287): Change null to default domain + msg.getAssetToken(), + msg.getSha256(), + msg.getOtrKey() + ); assetsDAO.insert(msg.getMessageId(), assetData); } catch (Exception e) { Logger.exception(e, "onAssetData"); @@ -161,34 +168,34 @@ public void onAssetData(WireClient client, RemoteMessage msg) { @Override public void onDelete(WireClient client, DeletedTextMessage msg) { UUID eventId = msg.getEventId(); - UUID convId = msg.getConversationId(); + QualifiedId conversationId = msg.getConversationId(); UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_DELETE_TEXT; - persist(eventId, convId, userId, type, msg); + persist(eventId, conversationId.id, userId, type, msg); } @Override public void onCalling(WireClient client, CallingMessage msg) { UUID eventId = msg.getEventId(); - UUID convId = msg.getConversationId(); + QualifiedId conversationId = msg.getConversationId(); UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_CALL; - persist(eventId, convId, userId, type, msg); + persist(eventId, conversationId.id, userId, type, msg); } public void onReaction(WireClient client, ReactionMessage msg) { UUID eventId = msg.getEventId(); - UUID convId = msg.getConversationId(); + QualifiedId conversationId = msg.getConversationId(); UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_REACTION; - persist(eventId, convId, userId, type, msg); + persist(eventId, conversationId.id, userId, type, msg); } @Override - public boolean onConnectRequest(WireClient client, UUID from, UUID to, String status) { + public boolean onConnectRequest(WireClient client, QualifiedId from, QualifiedId to, String status) { return false; } diff --git a/src/main/java/com/wire/bots/hold/NotificationProcessor.java b/src/main/java/com/wire/bots/hold/NotificationProcessor.java index 2303919..3cf36c2 100644 --- a/src/main/java/com/wire/bots/hold/NotificationProcessor.java +++ b/src/main/java/com/wire/bots/hold/NotificationProcessor.java @@ -103,7 +103,7 @@ private void process(UUID userId, NotificationList notificationList) { } else { Logger.debug("Processed: `%s` conv: %s, user: %s, notifId: %s", payload.type, - payload.convId, + payload.conversation, userId, notif.id); } diff --git a/src/main/java/com/wire/bots/hold/Service.java b/src/main/java/com/wire/bots/hold/Service.java index 2563e8a..568fdf7 100644 --- a/src/main/java/com/wire/bots/hold/Service.java +++ b/src/main/java/com/wire/bots/hold/Service.java @@ -29,7 +29,9 @@ import com.wire.bots.hold.resource.*; import com.wire.bots.hold.utils.HoldClientRepo; import com.wire.bots.hold.utils.ImagesBundle; +import com.wire.helium.LoginClient; import com.wire.xenon.Const; +import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.crypto.CryptoDatabase; import com.wire.xenon.crypto.storage.JdbiStorage; import com.wire.xenon.factories.CryptoFactory; @@ -44,12 +46,12 @@ import io.federecio.dropwizard.swagger.SwaggerBundle; import io.federecio.dropwizard.swagger.SwaggerBundleConfiguration; import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.dropwizard.DropwizardExports; +import io.prometheus.client.exporter.MetricsServlet; import org.flywaydb.core.Flyway; import org.glassfish.jersey.media.multipart.MultiPartFeature; import org.jdbi.v3.core.Jdbi; import org.jdbi.v3.sqlobject.SqlObjectPlugin; -import io.prometheus.client.dropwizard.DropwizardExports; -import io.prometheus.client.exporter.MetricsServlet; import javax.ws.rs.client.Client; import java.util.concurrent.TimeUnit; @@ -57,6 +59,7 @@ public class Service extends Application { public static Service instance; public static MetricRegistry metrics; + public static String API_DOMAIN; protected Config config; protected Environment environment; protected Jdbi jdbi; @@ -128,6 +131,8 @@ public void run(Config config, Environment environment) { environment.healthChecks().register("SanityCheck", new SanityCheck(accessDAO, httpClient)); final HoldClientRepo repo = new HoldClientRepo(jdbi, cf, httpClient); + final LoginClient loginClient = new LoginClient(httpClient); + final HoldMessageResource holdMessageResource = new HoldMessageResource(new MessageHandler(jdbi), repo); final NotificationProcessor notificationProcessor = new NotificationProcessor(httpClient, accessDAO, config, holdMessageResource); @@ -139,6 +144,10 @@ public void run(Config config, Environment environment) { CollectorRegistry.defaultRegistry.register(new DropwizardExports(metrics)); environment.getApplicationContext().addServlet(MetricsServlet.class, "/metrics"); + + // todo here + // String res = loginClient.getBackendConfiguration(); + // handle res ^ } public Config getConfig() { @@ -177,6 +186,9 @@ protected void setupDatabase(Config.Database database) { } public CryptoFactory getCryptoFactory(Jdbi jdbi) { - return (botId) -> new CryptoDatabase(botId, new JdbiStorage(jdbi)); + return (botId) -> new CryptoDatabase( + new QualifiedId(botId, null), // TODO(WPB-11287): Change null to default domain + new JdbiStorage(jdbi) + ); } } diff --git a/src/main/java/com/wire/bots/hold/healthchecks/SanityCheck.java b/src/main/java/com/wire/bots/hold/healthchecks/SanityCheck.java index 4a42742..80df73a 100644 --- a/src/main/java/com/wire/bots/hold/healthchecks/SanityCheck.java +++ b/src/main/java/com/wire/bots/hold/healthchecks/SanityCheck.java @@ -4,6 +4,7 @@ import com.wire.bots.hold.DAO.AccessDAO; import com.wire.bots.hold.model.LHAccess; import com.wire.helium.API; +import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.tools.Logger; import javax.ws.rs.client.Client; @@ -35,7 +36,10 @@ protected Result check() { while (!accessList.isEmpty()) { Logger.info("SanityCheck: checking %d devices, created: %s", accessList.size(), created); for (LHAccess access : accessList) { - boolean hasDevice = api.hasDevice(access.userId, access.clientId); + boolean hasDevice = api.hasDevice( + new QualifiedId(access.userId, null), // TODO(WPB-11287): Change null to default domain + access.clientId + ); if (!access.enabled && hasDevice) return Result.unhealthy("User %s is NOT tracked in LH", access.userId); diff --git a/src/main/java/com/wire/bots/hold/resource/ConversationResource.java b/src/main/java/com/wire/bots/hold/resource/ConversationResource.java index f126ec6..6a7c649 100644 --- a/src/main/java/com/wire/bots/hold/resource/ConversationResource.java +++ b/src/main/java/com/wire/bots/hold/resource/ConversationResource.java @@ -18,6 +18,7 @@ import com.wire.helium.API; import com.wire.xenon.backend.models.Conversation; import com.wire.xenon.backend.models.Member; +import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.backend.models.SystemMessage; import com.wire.xenon.models.*; import com.wire.xenon.tools.Logger; @@ -229,7 +230,7 @@ private void onVideoPreview(Collector collector, Event event) { private void onMember(Collector collector, Event event, String label) { try { SystemMessage msg = mapper.readValue(event.payload, SystemMessage.class); - for (UUID userId : msg.users) { + for (QualifiedId userId : msg.users) { String format = String.format("**%s** %s **%s**", getUserName(msg.from), label, @@ -297,8 +298,9 @@ private API getLHApi() { private String formatConversation(Conversation conversation) { StringBuilder sb = new StringBuilder(); + QualifiedId creatorId = new QualifiedId(conversation.creator, null); // TODO(WPB-11287): Change null to default domain sb.append(String.format("**%s** created conversation **%s** with: \n", - getUserName(conversation.creator), + getUserName(creatorId), conversation.name)); for (Member member : conversation.members) { sb.append(String.format("- **%s** \n", getUserName(member.id))); @@ -316,7 +318,7 @@ private String getText(UUID msgId) throws IOException { } @Nullable - private String getUserName(UUID userId) { + private String getUserName(QualifiedId userId) { Cache cache = new Cache(api, assetsDAO); return cache.getUser(userId).name; } diff --git a/src/main/java/com/wire/bots/hold/utils/Cache.java b/src/main/java/com/wire/bots/hold/utils/Cache.java index 619b63c..e0e55c2 100644 --- a/src/main/java/com/wire/bots/hold/utils/Cache.java +++ b/src/main/java/com/wire/bots/hold/utils/Cache.java @@ -2,6 +2,7 @@ import com.wire.bots.hold.DAO.AssetsDAO; import com.wire.helium.API; +import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.backend.models.User; import com.wire.xenon.exceptions.HttpException; import com.wire.xenon.tools.Logger; @@ -12,9 +13,10 @@ import java.util.concurrent.ConcurrentHashMap; public class Cache { + // TODO(WPB-11287): Add default domain here private static final ConcurrentHashMap assets = new ConcurrentHashMap<>();// - private static final ConcurrentHashMap users = new ConcurrentHashMap<>();// - private static final ConcurrentHashMap profiles = new ConcurrentHashMap<>();// + private static final ConcurrentHashMap users = new ConcurrentHashMap<>();// + private static final ConcurrentHashMap profiles = new ConcurrentHashMap<>();// private final API api; private final AssetsDAO assetsDAO; @@ -54,7 +56,8 @@ public File getProfileImage(User user) { return file; } - public User getUser(UUID userId) { + public User getUser(QualifiedId userId) { + // TODO(WPB-11287): Fetch first in map then check API return users.computeIfAbsent(userId, k -> { try { return api.getUser(userId); diff --git a/src/main/java/com/wire/bots/hold/utils/Collector.java b/src/main/java/com/wire/bots/hold/utils/Collector.java index c3370f5..3ab7814 100644 --- a/src/main/java/com/wire/bots/hold/utils/Collector.java +++ b/src/main/java/com/wire/bots/hold/utils/Collector.java @@ -1,5 +1,6 @@ package com.wire.bots.hold.utils; +import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.backend.models.User; import com.wire.xenon.models.OriginMessage; import com.wire.xenon.models.TextMessage; @@ -25,7 +26,7 @@ public void add(TextMessage event) throws ParseException { message.text = Helper.markdown2Html(event.getText(), true); message.time = toTime(event.getTime()); - UUID senderId = event.getUserId(); + QualifiedId senderId = event.getUserId(); String dateTime = event.getTime(); User user = cache.getUser(senderId); @@ -48,7 +49,7 @@ public void add(OriginMessage event) throws ParseException { message.text = Helper.markdown2Html(url, false); } - UUID senderId = event.getUserId(); + QualifiedId senderId = event.getUserId(); User user = cache.getUser(senderId); Sender sender = sender(user, message); @@ -78,7 +79,7 @@ private Sender sender(User user, Message message) { private Sender system(Message message, String type) { Sender sender = new Sender(); sender.system = "system"; - sender.senderId = UUID.randomUUID(); + sender.senderId = new QualifiedId(UUID.randomUUID(), null); // TODO(WPB-11287): Change null to default domain sender.avatar = systemIcon(type); sender.messages.add(message); return sender; @@ -212,7 +213,7 @@ public static class Message { } public static class Sender { - UUID senderId; + QualifiedId senderId; String avatar; String name; String accent; diff --git a/src/main/java/com/wire/bots/hold/utils/Helper.java b/src/main/java/com/wire/bots/hold/utils/Helper.java index fc70891..893384b 100644 --- a/src/main/java/com/wire/bots/hold/utils/Helper.java +++ b/src/main/java/com/wire/bots/hold/utils/Helper.java @@ -2,6 +2,7 @@ import com.wire.helium.API; import com.wire.xenon.backend.models.Asset; +import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.backend.models.User; import com.wire.xenon.exceptions.HttpException; import org.commonmark.Extension; @@ -28,7 +29,7 @@ static File getProfile(API api, User user) throws IOException, HttpException { for (Asset asset : user.assets) { if (asset.size.equals("preview")) { - byte[] profile = api.downloadAsset(asset.key, null); + byte[] profile = api.downloadAsset(asset.key, user.id.domain, null); save(profile, file); break; } @@ -54,8 +55,8 @@ static String getExtension(String mimeType) { return split.length == 1 ? split[0] : split[1]; } - static String avatarFile(UUID senderId) { - return String.format("avatars/%s.png", senderId); + static String avatarFile(QualifiedId senderId) { + return String.format("avatars/%s.png", senderId.id); } static String markdown2Html(String text, Boolean escape) { diff --git a/src/main/java/com/wire/bots/hold/utils/HoldClientRepo.java b/src/main/java/com/wire/bots/hold/utils/HoldClientRepo.java index b03f1b3..f808e2a 100644 --- a/src/main/java/com/wire/bots/hold/utils/HoldClientRepo.java +++ b/src/main/java/com/wire/bots/hold/utils/HoldClientRepo.java @@ -5,6 +5,7 @@ import com.wire.bots.hold.model.LHAccess; import com.wire.helium.API; import com.wire.xenon.WireClient; +import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.crypto.Crypto; import com.wire.xenon.factories.CryptoFactory; import org.jdbi.v3.core.Jdbi; @@ -24,10 +25,10 @@ public HoldClientRepo(Jdbi jdbi, CryptoFactory cf, Client httpClient) { this.httpClient = httpClient; } - public WireClient getClient(UUID userId, String deviceId, UUID convId) throws CryptoException { + public WireClient getClient(UUID userId, String deviceId, QualifiedId conversationId) throws CryptoException { Crypto crypto = cf.create(userId); final LHAccess single = jdbi.onDemand(AccessDAO.class).get(userId); - final API api = new API(httpClient, convId, single.token); - return new HoldWireClient(userId, deviceId, convId, crypto, api); + final API api = new API(httpClient, conversationId, single.token); + return new HoldWireClient(userId, deviceId, conversationId, crypto, api); } } diff --git a/src/main/java/com/wire/bots/hold/utils/HoldWireClient.java b/src/main/java/com/wire/bots/hold/utils/HoldWireClient.java index dfd2a05..2ad4701 100644 --- a/src/main/java/com/wire/bots/hold/utils/HoldWireClient.java +++ b/src/main/java/com/wire/bots/hold/utils/HoldWireClient.java @@ -4,6 +4,7 @@ import com.wire.xenon.WireClient; import com.wire.xenon.WireClientBase; import com.wire.xenon.assets.IAsset; +import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.backend.models.User; import com.wire.xenon.crypto.Crypto; import com.wire.xenon.models.AssetKey; @@ -15,10 +16,10 @@ public class HoldWireClient extends WireClientBase implements WireClient { private final UUID userId; - private final UUID convId; + private final QualifiedId convId; private final String deviceId; - public HoldWireClient(UUID userId, String deviceId, UUID convId, Crypto crypto, API api) { + HoldWireClient(UUID userId, String deviceId, QualifiedId convId, Crypto crypto, API api) { super(api, crypto, null); this.userId = userId; this.convId = convId; @@ -36,7 +37,7 @@ public String getDeviceId() { } @Override - public UUID getConversationId() { + public QualifiedId getConversationId() { return convId; } @@ -48,7 +49,7 @@ public User getSelf() { } @Override - public void acceptConnection(UUID user) { + public void acceptConnection(QualifiedId user) { } @@ -63,7 +64,7 @@ public ArrayList getAvailablePrekeys() { } @Override - public byte[] downloadProfilePicture(String assetKey) { + public byte[] downloadProfilePicture(String assetKey, String domain) { return new byte[0]; } diff --git a/src/test/java/com/wire/bots/hold/Consts.java b/src/test/java/com/wire/bots/hold/Consts.java deleted file mode 100644 index b45ce0e..0000000 --- a/src/test/java/com/wire/bots/hold/Consts.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.wire.bots.hold; - -import java.util.UUID; - -public class Consts { - public static final UUID dejan = UUID.fromString("40b96378-951d-11e9-bc42-526af7764f64"); - public static final UUID lipis = UUID.fromString("40b96896-951d-11e9-bc42-526af7764f64"); -} diff --git a/src/test/java/com/wire/bots/hold/DatabaseTest.java b/src/test/java/com/wire/bots/hold/DatabaseTest.java index c6c05d4..8ee64b9 100644 --- a/src/test/java/com/wire/bots/hold/DatabaseTest.java +++ b/src/test/java/com/wire/bots/hold/DatabaseTest.java @@ -8,6 +8,7 @@ import com.wire.bots.hold.model.Config; import com.wire.bots.hold.model.Event; import com.wire.bots.hold.model.LHAccess; +import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.models.TextMessage; import io.dropwizard.testing.ConfigOverride; import io.dropwizard.testing.DropwizardTestSupport; @@ -48,20 +49,20 @@ public void eventsTextMessageTest() throws JsonProcessingException { final String type = "conversation.otr-message-add.new-text"; final UUID eventId = UUID.randomUUID(); final UUID messageId = UUID.randomUUID(); - final UUID convId = UUID.randomUUID(); + final QualifiedId convId = new QualifiedId(UUID.randomUUID(), null); final String clientId = UUID.randomUUID().toString(); - final UUID userId = UUID.randomUUID(); + final QualifiedId userId = new QualifiedId(UUID.randomUUID(), null); final String time = new Date().toString(); final TextMessage textMessage = new TextMessage(eventId, messageId, convId, clientId, userId, time); - textMessage.addMention(UUID.randomUUID().toString(), 0, 5); + textMessage.addMention(new QualifiedId(UUID.randomUUID(), null), 0, 5); textMessage.setText("Some text"); String payload = mapper.writeValueAsString(textMessage); - int insert = eventsDAO.insert(eventId, convId, userId, type, payload); + int insert = eventsDAO.insert(eventId, convId.id, userId.id, type, payload); assert insert == 1; - insert = eventsDAO.insert(UUID.randomUUID(), convId, userId, type, payload); + insert = eventsDAO.insert(UUID.randomUUID(), convId.id, userId.id, type, payload); assert insert == 1; final Event event = eventsDAO.get(eventId); @@ -70,7 +71,7 @@ public void eventsTextMessageTest() throws JsonProcessingException { assert textMessage.getMessageId().equals(message.getMessageId()); - List events = eventsDAO.listAll(convId); + List events = eventsDAO.listAll(convId.id); assert events.size() == 2; } @@ -86,9 +87,9 @@ public void assetsTest() { assetsDAO.insert(messageId, image); final AssetsDAO.Asset asset = assetsDAO.get(messageId); - final boolean deepEquals = Objects.deepEquals(image, asset.data); - final boolean equals = Objects.equals(messageId, asset.messageId); - final boolean equals1 = Objects.equals(mimetype, asset.mimeType); + assert Objects.deepEquals(image, asset.data); + assert Objects.equals(messageId, asset.messageId); + assert Objects.equals(mimetype, asset.mimeType); } @Test @@ -105,11 +106,14 @@ public void accessTests() { accessDAO.update(userId, token, cookie2); final LHAccess lhAccess = accessDAO.get(userId); + assert lhAccess != null; + assert lhAccess.userId.equals(userId); accessDAO.disable(userId); final int insert2 = accessDAO.insert(userId, clientId, cookie); final LHAccess lhAccess2 = accessDAO.get(userId); - + assert lhAccess2 != null; + assert lhAccess2.created.equals(lhAccess.created); } } diff --git a/src/test/java/com/wire/bots/hold/MessageTemplateTest.java b/src/test/java/com/wire/bots/hold/MessageTemplateTest.java index ee541af..9306a11 100644 --- a/src/test/java/com/wire/bots/hold/MessageTemplateTest.java +++ b/src/test/java/com/wire/bots/hold/MessageTemplateTest.java @@ -6,6 +6,7 @@ import com.wire.bots.hold.utils.Collector; import com.wire.bots.hold.utils.PdfGenerator; import com.wire.bots.hold.utils.TestCache; +import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.models.OriginMessage; import com.wire.xenon.models.PhotoPreviewMessage; import com.wire.xenon.models.TextMessage; @@ -18,35 +19,33 @@ import java.text.ParseException; import java.util.UUID; -import static com.wire.bots.hold.Consts.dejan; -import static com.wire.bots.hold.Consts.lipis; - public class MessageTemplateTest { - private static TextMessage txt(UUID userId, String time, String text) { + private static TextMessage txt(QualifiedId userId, String time, String text) { TextMessage msg = new TextMessage(UUID.randomUUID(), - UUID.randomUUID(), - UUID.randomUUID(), - UUID.randomUUID().toString(), - userId, time); + UUID.randomUUID(), + new QualifiedId(UUID.randomUUID(), UUID.randomUUID().toString()), + UUID.randomUUID().toString(), + userId, + time); msg.setText(text); return msg; } - private static OriginMessage asset(UUID userId, String time, String name, String mime) { + private static OriginMessage asset(QualifiedId userId, String time, String name, String mime) { return new PhotoPreviewMessage( - UUID.randomUUID(), - UUID.randomUUID(), - UUID.randomUUID(), - UUID.randomUUID().toString(), - userId, - time, - mime, 0, name, 0, 0); + UUID.randomUUID(), + UUID.randomUUID(), + new QualifiedId(UUID.randomUUID(), UUID.randomUUID().toString()), + UUID.randomUUID().toString(), + userId, + time, + mime, 0, name, 0, 0); } @Before public void makeDirs() { File f = new File("src/test/output"); - boolean mkdir = f.mkdir(); + f.mkdir(); } @Test @@ -96,62 +95,64 @@ private Collector.Conversation getConversation() throws ParseException { final String thursday = "2019-07-08T08:35:21.348Z"; final String friday = "2019-07-09T18:21:17.548Z"; final String saturday = "2019-07-10T21:11:47.149Z"; + final QualifiedId user1 = new QualifiedId(UUID.randomUUID(), UUID.randomUUID().toString()); + final QualifiedId user2 = new QualifiedId(UUID.randomUUID(), UUID.randomUUID().toString()); Collector collector = new Collector(new TestCache()); String name = "Message Template Test"; collector.setConvName(name); final String format = String.format("**Dejan** created conversation **%s** with: \n- **Lipis**", name); collector.addSystem(format, thursday, "conversation.create"); - collector.add(txt(dejan, thursday, "Privet! Kak dela?")); - collector.add(txt(lipis, thursday, "Ladna")); - collector.add(asset(lipis, thursday, "i_know", "video/mp4")); - collector.add(txt(dejan, thursday, "πŸ˜ƒπŸ πŸ˜΄πŸ€§βœοΈπŸ‘‰πŸ‘¨β€πŸš’πŸ‘¨β€πŸ«πŸ‘©β€πŸ‘¦πŸ‘¨β€πŸ‘§β€πŸ‘¦πŸ₯πŸ§πŸΎπŸπŸ•πŸŽ‹πŸ²πŸ‰")); - collector.add(txt(dejan, thursday, "4")); - collector.add(txt(lipis, thursday, "5 πŸ‘")); - collector.add(txt(lipis, thursday, "πŸ˜ƒLorem ipsum **dolor** sit amet, consectetur adipiscing elit, sed " + + collector.add(txt(user1, thursday, "Privet! Kak dela?")); + collector.add(txt(user2, thursday, "Ladna")); + collector.add(asset(user2, thursday, "i_know", "video/mp4")); + collector.add(txt(user1, thursday, "πŸ˜ƒπŸ πŸ˜΄πŸ€§βœοΈπŸ‘‰πŸ‘¨β€πŸš’πŸ‘¨β€πŸ«πŸ‘©β€πŸ‘¦πŸ‘¨β€πŸ‘§β€πŸ‘¦πŸ₯πŸ§πŸΎπŸπŸ•πŸŽ‹πŸ²πŸ‰")); + collector.add(txt(user1, thursday, "4")); + collector.add(txt(user2, thursday, "5 πŸ‘")); + collector.add(txt(user2, thursday, "πŸ˜ƒLorem ipsum **dolor** sit amet, consectetur adipiscing elit, sed " + "do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam," + " quis nostrud exercitation ullamco _laboris_ nisi ut aliquip ex ea commodo consequat. " + "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum")); - collector.add(txt(dejan, friday, "7")); - collector.add(txt(lipis, saturday, "8")); - collector.add(asset(lipis, saturday, "ognjiste2", "image/png")); - collector.add(asset(lipis, saturday, "small", "image/png")); - collector.add(txt(dejan, saturday, "9")); - collector.add(txt(dejan, saturday, "10")); - collector.add(txt(lipis, saturday, "```collector.addSystem(img(dejan, friday," + + collector.add(txt(user1, friday, "7")); + collector.add(txt(user2, saturday, "8")); + collector.add(asset(user2, saturday, "ognjiste2", "image/png")); + collector.add(asset(user2, saturday, "small", "image/png")); + collector.add(txt(user1, saturday, "9")); + collector.add(txt(user1, saturday, "10")); + collector.add(txt(user2, saturday, "```collector.addSystem(img(dejan, friday," + " \"SP\", \"image/jpeg\"));\n" + " collector.addSystem(txt(dejan, friday, \"7\"));\n" + " collector.addSystem(txt(lipis, saturday, \"8\"));\n" + " collector.addSystem(img(lipis, saturday, \"ognjiste2\", \"image/png\"));\n" + " collector.addSystem(img(lipis, saturday, \"small\", \"image/png\"));\n" + "```")); - collector.add(txt(dejan, saturday, "12")); - collector.add(txt(lipis, saturday, "13")); - collector.add(asset(dejan, saturday, "ognjiste", "image/png")); - collector.add(txt(lipis, saturday, "14")); - collector.add(txt(lipis, saturday, "15")); - collector.add(txt(dejan, saturday, "Lorem ipsum **dolor** sit amet, consectetur adipiscing elit, sed " + + collector.add(txt(user1, saturday, "12")); + collector.add(txt(user2, saturday, "13")); + collector.add(asset(user1, saturday, "ognjiste", "image/png")); + collector.add(txt(user2, saturday, "14")); + collector.add(txt(user2, saturday, "15")); + collector.add(txt(user1, saturday, "Lorem ipsum **dolor** sit amet, consectetur adipiscing elit, sed " + "do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam," + " quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " + "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. " + "Excepteur sint occaecat cupidatat non _proident_, sunt in culpa qui officia deserunt mollit anim id est" + " laborum.")); - collector.add(txt(lipis, saturday, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed " + + collector.add(txt(user2, saturday, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed " + "do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam," + " quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " + "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. " + "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est" + " laborum.")); - collector.add(txt(dejan, saturday, "This is some url [google](https://google.com)")); - collector.add(txt(dejan, saturday, "https://google.com")); - collector.add(txt(lipis, saturday, "This is some url https://google.com and some text")); - collector.add(txt(dejan, saturday, "These two urls https://google.com https://wire.com")); + collector.add(txt(user1, saturday, "This is some url [google](https://google.com)")); + collector.add(txt(user1, saturday, "https://google.com")); + collector.add(txt(user2, saturday, "This is some url https://google.com and some text")); + collector.add(txt(user1, saturday, "These two urls https://google.com https://wire.com")); collector.addSystem("**Lipis** left the conversation", saturday, "conversation.member-leave"); collector.addSystem("**Tiago** joined the conversation", saturday, "conversation.member-join"); collector.addSystem("**Tiago** deleted text: 'some text'", saturday, "conversation.otr-message-add.delete-text"); collector.addSystem("**Tiago** called", saturday, "conversation.otr-message-add.call"); - collector.add(asset(dejan, saturday, "ognjiste2", "image/png")); + collector.add(asset(user1, saturday, "ognjiste2", "image/png")); return collector.getConversation(); } diff --git a/src/test/java/com/wire/bots/hold/Serialization.java b/src/test/java/com/wire/bots/hold/SerializationTest.java similarity index 94% rename from src/test/java/com/wire/bots/hold/Serialization.java rename to src/test/java/com/wire/bots/hold/SerializationTest.java index 81f6906..2a0689f 100644 --- a/src/test/java/com/wire/bots/hold/Serialization.java +++ b/src/test/java/com/wire/bots/hold/SerializationTest.java @@ -1,13 +1,13 @@ package com.wire.bots.hold; import com.fasterxml.jackson.databind.ObjectMapper; -import com.wire.xenon.models.ImageMessage; +import com.wire.xenon.models.PhotoPreviewMessage; import com.wire.xenon.models.TextMessage; import org.junit.Test; import java.io.IOException; -public class Serialization { +public class SerializationTest { @Test public void imageMessage() throws IOException { String payload = "{" + @@ -28,7 +28,7 @@ public void imageMessage() throws IOException { "\"tag\":null" + "}"; ObjectMapper mapper = new ObjectMapper(); - ImageMessage message = mapper.readValue(payload, ImageMessage.class); + PhotoPreviewMessage message = mapper.readValue(payload, PhotoPreviewMessage.class); } @Test diff --git a/src/test/java/com/wire/bots/hold/utils/TestCache.java b/src/test/java/com/wire/bots/hold/utils/TestCache.java index 86f78bb..0c36fb2 100644 --- a/src/test/java/com/wire/bots/hold/utils/TestCache.java +++ b/src/test/java/com/wire/bots/hold/utils/TestCache.java @@ -1,12 +1,11 @@ package com.wire.bots.hold.utils; +import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.backend.models.User; import java.io.File; import java.util.UUID; -import static com.wire.bots.hold.Consts.dejan; - public class TestCache extends Cache { public TestCache() { super(null, null); @@ -24,11 +23,13 @@ public File getProfileImage(User user) { } @Override - public User getUser(UUID userId) { - User user = new User(); - user.id = userId; - user.name = userId.equals(dejan) ? "Dejan" : "Lipis"; - user.accent = userId.equals(dejan) ? 3 : 5; - return user; + public User getUser(QualifiedId userId) { + User dummyUser = new User(); + + dummyUser.id = userId; + dummyUser.name = userId.toString(); + dummyUser.accent = 3; + + return dummyUser; } } From a051559b43840cc56c936e8420a2d6c54147ad67 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Tue, 1 Oct 2024 16:46:37 +0200 Subject: [PATCH 2/5] feat(get-domain-from-backend) Use new Helium API to fetch backend domain * Update Helium to 1.4.1 * Add assertions to major tests * Remove Consts.java from tests * Add call to get api version and default domain --- .../wire/bots/hold/NotificationProcessor.java | 80 ++++++------------- src/main/java/com/wire/bots/hold/Service.java | 11 ++- .../bots/hold/healthchecks/SanityCheck.java | 21 ++++- .../wire/bots/hold/model/Notification.java | 20 ----- .../bots/hold/model/NotificationList.java | 18 ----- .../bots/hold/resource/ConfirmResource.java | 10 ++- .../hold/resource/ConversationResource.java | 2 + .../java/com/wire/bots/hold/utils/Cache.java | 20 +++-- .../com/wire/bots/hold/utils/TestCache.java | 19 +++++ 9 files changed, 93 insertions(+), 108 deletions(-) delete mode 100644 src/main/java/com/wire/bots/hold/model/Notification.java delete mode 100644 src/main/java/com/wire/bots/hold/model/NotificationList.java diff --git a/src/main/java/com/wire/bots/hold/NotificationProcessor.java b/src/main/java/com/wire/bots/hold/NotificationProcessor.java index 3cf36c2..b9fc760 100644 --- a/src/main/java/com/wire/bots/hold/NotificationProcessor.java +++ b/src/main/java/com/wire/bots/hold/NotificationProcessor.java @@ -3,39 +3,34 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.wire.bots.hold.DAO.AccessDAO; -import com.wire.bots.hold.model.Config; import com.wire.bots.hold.model.LHAccess; -import com.wire.bots.hold.model.Notification; -import com.wire.bots.hold.model.NotificationList; +import com.wire.helium.API; import com.wire.helium.LoginClient; import com.wire.helium.models.Access; +import com.wire.helium.models.Event; +import com.wire.helium.models.NotificationList; import com.wire.xenon.backend.models.Payload; import com.wire.xenon.exceptions.AuthException; import com.wire.xenon.exceptions.HttpException; import com.wire.xenon.tools.Logger; import javax.ws.rs.client.Client; -import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Cookie; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; import java.util.List; import java.util.UUID; import java.util.logging.Level; public class NotificationProcessor implements Runnable { + private static final int DEFAULT_NOTIFICATION_SIZE = 100; + private final Client client; private final AccessDAO accessDAO; private final HoldMessageResource messageResource; - private final WebTarget api; - NotificationProcessor(Client client, AccessDAO accessDAO, Config config, HoldMessageResource messageResource) { + NotificationProcessor(Client client, AccessDAO accessDAO, HoldMessageResource messageResource) { this.client = client; this.accessDAO = accessDAO; this.messageResource = messageResource; - - api = client.target(config.apiHost); } @Override @@ -59,7 +54,11 @@ private Access getAccess(Cookie cookie) throws HttpException { private void process(LHAccess device) { UUID userId = device.userId; + try { + // todo get exception + final API api = new API(client, null, device.token); + Logger.debug("`GET /notifications`: user: %s, last: %s", userId, device.last); String cookieValue = device.cookie; @@ -77,10 +76,13 @@ private void process(LHAccess device) { device.token = access.getAccessToken(); - NotificationList notificationList = retrieveNotifications(device); + NotificationList notificationList = api.retrieveNotifications( + device.clientId, + device.last, + DEFAULT_NOTIFICATION_SIZE + ); process(userId, notificationList); - } catch (AuthException e) { accessDAO.disable(userId); Logger.info("Disabled LH device for user: %s, error: %s", userId, e.getMessage()); @@ -89,27 +91,21 @@ private void process(LHAccess device) { } } - private static String bearer(String token) { - return token == null ? null : String.format("Bearer %s", token); - } - private void process(UUID userId, NotificationList notificationList) { - - for (Notification notif : notificationList.notifications) { - for (Payload payload : notif.payload) { - if (!process(userId, payload, notif.id)) { - Logger.error("Failed to process: user: %s, notif: %s", userId, notif.id); - //return; + for (Event event : notificationList.notifications) { + for (Payload payload : event.payload) { + if (!process(userId, payload, event.id)) { + Logger.error("Failed to process: user: %s, event: %s", userId, event.id); } else { - Logger.debug("Processed: `%s` conv: %s, user: %s, notifId: %s", + Logger.debug("Processed: `%s` conv: %s, user: %s, eventId: %s", payload.type, payload.conversation, userId, - notif.id); + event.id); } } - accessDAO.updateLast(userId, notif.id); + accessDAO.updateLast(userId, event.id); } } @@ -123,13 +119,13 @@ private boolean process(UUID userId, Payload payload, UUID id) { if (payload.from == null || payload.data == null) return true; - final boolean b = messageResource.onNewMessage(userId, id, payload); + final boolean wasMessageSent = messageResource.onNewMessage(userId, id, payload); - if (!b) { + if (!wasMessageSent) { Logger.error("process: `%s` user: %s, from: %s:%s, error: %s", payload.type, userId, payload.from, payload.data.sender); } - return b; + return wasMessageSent; } private void trace(Payload payload) { @@ -142,30 +138,4 @@ private void trace(Payload payload) { } } } - - //TODO remove this and use retrieveNotifications provided by Helium - private NotificationList retrieveNotifications(LHAccess access) throws HttpException { - Response response = api.path("notifications").queryParam("client", access.clientId).queryParam("since", access.last).queryParam("size", 100).request(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).header(HttpHeaders.AUTHORIZATION, bearer(access.token)).get(); - - int status = response.getStatus(); - - if (status == 200) { - return response.readEntity(NotificationList.class); - } - - if (status == 404) { //todo what??? - return response.readEntity(NotificationList.class); - } - - if (status == 401) { //todo nginx returns text/html for 401. Cannot deserialize as json - response.readEntity(String.class); - throw new AuthException(status); - } - - if (status == 403) { - throw response.readEntity(AuthException.class); - } - - throw response.readEntity(HttpException.class); - } } diff --git a/src/main/java/com/wire/bots/hold/Service.java b/src/main/java/com/wire/bots/hold/Service.java index 568fdf7..5075545 100644 --- a/src/main/java/com/wire/bots/hold/Service.java +++ b/src/main/java/com/wire/bots/hold/Service.java @@ -27,14 +27,18 @@ import com.wire.bots.hold.monitoring.RequestMdcFactoryFilter; import com.wire.bots.hold.monitoring.StatusResource; import com.wire.bots.hold.resource.*; +import com.wire.bots.hold.utils.Cache; import com.wire.bots.hold.utils.HoldClientRepo; import com.wire.bots.hold.utils.ImagesBundle; import com.wire.helium.LoginClient; +import com.wire.helium.models.BackendConfiguration; import com.wire.xenon.Const; import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.crypto.CryptoDatabase; import com.wire.xenon.crypto.storage.JdbiStorage; +import com.wire.xenon.exceptions.HttpException; import com.wire.xenon.factories.CryptoFactory; +import com.wire.xenon.tools.Logger; import io.dropwizard.Application; import io.dropwizard.assets.AssetsBundle; import io.dropwizard.client.JerseyClientBuilder; @@ -131,10 +135,9 @@ public void run(Config config, Environment environment) { environment.healthChecks().register("SanityCheck", new SanityCheck(accessDAO, httpClient)); final HoldClientRepo repo = new HoldClientRepo(jdbi, cf, httpClient); - final LoginClient loginClient = new LoginClient(httpClient); final HoldMessageResource holdMessageResource = new HoldMessageResource(new MessageHandler(jdbi), repo); - final NotificationProcessor notificationProcessor = new NotificationProcessor(httpClient, accessDAO, config, holdMessageResource); + final NotificationProcessor notificationProcessor = new NotificationProcessor(httpClient, accessDAO, holdMessageResource); environment.lifecycle() .scheduledExecutorService("notifications") @@ -144,10 +147,6 @@ public void run(Config config, Environment environment) { CollectorRegistry.defaultRegistry.register(new DropwizardExports(metrics)); environment.getApplicationContext().addServlet(MetricsServlet.class, "/metrics"); - - // todo here - // String res = loginClient.getBackendConfiguration(); - // handle res ^ } public Config getConfig() { diff --git a/src/main/java/com/wire/bots/hold/healthchecks/SanityCheck.java b/src/main/java/com/wire/bots/hold/healthchecks/SanityCheck.java index 80df73a..eb8afeb 100644 --- a/src/main/java/com/wire/bots/hold/healthchecks/SanityCheck.java +++ b/src/main/java/com/wire/bots/hold/healthchecks/SanityCheck.java @@ -3,8 +3,11 @@ import com.codahale.metrics.health.HealthCheck; import com.wire.bots.hold.DAO.AccessDAO; import com.wire.bots.hold.model.LHAccess; +import com.wire.bots.hold.utils.Cache; import com.wire.helium.API; +import com.wire.helium.models.BackendConfiguration; import com.wire.xenon.backend.models.QualifiedId; +import com.wire.xenon.exceptions.HttpException; import com.wire.xenon.tools.Logger; import javax.ws.rs.client.Client; @@ -30,14 +33,18 @@ protected Result check() { API api = new API(client, null, single.token); + fetchAndStoreApiVersion(api); + String created = single.created; List accessList = accessDAO.list(100, created); while (!accessList.isEmpty()) { Logger.info("SanityCheck: checking %d devices, created: %s", accessList.size(), created); for (LHAccess access : accessList) { + // TODO(WPB-11287): Use user domain if exists, otherwise default + // TODO: String domain = (access.domain != null) ? access.domain : Cache.DEFAULT_DOMAIN; boolean hasDevice = api.hasDevice( - new QualifiedId(access.userId, null), // TODO(WPB-11287): Change null to default domain + new QualifiedId(access.userId, Cache.getDefaultDomain()), // TODO(WPB-11287): Change null to default domain access.clientId ); @@ -61,4 +68,16 @@ protected Result check() { Logger.debug("Finished SanityCheck"); } } + + private void fetchAndStoreApiVersion(API api) { + if (Cache.getDefaultDomain() != null) { return; } + + try { + BackendConfiguration apiVersionResponse = api.getBackendConfiguration(); + + Cache.setDefaultDomain(apiVersionResponse.domain); + } catch (HttpException exception) { + Logger.exception(exception, "Service.getApiVersion, exception: %s", exception.getMessage()); + } + } } diff --git a/src/main/java/com/wire/bots/hold/model/Notification.java b/src/main/java/com/wire/bots/hold/model/Notification.java deleted file mode 100644 index 5bfba19..0000000 --- a/src/main/java/com/wire/bots/hold/model/Notification.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.wire.bots.hold.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.wire.xenon.backend.models.Payload; - -import javax.validation.constraints.NotNull; -import java.util.List; -import java.util.UUID; - -@JsonIgnoreProperties(ignoreUnknown = true) -public class Notification { - @JsonProperty - @NotNull - public List payload; - - @JsonProperty - @NotNull - public UUID id; -} diff --git a/src/main/java/com/wire/bots/hold/model/NotificationList.java b/src/main/java/com/wire/bots/hold/model/NotificationList.java deleted file mode 100644 index 76a31f8..0000000 --- a/src/main/java/com/wire/bots/hold/model/NotificationList.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.wire.bots.hold.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -import javax.validation.constraints.NotNull; -import java.util.ArrayList; - -@JsonIgnoreProperties(ignoreUnknown = true) -public class NotificationList { - @JsonProperty("has_more") - @NotNull - public Boolean hasMore; - - @JsonProperty - @NotNull - public ArrayList notifications; -} diff --git a/src/main/java/com/wire/bots/hold/resource/ConfirmResource.java b/src/main/java/com/wire/bots/hold/resource/ConfirmResource.java index 352f489..9cea782 100644 --- a/src/main/java/com/wire/bots/hold/resource/ConfirmResource.java +++ b/src/main/java/com/wire/bots/hold/resource/ConfirmResource.java @@ -33,9 +33,13 @@ public ConfirmResource(AccessDAO accessDAO) { @ApiResponse(code = 200, message = "Legal Hold Device enabled")}) public Response confirm(@ApiParam @Valid @NotNull ConfirmPayload payload) { try { - int insert = accessDAO.insert(payload.userId, - payload.clientId, - payload.refreshToken); + int insert = accessDAO.insert( + // TODO(WPB-11287): Add new column in DB for domain + // TODO(WPB-11287): + Use default domain for v0 + payload.userId, + payload.clientId, + payload.refreshToken + ); if (0 == insert) { Logger.error("ConfirmResource: Failed to insert Access %s:%s", diff --git a/src/main/java/com/wire/bots/hold/resource/ConversationResource.java b/src/main/java/com/wire/bots/hold/resource/ConversationResource.java index 6a7c649..ed226e3 100644 --- a/src/main/java/com/wire/bots/hold/resource/ConversationResource.java +++ b/src/main/java/com/wire/bots/hold/resource/ConversationResource.java @@ -67,6 +67,8 @@ public Response list(@ApiParam @PathParam("conversationId") UUID conversationId, try { List events = eventsDAO.listAllAsc(conversationId); + // TODO(WPB-11287) Verify default domain + testAPI(); Cache cache = new Cache(api, assetsDAO); diff --git a/src/main/java/com/wire/bots/hold/utils/Cache.java b/src/main/java/com/wire/bots/hold/utils/Cache.java index e0e55c2..b41101a 100644 --- a/src/main/java/com/wire/bots/hold/utils/Cache.java +++ b/src/main/java/com/wire/bots/hold/utils/Cache.java @@ -13,10 +13,11 @@ import java.util.concurrent.ConcurrentHashMap; public class Cache { - // TODO(WPB-11287): Add default domain here - private static final ConcurrentHashMap assets = new ConcurrentHashMap<>();// - private static final ConcurrentHashMap users = new ConcurrentHashMap<>();// - private static final ConcurrentHashMap profiles = new ConcurrentHashMap<>();// + private static String DEFAULT_DOMAIN = null; + + private static final ConcurrentHashMap assets = new ConcurrentHashMap<>(); // + private static final ConcurrentHashMap users = new ConcurrentHashMap<>(); // + private static final ConcurrentHashMap profiles = new ConcurrentHashMap<>(); // private final API api; private final AssetsDAO assetsDAO; @@ -25,6 +26,16 @@ public Cache(API api, AssetsDAO assetsDAO) { this.assetsDAO = assetsDAO; } + public static void setDefaultDomain(String domain) { + if (DEFAULT_DOMAIN == null) { + DEFAULT_DOMAIN = domain; + } + } + + public static String getDefaultDomain() { + return DEFAULT_DOMAIN; + } + @Nullable public File getAssetFile(UUID messageId) { @@ -57,7 +68,6 @@ public File getProfileImage(User user) { } public User getUser(QualifiedId userId) { - // TODO(WPB-11287): Fetch first in map then check API return users.computeIfAbsent(userId, k -> { try { return api.getUser(userId); diff --git a/src/test/java/com/wire/bots/hold/utils/TestCache.java b/src/test/java/com/wire/bots/hold/utils/TestCache.java index 0c36fb2..1ea23e8 100644 --- a/src/test/java/com/wire/bots/hold/utils/TestCache.java +++ b/src/test/java/com/wire/bots/hold/utils/TestCache.java @@ -2,6 +2,7 @@ import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.backend.models.User; +import org.junit.Test; import java.io.File; import java.util.UUID; @@ -32,4 +33,22 @@ public User getUser(QualifiedId userId) { return dummyUser; } + + @Test + public void verifyDefaultDomainIsSetCorrectly() { + String firstDomain = Cache.getDefaultDomain(); + assert firstDomain == null; + + Cache.setDefaultDomain("dummy_domain"); + String secondDomain = Cache.getDefaultDomain(); + + assert secondDomain != null; + assert secondDomain.equals("dummy_domain"); + + Cache.setDefaultDomain("dummy_domain_3"); + String thirdDomain = Cache.getDefaultDomain(); + + assert thirdDomain != null; + assert thirdDomain.equals("dummy_domain"); + } } From 84673c0bd947fde6d01b95455e17a2c04e892d8e Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Mon, 7 Oct 2024 12:29:05 +0200 Subject: [PATCH 3/5] feat(get-domain-from-backend) Use new Helium API to fetch backend domain * Update Helium to 1.4.1 * Add assertions to major tests * Remove Consts.java from tests * Add call to get api version and default domain * Persist fallback domain to database * Add FallbackDomainFetcher to handle fetch from cache, database and API --- pom.xml | 6 ++ .../com/wire/bots/hold/DAO/MetadataDAO.java | 20 ++++ .../hold/DAO/MetadataResultSetMapper.java | 19 ++++ .../wire/bots/hold/FallbackDomainFetcher.java | 59 ++++++++++ .../wire/bots/hold/NotificationProcessor.java | 3 +- src/main/java/com/wire/bots/hold/Service.java | 23 ++-- .../bots/hold/healthchecks/SanityCheck.java | 18 +--- .../com/wire/bots/hold/model/Metadata.java | 11 ++ .../java/com/wire/bots/hold/utils/Cache.java | 12 +-- .../db/migration/V107__add_metadata_table.sql | 4 + .../java/com/wire/bots/hold/DatabaseTest.java | 18 ++++ .../bots/hold/FallbackDomainFetcherTest.java | 102 ++++++++++++++++++ .../com/wire/bots/hold/utils/CacheTest.java | 38 +++++++ .../com/wire/bots/hold/utils/TestCache.java | 18 ---- 14 files changed, 302 insertions(+), 49 deletions(-) create mode 100644 src/main/java/com/wire/bots/hold/DAO/MetadataDAO.java create mode 100644 src/main/java/com/wire/bots/hold/DAO/MetadataResultSetMapper.java create mode 100644 src/main/java/com/wire/bots/hold/FallbackDomainFetcher.java create mode 100644 src/main/java/com/wire/bots/hold/model/Metadata.java create mode 100644 src/main/resources/db/migration/V107__add_metadata_table.sql create mode 100644 src/test/java/com/wire/bots/hold/FallbackDomainFetcherTest.java create mode 100644 src/test/java/com/wire/bots/hold/utils/CacheTest.java diff --git a/pom.xml b/pom.xml index b5e93dc..c255d0e 100644 --- a/pom.xml +++ b/pom.xml @@ -135,6 +135,12 @@ simpleclient_servlet ${prometheus.version} + + org.mockito + mockito-all + 1.10.19 + test + diff --git a/src/main/java/com/wire/bots/hold/DAO/MetadataDAO.java b/src/main/java/com/wire/bots/hold/DAO/MetadataDAO.java new file mode 100644 index 0000000..933d83e --- /dev/null +++ b/src/main/java/com/wire/bots/hold/DAO/MetadataDAO.java @@ -0,0 +1,20 @@ +package com.wire.bots.hold.DAO; + +import com.wire.bots.hold.model.Metadata; +import org.jdbi.v3.sqlobject.config.RegisterColumnMapper; +import org.jdbi.v3.sqlobject.customizer.Bind; +import org.jdbi.v3.sqlobject.statement.SqlQuery; +import org.jdbi.v3.sqlobject.statement.SqlUpdate; + +public interface MetadataDAO { + String FALLBACK_DOMAIN_KEY = "FALLBACK_DOMAIN_KEY"; + + @SqlUpdate("INSERT INTO Metadata (key, value)" + + "VALUES (:key, :value)" + + "ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value") + int insert(@Bind("key") String key, @Bind("value") String value); + + @SqlQuery("SELECT key, value FROM Metadata WHERE key = :key LIMIT 1") + @RegisterColumnMapper(MetadataResultSetMapper.class) + Metadata get(@Bind("key") String key); +} diff --git a/src/main/java/com/wire/bots/hold/DAO/MetadataResultSetMapper.java b/src/main/java/com/wire/bots/hold/DAO/MetadataResultSetMapper.java new file mode 100644 index 0000000..526985e --- /dev/null +++ b/src/main/java/com/wire/bots/hold/DAO/MetadataResultSetMapper.java @@ -0,0 +1,19 @@ +package com.wire.bots.hold.DAO; + +import com.wire.bots.hold.model.Metadata; +import org.jdbi.v3.core.mapper.ColumnMapper; +import org.jdbi.v3.core.statement.StatementContext; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class MetadataResultSetMapper implements ColumnMapper { + + @Override + public Metadata map(ResultSet rs, int columnNumber, StatementContext ctx) throws SQLException { + return new Metadata( + rs.getString("key"), + rs.getString("value") + ); + } +} diff --git a/src/main/java/com/wire/bots/hold/FallbackDomainFetcher.java b/src/main/java/com/wire/bots/hold/FallbackDomainFetcher.java new file mode 100644 index 0000000..58e7a40 --- /dev/null +++ b/src/main/java/com/wire/bots/hold/FallbackDomainFetcher.java @@ -0,0 +1,59 @@ +package com.wire.bots.hold; + +import com.wire.bots.hold.DAO.MetadataDAO; +import com.wire.bots.hold.model.Metadata; +import com.wire.bots.hold.utils.Cache; +import com.wire.helium.LoginClient; +import com.wire.helium.models.BackendConfiguration; +import com.wire.xenon.exceptions.HttpException; +import com.wire.xenon.tools.Logger; + +public class FallbackDomainFetcher implements Runnable { + + private final LoginClient loginClient; + private final MetadataDAO metadataDAO; + + /** + * Fetcher and handler for fallback domain. + *

+ * Fetches from API and compares against database value (if any), then inserts into database and updates cache value. + * If value received from the API is different from what is saved in the database, a [RuntimeException] is thrown. + *

+ * @param loginClient [{@link LoginClient}] as API to get backend configuration containing default domain. + * @param metadataDAO [{@link MetadataDAO}] as DAO to get/insert default domain to database. + * + * @throws RuntimeException if received domain from API is different from the one saved in the database. + */ + FallbackDomainFetcher(LoginClient loginClient, MetadataDAO metadataDAO) { + this.loginClient = loginClient; + this.metadataDAO = metadataDAO; + } + + @Override + public void run() { + if (Cache.getFallbackDomain() != null) { return; } + + Metadata metadata = metadataDAO.get(MetadataDAO.FALLBACK_DOMAIN_KEY); + try { + BackendConfiguration apiVersionResponse = loginClient.getBackendConfiguration(); + + if (metadata == null) { + metadataDAO.insert(MetadataDAO.FALLBACK_DOMAIN_KEY, apiVersionResponse.domain); + Cache.setFallbackDomain(apiVersionResponse.domain); + } else { + if (metadata.value.equals(apiVersionResponse.domain)) { + Cache.setFallbackDomain(apiVersionResponse.domain); + } else { + String formattedExceptionMessage = String.format( + "Database already has a default domain as %s and instead we got %s from the Backend API.", + metadata.value, + apiVersionResponse.domain + ); + throw new RuntimeException(formattedExceptionMessage); + } + } + } catch (HttpException exception) { + Logger.exception(exception, "FallbackDomainFetcher.run, exception: %s", exception.getMessage()); + } + } +} diff --git a/src/main/java/com/wire/bots/hold/NotificationProcessor.java b/src/main/java/com/wire/bots/hold/NotificationProcessor.java index b9fc760..1f504cf 100644 --- a/src/main/java/com/wire/bots/hold/NotificationProcessor.java +++ b/src/main/java/com/wire/bots/hold/NotificationProcessor.java @@ -56,7 +56,6 @@ private void process(LHAccess device) { UUID userId = device.userId; try { - // todo get exception final API api = new API(client, null, device.token); Logger.debug("`GET /notifications`: user: %s, last: %s", userId, device.last); @@ -86,6 +85,8 @@ private void process(LHAccess device) { } catch (AuthException e) { accessDAO.disable(userId); Logger.info("Disabled LH device for user: %s, error: %s", userId, e.getMessage()); + } catch (HttpException e) { + Logger.exception(e, "NotificationProcessor: Couldn't retrieve notifications, error: %s", e.getMessage()); } catch (Exception e) { Logger.exception(e, "NotificationProcessor: user: %s, last: %s, error: %s", userId, device.last, e.getMessage()); } diff --git a/src/main/java/com/wire/bots/hold/Service.java b/src/main/java/com/wire/bots/hold/Service.java index 5075545..4b2615f 100644 --- a/src/main/java/com/wire/bots/hold/Service.java +++ b/src/main/java/com/wire/bots/hold/Service.java @@ -21,24 +21,21 @@ import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; import com.wire.bots.hold.DAO.AccessDAO; import com.wire.bots.hold.DAO.EventsDAO; +import com.wire.bots.hold.DAO.MetadataDAO; import com.wire.bots.hold.filters.ServiceAuthenticationFilter; import com.wire.bots.hold.healthchecks.SanityCheck; import com.wire.bots.hold.model.Config; import com.wire.bots.hold.monitoring.RequestMdcFactoryFilter; import com.wire.bots.hold.monitoring.StatusResource; import com.wire.bots.hold.resource.*; -import com.wire.bots.hold.utils.Cache; import com.wire.bots.hold.utils.HoldClientRepo; import com.wire.bots.hold.utils.ImagesBundle; import com.wire.helium.LoginClient; -import com.wire.helium.models.BackendConfiguration; import com.wire.xenon.Const; import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.crypto.CryptoDatabase; import com.wire.xenon.crypto.storage.JdbiStorage; -import com.wire.xenon.exceptions.HttpException; import com.wire.xenon.factories.CryptoFactory; -import com.wire.xenon.tools.Logger; import io.dropwizard.Application; import io.dropwizard.assets.AssetsBundle; import io.dropwizard.client.JerseyClientBuilder; @@ -63,7 +60,6 @@ public class Service extends Application { public static Service instance; public static MetricRegistry metrics; - public static String API_DOMAIN; protected Config config; protected Environment environment; protected Jdbi jdbi; @@ -113,6 +109,7 @@ public void run(Config config, Environment environment) { final AccessDAO accessDAO = jdbi.onDemand(AccessDAO.class); final EventsDAO eventsDAO = jdbi.onDemand(EventsDAO.class); + final MetadataDAO metadataDAO = jdbi.onDemand(MetadataDAO.class); // Monitoring resources addResource(new StatusResource()); @@ -132,7 +129,21 @@ public void run(Config config, Environment environment) { addResource(ServiceAuthenticationFilter.ServiceAuthenticationFeature.class); - environment.healthChecks().register("SanityCheck", new SanityCheck(accessDAO, httpClient)); + environment + .lifecycle() + .executorService("fallback_domain_fetcher") + .build() + .execute( + new FallbackDomainFetcher( + new LoginClient(httpClient), + metadataDAO + ) + ); + + environment.healthChecks().register( + "SanityCheck", + new SanityCheck(accessDAO, httpClient) + ); final HoldClientRepo repo = new HoldClientRepo(jdbi, cf, httpClient); diff --git a/src/main/java/com/wire/bots/hold/healthchecks/SanityCheck.java b/src/main/java/com/wire/bots/hold/healthchecks/SanityCheck.java index eb8afeb..94df99a 100644 --- a/src/main/java/com/wire/bots/hold/healthchecks/SanityCheck.java +++ b/src/main/java/com/wire/bots/hold/healthchecks/SanityCheck.java @@ -5,9 +5,7 @@ import com.wire.bots.hold.model.LHAccess; import com.wire.bots.hold.utils.Cache; import com.wire.helium.API; -import com.wire.helium.models.BackendConfiguration; import com.wire.xenon.backend.models.QualifiedId; -import com.wire.xenon.exceptions.HttpException; import com.wire.xenon.tools.Logger; import javax.ws.rs.client.Client; @@ -33,8 +31,6 @@ protected Result check() { API api = new API(client, null, single.token); - fetchAndStoreApiVersion(api); - String created = single.created; List accessList = accessDAO.list(100, created); @@ -44,7 +40,7 @@ protected Result check() { // TODO(WPB-11287): Use user domain if exists, otherwise default // TODO: String domain = (access.domain != null) ? access.domain : Cache.DEFAULT_DOMAIN; boolean hasDevice = api.hasDevice( - new QualifiedId(access.userId, Cache.getDefaultDomain()), // TODO(WPB-11287): Change null to default domain + new QualifiedId(access.userId, Cache.getFallbackDomain()), // TODO(WPB-11287): Change null to default domain access.clientId ); @@ -68,16 +64,4 @@ protected Result check() { Logger.debug("Finished SanityCheck"); } } - - private void fetchAndStoreApiVersion(API api) { - if (Cache.getDefaultDomain() != null) { return; } - - try { - BackendConfiguration apiVersionResponse = api.getBackendConfiguration(); - - Cache.setDefaultDomain(apiVersionResponse.domain); - } catch (HttpException exception) { - Logger.exception(exception, "Service.getApiVersion, exception: %s", exception.getMessage()); - } - } } diff --git a/src/main/java/com/wire/bots/hold/model/Metadata.java b/src/main/java/com/wire/bots/hold/model/Metadata.java new file mode 100644 index 0000000..f145827 --- /dev/null +++ b/src/main/java/com/wire/bots/hold/model/Metadata.java @@ -0,0 +1,11 @@ +package com.wire.bots.hold.model; + +public class Metadata { + public String key; + public String value; + + public Metadata(String key, String value) { + this.key = key; + this.value = value; + } +} diff --git a/src/main/java/com/wire/bots/hold/utils/Cache.java b/src/main/java/com/wire/bots/hold/utils/Cache.java index b41101a..95b4ee4 100644 --- a/src/main/java/com/wire/bots/hold/utils/Cache.java +++ b/src/main/java/com/wire/bots/hold/utils/Cache.java @@ -13,7 +13,7 @@ import java.util.concurrent.ConcurrentHashMap; public class Cache { - private static String DEFAULT_DOMAIN = null; + private static String FALLBACK_DOMAIN = null; private static final ConcurrentHashMap assets = new ConcurrentHashMap<>(); // private static final ConcurrentHashMap users = new ConcurrentHashMap<>(); // @@ -26,14 +26,12 @@ public Cache(API api, AssetsDAO assetsDAO) { this.assetsDAO = assetsDAO; } - public static void setDefaultDomain(String domain) { - if (DEFAULT_DOMAIN == null) { - DEFAULT_DOMAIN = domain; - } + public static void setFallbackDomain(String domain) { + FALLBACK_DOMAIN = domain; } - public static String getDefaultDomain() { - return DEFAULT_DOMAIN; + public static String getFallbackDomain() { + return FALLBACK_DOMAIN; } @Nullable diff --git a/src/main/resources/db/migration/V107__add_metadata_table.sql b/src/main/resources/db/migration/V107__add_metadata_table.sql new file mode 100644 index 0000000..86e2406 --- /dev/null +++ b/src/main/resources/db/migration/V107__add_metadata_table.sql @@ -0,0 +1,4 @@ +CREATE TABLE Metadata ( + key VARCHAR NOT NULL UNIQUE, + value VARCHAR NOT NULL +); diff --git a/src/test/java/com/wire/bots/hold/DatabaseTest.java b/src/test/java/com/wire/bots/hold/DatabaseTest.java index 8ee64b9..833b28f 100644 --- a/src/test/java/com/wire/bots/hold/DatabaseTest.java +++ b/src/test/java/com/wire/bots/hold/DatabaseTest.java @@ -5,9 +5,11 @@ import com.wire.bots.hold.DAO.AccessDAO; import com.wire.bots.hold.DAO.AssetsDAO; import com.wire.bots.hold.DAO.EventsDAO; +import com.wire.bots.hold.DAO.MetadataDAO; import com.wire.bots.hold.model.Config; import com.wire.bots.hold.model.Event; import com.wire.bots.hold.model.LHAccess; +import com.wire.bots.hold.model.Metadata; import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.models.TextMessage; import io.dropwizard.testing.ConfigOverride; @@ -28,6 +30,7 @@ public class DatabaseTest { private static AssetsDAO assetsDAO; private static EventsDAO eventsDAO; private static AccessDAO accessDAO; + private static MetadataDAO metadataDAO; @BeforeClass public static void init() throws Exception { @@ -37,6 +40,7 @@ public static void init() throws Exception { eventsDAO = app.getJdbi().onDemand(EventsDAO.class); assetsDAO = app.getJdbi().onDemand(AssetsDAO.class); accessDAO = app.getJdbi().onDemand(AccessDAO.class); + metadataDAO = app.getJdbi().onDemand(MetadataDAO.class); } @AfterClass @@ -116,4 +120,18 @@ public void accessTests() { assert lhAccess2 != null; assert lhAccess2.created.equals(lhAccess.created); } + + @Test + public void metadataTests() { + String dummyKey = MetadataDAO.FALLBACK_DOMAIN_KEY + UUID.randomUUID(); + + Metadata nullMetadata = metadataDAO.get(dummyKey); + assert nullMetadata == null; + + int insert = metadataDAO.insert(dummyKey, "dummy_domain"); + Metadata metadata = metadataDAO.get(dummyKey); + + assert metadata != null; + assert metadata.value.equals("dummy_domain"); + } } diff --git a/src/test/java/com/wire/bots/hold/FallbackDomainFetcherTest.java b/src/test/java/com/wire/bots/hold/FallbackDomainFetcherTest.java new file mode 100644 index 0000000..0b83ffe --- /dev/null +++ b/src/test/java/com/wire/bots/hold/FallbackDomainFetcherTest.java @@ -0,0 +1,102 @@ +package com.wire.bots.hold; + +import com.wire.bots.hold.DAO.MetadataDAO; +import com.wire.bots.hold.model.Metadata; +import com.wire.bots.hold.utils.Cache; +import com.wire.helium.LoginClient; +import com.wire.helium.models.BackendConfiguration; +import com.wire.xenon.exceptions.HttpException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.times; + +public class FallbackDomainFetcherTest { + + private MetadataDAO metadataDAO; + private LoginClient loginClient; + private FallbackDomainFetcher fallbackDomainFetcher; + + @Before + public void before() { + // Clears cached domain + Cache.setFallbackDomain(null); + metadataDAO = mock(MetadataDAO.class); + loginClient = mock(LoginClient.class); + fallbackDomainFetcher = new FallbackDomainFetcher(loginClient, metadataDAO); + } + + @After + public void after() { + // Clears cached domain + Cache.setFallbackDomain(null); + } + + @Test + public void givenNoFallbackDomainInCacheAndDatabase_whenExecuting_thenFetchFromApiAndStoreInCacheAndDatabase() throws HttpException { + // given + BackendConfiguration backendConfiguration = new BackendConfiguration(); + backendConfiguration.domain = "dummy_domain_3"; + + when(metadataDAO.get(MetadataDAO.FALLBACK_DOMAIN_KEY)).thenReturn(null); + when(metadataDAO.insert(any(), any())).thenReturn(1); + when(loginClient.getBackendConfiguration()).thenReturn(backendConfiguration); + + // when + fallbackDomainFetcher.run(); + + // then + assert Cache.getFallbackDomain().equals("dummy_domain_3"); + verify(metadataDAO, times(1)).insert(MetadataDAO.FALLBACK_DOMAIN_KEY, backendConfiguration.domain); + } + + @Test + public void givenNoFallbackDomainInCache_whenExecuting_thenFetchFromAPIAndCompareWithDatabase() throws HttpException { + // given + BackendConfiguration backendConfiguration = new BackendConfiguration(); + backendConfiguration.domain = "dummy_domain_2"; + + Metadata metadata = new Metadata(MetadataDAO.FALLBACK_DOMAIN_KEY, "dummy_domain_2"); + + when(metadataDAO.get(MetadataDAO.FALLBACK_DOMAIN_KEY)).thenReturn(metadata); + when(loginClient.getBackendConfiguration()).thenReturn(backendConfiguration); + + // when + fallbackDomainFetcher.run(); + + // then + assert Cache.getFallbackDomain().equals("dummy_domain_2"); + } + + @Test(expected=RuntimeException.class) + public void givenNoFallbackDomainInCache_whenExecutingAndApiReturnsDifferentDomainFromDatabase_thenThrowRuntimeException() throws HttpException, RuntimeException { + // given + BackendConfiguration backendConfiguration = new BackendConfiguration(); + backendConfiguration.domain = "dummy_domain_1"; + + Metadata metadata = new Metadata(MetadataDAO.FALLBACK_DOMAIN_KEY, "dummy_domain_2"); + + when(metadataDAO.get(MetadataDAO.FALLBACK_DOMAIN_KEY)).thenReturn(metadata); + when(loginClient.getBackendConfiguration()).thenReturn(backendConfiguration); + + // when + fallbackDomainFetcher.run(); + } + + @Test + public void givenFallbackDomainInCache_whenExecuting_thenReturnAndIgnoreDatabaseAndApiCalls() { + // given + Cache.setFallbackDomain("dummy_domain_0"); + + // when + fallbackDomainFetcher.run(); + + // then + assert Cache.getFallbackDomain().equals("dummy_domain_0"); + } +} diff --git a/src/test/java/com/wire/bots/hold/utils/CacheTest.java b/src/test/java/com/wire/bots/hold/utils/CacheTest.java new file mode 100644 index 0000000..ddbf2dd --- /dev/null +++ b/src/test/java/com/wire/bots/hold/utils/CacheTest.java @@ -0,0 +1,38 @@ +package com.wire.bots.hold.utils; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class CacheTest { + + @Before + public void before() { + // Clears cached domain + Cache.setFallbackDomain(null); + } + + @After + public void after() { + // Clears cached domain + Cache.setFallbackDomain(null); + } + + @Test + public void verifyDefaultDomainIsSetCorrectly() { + String firstDomain = Cache.getFallbackDomain(); + assert firstDomain == null; + + Cache.setFallbackDomain("dummy_domain"); + String secondDomain = Cache.getFallbackDomain(); + + assert secondDomain != null; + assert secondDomain.equals("dummy_domain"); + + Cache.setFallbackDomain("dummy_domain_3"); + String thirdDomain = Cache.getFallbackDomain(); + + assert thirdDomain != null; + assert thirdDomain.equals("dummy_domain_3"); + } +} diff --git a/src/test/java/com/wire/bots/hold/utils/TestCache.java b/src/test/java/com/wire/bots/hold/utils/TestCache.java index 1ea23e8..3a16723 100644 --- a/src/test/java/com/wire/bots/hold/utils/TestCache.java +++ b/src/test/java/com/wire/bots/hold/utils/TestCache.java @@ -33,22 +33,4 @@ public User getUser(QualifiedId userId) { return dummyUser; } - - @Test - public void verifyDefaultDomainIsSetCorrectly() { - String firstDomain = Cache.getDefaultDomain(); - assert firstDomain == null; - - Cache.setDefaultDomain("dummy_domain"); - String secondDomain = Cache.getDefaultDomain(); - - assert secondDomain != null; - assert secondDomain.equals("dummy_domain"); - - Cache.setDefaultDomain("dummy_domain_3"); - String thirdDomain = Cache.getDefaultDomain(); - - assert thirdDomain != null; - assert thirdDomain.equals("dummy_domain"); - } } From b2137d607415eb451b23ff39b95cfd2a9fd9617a Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Mon, 7 Oct 2024 14:59:51 +0200 Subject: [PATCH 4/5] feat(get-domain-from-backend) Use new Helium API to fetch backend domain * Update Helium to 1.4.1 * Add assertions to major tests * Remove Consts.java from tests * Add call to get api version and default domain * Persist fallback domain to database * Add FallbackDomainFetcher to handle fetch from cache, database and API * Properly receive thrown exception from dropwizard task execution * Add Mockito for testing mocks * Add JavaDoc for FallbackDomainFetcher --- pom.xml | 4 +-- .../wire/bots/hold/FallbackDomainFetcher.java | 9 +++++ src/main/java/com/wire/bots/hold/Service.java | 36 +++++++++++++++---- .../db/migration/V107__add_metadata_table.sql | 4 +-- .../bots/hold/FallbackDomainFetcherTest.java | 2 +- 5 files changed, 43 insertions(+), 12 deletions(-) diff --git a/pom.xml b/pom.xml index c255d0e..33772e3 100644 --- a/pom.xml +++ b/pom.xml @@ -137,8 +137,8 @@ org.mockito - mockito-all - 1.10.19 + mockito-core + 5.14.1 test diff --git a/src/main/java/com/wire/bots/hold/FallbackDomainFetcher.java b/src/main/java/com/wire/bots/hold/FallbackDomainFetcher.java index 58e7a40..40741e9 100644 --- a/src/main/java/com/wire/bots/hold/FallbackDomainFetcher.java +++ b/src/main/java/com/wire/bots/hold/FallbackDomainFetcher.java @@ -19,6 +19,12 @@ public class FallbackDomainFetcher implements Runnable { * Fetches from API and compares against database value (if any), then inserts into database and updates cache value. * If value received from the API is different from what is saved in the database, a [RuntimeException] is thrown. *

+ *

+ * This fallback domain is necessary for LegalHold to work with Federation (as it needs id@domain) and not just the ID anymore. + * In case there is a mismatch we are throwing a RuntimeException so it stops the execution of this app, so in an event + * of already having a defined default domain saved in the database and this app restarts with a different domain + * we don't get mismatching domains. + *

* @param loginClient [{@link LoginClient}] as API to get backend configuration containing default domain. * @param metadataDAO [{@link MetadataDAO}] as DAO to get/insert default domain to database. * @@ -41,9 +47,12 @@ public void run() { metadataDAO.insert(MetadataDAO.FALLBACK_DOMAIN_KEY, apiVersionResponse.domain); Cache.setFallbackDomain(apiVersionResponse.domain); } else { + System.out.println("EEEEEEEE -> " + apiVersionResponse.domain); if (metadata.value.equals(apiVersionResponse.domain)) { + System.out.println("EEEEEEEE -> p1"); Cache.setFallbackDomain(apiVersionResponse.domain); } else { + System.out.println("EEEEEEEE -> p2"); String formattedExceptionMessage = String.format( "Database already has a default domain as %s and instead we got %s from the Backend API.", metadata.value, diff --git a/src/main/java/com/wire/bots/hold/Service.java b/src/main/java/com/wire/bots/hold/Service.java index 4b2615f..61a1ed8 100644 --- a/src/main/java/com/wire/bots/hold/Service.java +++ b/src/main/java/com/wire/bots/hold/Service.java @@ -55,6 +55,8 @@ import org.jdbi.v3.sqlobject.SqlObjectPlugin; import javax.ws.rs.client.Client; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; public class Service extends Application { @@ -92,7 +94,7 @@ protected SwaggerBundleConfiguration getSwaggerBundleConfiguration(Config config } @Override - public void run(Config config, Environment environment) { + public void run(Config config, Environment environment) throws ExecutionException, InterruptedException { this.config = config; this.environment = environment; Service.metrics = environment.metrics(); @@ -129,17 +131,37 @@ public void run(Config config, Environment environment) { addResource(ServiceAuthenticationFilter.ServiceAuthenticationFeature.class); - environment - .lifecycle() - .executorService("fallback_domain_fetcher") - .build() - .execute( +// environment +// .lifecycle() +// .executorService("fallback_domain_fetcher") +// .build() +// .submit( +// new FallbackDomainFetcher( +// new LoginClient(httpClient), +// metadataDAO +// ) +// ); + + Runnable run = new Runnable() { + @Override + public void run(){ new FallbackDomainFetcher( new LoginClient(httpClient), metadataDAO - ) + ).run(); + } + }; + + final Future fallbackDomainFetcher = environment + .lifecycle() + .executorService("fallback_domain_fetcher") + .build() + .submit( + run ); + fallbackDomainFetcher.get(); + environment.healthChecks().register( "SanityCheck", new SanityCheck(accessDAO, httpClient) diff --git a/src/main/resources/db/migration/V107__add_metadata_table.sql b/src/main/resources/db/migration/V107__add_metadata_table.sql index 86e2406..ef255d4 100644 --- a/src/main/resources/db/migration/V107__add_metadata_table.sql +++ b/src/main/resources/db/migration/V107__add_metadata_table.sql @@ -1,4 +1,4 @@ CREATE TABLE Metadata ( - key VARCHAR NOT NULL UNIQUE, - value VARCHAR NOT NULL + key VARCHAR(255) PRIMARY KEY, + value VARCHAR(255) NOT NULL ); diff --git a/src/test/java/com/wire/bots/hold/FallbackDomainFetcherTest.java b/src/test/java/com/wire/bots/hold/FallbackDomainFetcherTest.java index 0b83ffe..26f9c4c 100644 --- a/src/test/java/com/wire/bots/hold/FallbackDomainFetcherTest.java +++ b/src/test/java/com/wire/bots/hold/FallbackDomainFetcherTest.java @@ -10,7 +10,7 @@ import org.junit.Before; import org.junit.Test; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mockito.Mockito.verify; From c7e416cda9beeec7ad1581fa703ff6dc902fac8c Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Mon, 7 Oct 2024 14:59:51 +0200 Subject: [PATCH 5/5] feat(get-domain-from-backend) Use new Helium API to fetch backend domain * Update Helium to 1.4.1 * Add assertions to major tests * Remove Consts.java from tests * Add call to get api version and default domain * Persist fallback domain to database * Add FallbackDomainFetcher to handle fetch from cache, database and API * Properly receive thrown exception from dropwizard task execution * Add Mockito for testing mocks * Add JavaDoc for FallbackDomainFetcher --- pom.xml | 4 +-- .../wire/bots/hold/FallbackDomainFetcher.java | 9 +++++++ src/main/java/com/wire/bots/hold/Service.java | 25 +++++++++++++------ .../db/migration/V107__add_metadata_table.sql | 4 +-- .../bots/hold/FallbackDomainFetcherTest.java | 2 +- 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/pom.xml b/pom.xml index c255d0e..33772e3 100644 --- a/pom.xml +++ b/pom.xml @@ -137,8 +137,8 @@ org.mockito - mockito-all - 1.10.19 + mockito-core + 5.14.1 test diff --git a/src/main/java/com/wire/bots/hold/FallbackDomainFetcher.java b/src/main/java/com/wire/bots/hold/FallbackDomainFetcher.java index 58e7a40..40741e9 100644 --- a/src/main/java/com/wire/bots/hold/FallbackDomainFetcher.java +++ b/src/main/java/com/wire/bots/hold/FallbackDomainFetcher.java @@ -19,6 +19,12 @@ public class FallbackDomainFetcher implements Runnable { * Fetches from API and compares against database value (if any), then inserts into database and updates cache value. * If value received from the API is different from what is saved in the database, a [RuntimeException] is thrown. *

+ *

+ * This fallback domain is necessary for LegalHold to work with Federation (as it needs id@domain) and not just the ID anymore. + * In case there is a mismatch we are throwing a RuntimeException so it stops the execution of this app, so in an event + * of already having a defined default domain saved in the database and this app restarts with a different domain + * we don't get mismatching domains. + *

* @param loginClient [{@link LoginClient}] as API to get backend configuration containing default domain. * @param metadataDAO [{@link MetadataDAO}] as DAO to get/insert default domain to database. * @@ -41,9 +47,12 @@ public void run() { metadataDAO.insert(MetadataDAO.FALLBACK_DOMAIN_KEY, apiVersionResponse.domain); Cache.setFallbackDomain(apiVersionResponse.domain); } else { + System.out.println("EEEEEEEE -> " + apiVersionResponse.domain); if (metadata.value.equals(apiVersionResponse.domain)) { + System.out.println("EEEEEEEE -> p1"); Cache.setFallbackDomain(apiVersionResponse.domain); } else { + System.out.println("EEEEEEEE -> p2"); String formattedExceptionMessage = String.format( "Database already has a default domain as %s and instead we got %s from the Backend API.", metadata.value, diff --git a/src/main/java/com/wire/bots/hold/Service.java b/src/main/java/com/wire/bots/hold/Service.java index 4b2615f..dcae767 100644 --- a/src/main/java/com/wire/bots/hold/Service.java +++ b/src/main/java/com/wire/bots/hold/Service.java @@ -55,6 +55,8 @@ import org.jdbi.v3.sqlobject.SqlObjectPlugin; import javax.ws.rs.client.Client; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; public class Service extends Application { @@ -92,7 +94,7 @@ protected SwaggerBundleConfiguration getSwaggerBundleConfiguration(Config config } @Override - public void run(Config config, Environment environment) { + public void run(Config config, Environment environment) throws ExecutionException, InterruptedException { this.config = config; this.environment = environment; Service.metrics = environment.metrics(); @@ -129,17 +131,26 @@ public void run(Config config, Environment environment) { addResource(ServiceAuthenticationFilter.ServiceAuthenticationFeature.class); - environment - .lifecycle() - .executorService("fallback_domain_fetcher") - .build() - .execute( + Runnable run = new Runnable() { + @Override + public void run(){ new FallbackDomainFetcher( new LoginClient(httpClient), metadataDAO - ) + ).run(); + } + }; + + final Future fallbackDomainFetcher = environment + .lifecycle() + .executorService("fallback_domain_fetcher") + .build() + .submit( + run ); + fallbackDomainFetcher.get(); + environment.healthChecks().register( "SanityCheck", new SanityCheck(accessDAO, httpClient) diff --git a/src/main/resources/db/migration/V107__add_metadata_table.sql b/src/main/resources/db/migration/V107__add_metadata_table.sql index 86e2406..ef255d4 100644 --- a/src/main/resources/db/migration/V107__add_metadata_table.sql +++ b/src/main/resources/db/migration/V107__add_metadata_table.sql @@ -1,4 +1,4 @@ CREATE TABLE Metadata ( - key VARCHAR NOT NULL UNIQUE, - value VARCHAR NOT NULL + key VARCHAR(255) PRIMARY KEY, + value VARCHAR(255) NOT NULL ); diff --git a/src/test/java/com/wire/bots/hold/FallbackDomainFetcherTest.java b/src/test/java/com/wire/bots/hold/FallbackDomainFetcherTest.java index 0b83ffe..26f9c4c 100644 --- a/src/test/java/com/wire/bots/hold/FallbackDomainFetcherTest.java +++ b/src/test/java/com/wire/bots/hold/FallbackDomainFetcherTest.java @@ -10,7 +10,7 @@ import org.junit.Before; import org.junit.Test; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mockito.Mockito.verify;