diff options
author | Vitaly Takmazov | 2023-01-14 12:22:53 +0300 |
---|---|---|
committer | Vitaly Takmazov | 2023-01-14 13:59:24 +0300 |
commit | 834fef2a7794d769144c7c087a8b1fa0a2a9bb21 (patch) | |
tree | f44d2139be62ed7382c83ff234a557be66a9bde5 | |
parent | 456f2a28efc72809c4ac7dd7ee838e0e5b0a061f (diff) |
DB: merge ios, android, winphone tables to user_services
-rw-r--r-- | src/main/java/com/juick/service/PushQueriesService.java | 29 | ||||
-rw-r--r-- | src/main/java/com/juick/service/PushQueriesServiceImpl.java | 124 | ||||
-rw-r--r-- | src/main/java/com/juick/www/api/Notifications.java | 45 | ||||
-rw-r--r-- | src/main/resources/db/migration/V1.30__token_type.sql | 4 | ||||
-rw-r--r-- | src/main/resources/db/migration/V1.31__user_services.sql | 1 | ||||
-rw-r--r-- | src/main/resources/db/migration/V1.32__drop_token_tables.sql | 2 | ||||
-rw-r--r-- | src/main/resources/db/specific/h2/V1.22__schema.sql | 2 | ||||
-rw-r--r-- | src/test/java/com/juick/server/tests/ServerTests.java | 15 | ||||
-rw-r--r-- | vnext/server/middleware/event.js | 2 | ||||
-rw-r--r-- | vnext/server/sender.js | 4 |
10 files changed, 81 insertions, 147 deletions
diff --git a/src/main/java/com/juick/service/PushQueriesService.java b/src/main/java/com/juick/service/PushQueriesService.java index ef33f318..7da89dee 100644 --- a/src/main/java/com/juick/service/PushQueriesService.java +++ b/src/main/java/com/juick/service/PushQueriesService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2020, Juick + * Copyright (C) 2008-2023, Juick * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,34 +17,19 @@ package com.juick.service; +import com.juick.model.ExternalToken; + import java.util.Collection; -import java.util.List; /** * Created by aalexeev on 11/13/16. */ public interface PushQueriesService { - List<String> getGCMRegID(int uid); - - List<String> getGCMTokens(Collection<Integer> uids); - - boolean addGCMToken(Integer uid, String token); - - boolean deleteGCMToken(String token); - - List<String> getMPNSURL(int uid); - - List<String> getMPNSTokens(Collection<Integer> uids); - - boolean addMPNSToken(Integer uid, String token); - - boolean deleteMPNSToken(String token); - - List<String> getAPNSToken(int uid); + Collection<ExternalToken> getToken(int uid, String serviceType); - List<String> getAPNSTokens(Collection<Integer> uids); + Collection<ExternalToken> getTokens(Collection<Integer> uids); - boolean addAPNSToken(Integer uid, String token); + boolean addToken(Integer uid, String serviceType, String token); - boolean deleteAPNSToken(String token); + boolean deleteToken(String serviceType, String token); } diff --git a/src/main/java/com/juick/service/PushQueriesServiceImpl.java b/src/main/java/com/juick/service/PushQueriesServiceImpl.java index 0b83465a..18a14bd6 100644 --- a/src/main/java/com/juick/service/PushQueriesServiceImpl.java +++ b/src/main/java/com/juick/service/PushQueriesServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2020, Juick + * Copyright (C) 2008-2023, Juick * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,15 +17,18 @@ package com.juick.service; +import com.juick.model.ExternalToken; import org.apache.commons.collections4.CollectionUtils; import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.Collection; import java.util.Collections; -import java.util.List; /** * Created by aalexeev on 11/13/16. @@ -33,113 +36,56 @@ import java.util.List; @Repository public class PushQueriesServiceImpl extends BaseJdbcService implements PushQueriesService { - @Transactional(readOnly = true) - @Override - public List<String> getGCMRegID(final int uid) { - return getJdbcTemplate().queryForList( - "SELECT regid FROM android WHERE user_id=?", - String.class, - uid); - } - - @Transactional(readOnly = true) - @Override - public List<String> getGCMTokens(final Collection<Integer> uids) { - if (CollectionUtils.isEmpty(uids)) - return Collections.emptyList(); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT regid FROM android INNER JOIN users ON (users.id = android.user_id) WHERE users.id IN (:ids)", - new MapSqlParameterSource("ids", uids), - String.class); - } + private class TokenMapper implements RowMapper<ExternalToken> { - @Transactional - @Override - public boolean addGCMToken(Integer uid, String token) { - try { - return getJdbcTemplate().update("INSERT INTO android(user_id,regid) VALUES (?, ?)", - uid, token) > 0; - } catch (DataIntegrityViolationException e) { - return false; + @Override + public ExternalToken mapRow(ResultSet rs, int rowNum) throws SQLException { + return new ExternalToken( + null, + rs.getString("service_type"), + rs.getString("regid"), + null + ); } } - @Transactional - @Override - public boolean deleteGCMToken(String token) { - return getJdbcTemplate().update("DELETE FROM android WHERE regid=?", token) > 0; - } - @Transactional(readOnly = true) @Override - public List<String> getMPNSURL(final int uid) { - return getJdbcTemplate().queryForList( - "SELECT url FROM winphone WHERE user_id=?", - String.class, - uid); + public Collection<ExternalToken> getToken(final int uid, final String serviceType) { + return getJdbcTemplate().query( + "SELECT regid, service_type FROM user_services WHERE user_id=? AND service_type=?", + new TokenMapper(), + uid, serviceType); } @Transactional(readOnly = true) @Override - public List<String> getMPNSTokens(final Collection<Integer> uids) { + public Collection<ExternalToken> getTokens(final Collection<Integer> uids) { if (CollectionUtils.isEmpty(uids)) return Collections.emptyList(); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT url FROM winphone INNER JOIN users ON (users.id=winphone.user_id) WHERE users.id IN (:ids)", + return getNamedParameterJdbcTemplate().query( + """ + SELECT regid, service_type FROM user_services INNER JOIN users + ON (users.id = user_services.user_id) WHERE users.id IN (:ids)""", new MapSqlParameterSource("ids", uids), - String.class); - } - - @Transactional - @Override - public boolean addMPNSToken(Integer uid, String token) { - return getJdbcTemplate().update("INSERT IGNORE INTO winphone(user_id,url) VALUES (?, ?)", - uid, token) > 0; - } - - @Transactional - @Override - public boolean deleteMPNSToken(String token) { - return getJdbcTemplate().update("DELETE FROM winphone WHERE url=?", token) > 0; - } - - @Transactional(readOnly = true) - @Override - public List<String> getAPNSToken(final int uid) { - return getJdbcTemplate().queryForList( - "SELECT token from ios WHERE user_id=?", - String.class, - uid); + new TokenMapper()); } @Transactional @Override - public boolean deleteAPNSToken(String token) { - return getJdbcTemplate().update("DELETE FROM ios WHERE token=?", token) > 0; - } - - @Transactional(readOnly = true) - @Override - public List<String> getAPNSTokens(final Collection<Integer> uids) { - if (CollectionUtils.isEmpty(uids)) - return Collections.emptyList(); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT token FROM ios INNER JOIN users ON (users.id = ios.user_id) WHERE users.id IN (:ids)", - new MapSqlParameterSource("ids", uids), - String.class); + public boolean addToken(final Integer uid, final String serviceType, final String token) { + try { + return getJdbcTemplate().update( + "INSERT INTO user_services(user_id, regid, service_type) VALUES (?, ?, ?)", + uid, token, serviceType) > 0; + } catch (DataIntegrityViolationException e) { + return false; + } } @Transactional @Override - public boolean addAPNSToken(Integer uid, String token) { - try { - return getJdbcTemplate().update("INSERT INTO ios(user_id,token) VALUES (?, ?)", - uid, token) > 0; - } catch (DataIntegrityViolationException e) { - return true; - } + public boolean deleteToken(final String serviceType, final String token) { + return getJdbcTemplate().update("DELETE FROM user_services WHERE regid=? AND service_type=?", token, serviceType) > 0; } } diff --git a/src/main/java/com/juick/www/api/Notifications.java b/src/main/java/com/juick/www/api/Notifications.java index e5e85eb7..1ebe3c61 100644 --- a/src/main/java/com/juick/www/api/Notifications.java +++ b/src/main/java/com/juick/www/api/Notifications.java @@ -19,10 +19,10 @@ package com.juick.www.api; import com.juick.model.*; import com.juick.service.*; -import com.juick.util.HttpBadRequestException; import com.juick.util.HttpForbiddenException; import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Parameter; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; @@ -31,6 +31,7 @@ import org.springframework.web.bind.annotation.*; import javax.inject.Inject; import java.util.Collections; import java.util.List; +import java.util.stream.Stream; /** * Created by vitalyster on 24.10.2016. @@ -55,17 +56,12 @@ public class Notifications { private User collectTokens(Integer uid) { User user = userService.getUserByUID(uid).orElse(AnonymousUser.INSTANCE); user.setUnreadCount(messagesService.getUnread(user).size()); - pushQueriesService.getGCMRegID(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "gcm", t, null))); - pushQueriesService.getAPNSToken(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "apns", t, null))); - pushQueriesService.getMPNSURL(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "mpns", t, null))); - List<ExternalToken> xmppJids = userService.getJIDsbyUID(uid).stream() - .map(jid -> new ExternalToken(null, "xmpp", jid, null)) - .toList(); - user.getTokens().addAll(xmppJids); - List<ExternalToken> tgIds = telegramService.getTelegramIdentifiers(Collections.singletonList(user)).stream() - .map(tgId -> new ExternalToken(null, "durov", String.valueOf(tgId), null)) - .toList(); - user.getTokens().addAll(tgIds); + var tokens = pushQueriesService.getTokens(List.of(user.getUid())); + var xmppJids = userService.getJIDsbyUID(uid).stream() + .map(jid -> new ExternalToken(null, "xmpp", jid, null)); + var tgIds = telegramService.getTelegramIdentifiers(Collections.singletonList(user)).stream() + .map(tgId -> new ExternalToken(null, "durov", String.valueOf(tgId), null)); + user.setTokens(Stream.concat(Stream.concat(tokens.stream(), xmppJids), tgIds).toList()); return user; } @@ -117,12 +113,7 @@ public class Notifications { throw new HttpForbiddenException(); } list.forEach(t -> { - switch (t.type()) { - case "gcm" -> pushQueriesService.deleteGCMToken(t.token()); - case "apns" -> pushQueriesService.deleteAPNSToken(t.token()); - case "mpns" -> pushQueriesService.deleteMPNSToken(t.token()); - default -> throw new HttpBadRequestException(); - } + pushQueriesService.deleteToken(t.type(), t.token()); }); return Status.OK; @@ -136,12 +127,7 @@ public class Notifications { throw new HttpForbiddenException(); } list.forEach(t -> { - switch (t.type()) { - case "gcm" -> pushQueriesService.deleteGCMToken(t.token()); - case "apns" -> pushQueriesService.deleteAPNSToken(t.token()); - case "mpns" -> pushQueriesService.deleteMPNSToken(t.token()); - default -> throw new HttpBadRequestException(); - } + pushQueriesService.deleteToken(t.type(), t.token()); }); return Status.OK; @@ -153,12 +139,7 @@ public class Notifications { @Parameter(hidden = true) User visitor, @RequestBody List<ExternalToken> list) { list.forEach(t -> { - switch (t.type()) { - case "gcm" -> pushQueriesService.addGCMToken(visitor.getUid(), t.token()); - case "apns" -> pushQueriesService.addAPNSToken(visitor.getUid(), t.token()); - case "mpns" -> pushQueriesService.addMPNSToken(visitor.getUid(), t.token()); - default -> throw new HttpBadRequestException(); - } + pushQueriesService.addToken(visitor.getUid(), t.type(), t.token()); }); return Status.OK; } @@ -168,7 +149,7 @@ public class Notifications { public Status doAndroidRegister( @Parameter(hidden = true) User visitor, @RequestParam(name = "regid") String regId) { - pushQueriesService.addGCMToken(visitor.getUid(), regId); + pushQueriesService.addToken(visitor.getUid(),"fcm", regId); return Status.OK; } @@ -177,7 +158,7 @@ public class Notifications { public Status doWinphoneRegister( @Parameter(hidden = true) User visitor, @RequestParam(name = "url") String regId) { - pushQueriesService.addMPNSToken(visitor.getUid(), regId); + pushQueriesService.addToken(visitor.getUid(), "mpns", regId); return Status.OK; } } diff --git a/src/main/resources/db/migration/V1.30__token_type.sql b/src/main/resources/db/migration/V1.30__token_type.sql new file mode 100644 index 00000000..601695d8 --- /dev/null +++ b/src/main/resources/db/migration/V1.30__token_type.sql @@ -0,0 +1,4 @@ +alter table android add column service_type varchar(255) not null default 'fcm'; +insert into android (user_id, ts, regid, service_type) select user_id, ts, url regid, 'mpns' from winphone; +insert into android (user_id, ts, regid, service_type) select user_id, ts, token regid, 'apns' from ios; + diff --git a/src/main/resources/db/migration/V1.31__user_services.sql b/src/main/resources/db/migration/V1.31__user_services.sql new file mode 100644 index 00000000..cca1f9dd --- /dev/null +++ b/src/main/resources/db/migration/V1.31__user_services.sql @@ -0,0 +1 @@ +alter table android rename to user_services diff --git a/src/main/resources/db/migration/V1.32__drop_token_tables.sql b/src/main/resources/db/migration/V1.32__drop_token_tables.sql new file mode 100644 index 00000000..a1599670 --- /dev/null +++ b/src/main/resources/db/migration/V1.32__drop_token_tables.sql @@ -0,0 +1,2 @@ +DROP TABLE ios; +DROP TABLE winphone diff --git a/src/main/resources/db/specific/h2/V1.22__schema.sql b/src/main/resources/db/specific/h2/V1.22__schema.sql index de6946e8..46d39ac6 100644 --- a/src/main/resources/db/specific/h2/V1.22__schema.sql +++ b/src/main/resources/db/specific/h2/V1.22__schema.sql @@ -3,7 +3,7 @@ SET DB_CLOSE_DELAY -1; CREATE MEMORY TABLE "PUBLIC"."ANDROID"( "USER_ID" INTEGER NOT NULL, - "REGID" CHARACTER(255) NOT NULL, + "REGID" VARCHAR(255) NOT NULL, "TS" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL ); -- 0 +/- SELECT COUNT(*) FROM PUBLIC.ANDROID; diff --git a/src/test/java/com/juick/server/tests/ServerTests.java b/src/test/java/com/juick/server/tests/ServerTests.java index 8956c721..a6c840bf 100644 --- a/src/test/java/com/juick/server/tests/ServerTests.java +++ b/src/test/java/com/juick/server/tests/ServerTests.java @@ -280,6 +280,8 @@ public class ServerTests { private User archiveUser; @Inject private ConversionService conversionService; + @Inject + private PushQueriesService pushQueriesService; private static User ugnich, freefd; static String ugnichName, ugnichPassword, freefdName, freefdPassword; @@ -2753,6 +2755,19 @@ public class ServerTests { public void getLastRepliesShouldNotCrash() { messagesService.getLastMessages(48); } + @Test + public void userServicesTest() throws Exception { + jdbcTemplate.execute("DELETE FROM user_services"); + pushQueriesService.addToken(ugnich.getUid(), "fcm", "12345"); + pushQueriesService.addToken(ugnich.getUid(), "mpns", "23456"); + pushQueriesService.addToken(ugnich.getUid(), "xmpp", "234567"); + pushQueriesService.addToken(ugnich.getUid(), "durov", "345678"); + assertThat(pushQueriesService.getTokens(List.of(ugnich.getUid())).size(), is(4)); + mockMvc.perform(get("/api/notifications") + .with(httpBasic(serviceUser.getName(), serviceUser.getCredentials())) + .param("uid", String.valueOf(ugnich.getUid()))) + .andExpect(status().isOk()); + } /* @Test public void tokenAuth() throws Exception { diff --git a/vnext/server/middleware/event.js b/vnext/server/middleware/event.js index 1267d1c4..deb974ee 100644 --- a/vnext/server/middleware/event.js +++ b/vnext/server/middleware/event.js @@ -44,7 +44,7 @@ function processMessageEvent(msg) { users.forEach(user => { log(`${user.uname}: ${user.unreadCount}`); let [sandboxTokens, productionTokens] = (user.tokens || []) - .filter(t => ['mpns', 'apns', 'gcm'].includes(t.type)) + .filter(t => ['mpns', 'apns', 'fcm'].includes(t.type)) .map(t => t.token) .reduce((result, element, i) => { allSandboxIds.includes(user.uid) diff --git a/vnext/server/sender.js b/vnext/server/sender.js index f33eadcf..7adb1ceb 100644 --- a/vnext/server/sender.js +++ b/vnext/server/sender.js @@ -69,10 +69,10 @@ export function sendNotification(productionIds, sandboxIds, data) { log(`${badTokens.length} APNS tokens deleted`); } }); - results.filter(r => r.method === 'gcm') + results.filter(r => r.method === 'fcm') .forEach(async r => { let badTokens = r.message.filter(m => m.errorMsg === 'NotRegistered' || m.errorMsg === 'MismatchSenderId').map(m => { - return { 'type': 'gcm', 'token': m.regId }; + return { 'type': 'fcm', 'token': m.regId }; }); if (badTokens.length > 0) { await deleteSubscribers(badTokens); |