From 954405b325276f88266359d6d8e449ea7045a4cf Mon Sep 17 00:00:00 2001 From: Antoine PRONNIER Date: Thu, 19 Dec 2024 12:30:22 +0100 Subject: [PATCH 1/6] WIP: Refacto Twitch module --- .../services/AccountingServiceTest.java | 10 +- .../ressources/GoogleCaptchaResourceTest.java | 4 +- .../resources/BillingResourceTest.java | 6 +- .../services/BillingCrudServiceTest.java | 6 +- .../PaypalAccessTokenServiceTest.java | 4 +- .../resources/PaypalOrderResourceTest.java | 6 +- .../services/PaypalOrderServiceTest.java | 6 +- .../clients/TwitchTokenAuthClient.java | 1 + .../clients/TwitchValidTokenClient.java | 4 +- .../resources/TwitchAuthResourceTest.java | 10 +- .../TestTwitchServerTokenService.java | 4 +- .../TwitchEventSubWebsocketMessage.java | 4 + .../service/security/WebSecurity.java | 1 + .../TwitchEventSubCallbackService.java | 93 ++++++++++--------- .../services/TwitchEventSubHmacService.java | 16 ++-- .../TwitchEventSubReferenceService.java | 3 +- .../handler/TwitchEventSubHandlerService.java | 60 ------------ .../TwitchEventSubWebsocketService.java | 68 ++++++++++---- .../TwitchEventSubCallbackServiceTest.java | 10 -- .../TwitchEventSubReferenceServiceTest.java | 8 +- ...TwitchEventSubRegistrationServiceTest.java | 10 +- ...itchReferenceChannelPointsServiceTest.java | 6 +- .../TwitchReferenceChannelServiceTest.java | 4 +- .../service/components/UserTestComponent.java | 10 +- ...TestUserUpdateSelfAccountResourceTest.java | 4 +- 25 files changed, 163 insertions(+), 195 deletions(-) delete mode 100644 modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/handler/TwitchEventSubHandlerService.java diff --git a/modules/accounting/service/src/test/java/com/funixproductions/api/accounting/service/services/AccountingServiceTest.java b/modules/accounting/service/src/test/java/com/funixproductions/api/accounting/service/services/AccountingServiceTest.java index 5f2fe789..e43d4ff9 100644 --- a/modules/accounting/service/src/test/java/com/funixproductions/api/accounting/service/services/AccountingServiceTest.java +++ b/modules/accounting/service/src/test/java/com/funixproductions/api/accounting/service/services/AccountingServiceTest.java @@ -13,7 +13,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import java.util.List; import java.util.Random; @@ -30,16 +30,16 @@ class AccountingServiceTest { @Autowired private AccountingService accountingService; - @MockBean + @MockitoBean private IncomeRepository incomeRepository; - @MockBean + @MockitoBean private ProductRepository productRepository; - @MockBean + @MockitoBean private GoogleGmailClient gmailClient; - @MockBean + @MockitoBean private BillingFeignInternalClient billingClient; @Test diff --git a/modules/google/reCaptcha/service/src/test/java/com/funixproductions/api/google/recaptcha/service/ressources/GoogleCaptchaResourceTest.java b/modules/google/reCaptcha/service/src/test/java/com/funixproductions/api/google/recaptcha/service/ressources/GoogleCaptchaResourceTest.java index d814e251..d740825e 100644 --- a/modules/google/reCaptcha/service/src/test/java/com/funixproductions/api/google/recaptcha/service/ressources/GoogleCaptchaResourceTest.java +++ b/modules/google/reCaptcha/service/src/test/java/com/funixproductions/api/google/recaptcha/service/ressources/GoogleCaptchaResourceTest.java @@ -9,7 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; import static org.mockito.ArgumentMatchers.anyString; @@ -25,7 +25,7 @@ class GoogleCaptchaResourceTest { @Autowired private MockMvc mockMvc; - @MockBean + @MockitoBean private GoogleRecaptchaClient googleRecaptchaClient; @Test diff --git a/modules/payment/billing/service/src/test/java/com/funixproductions/api/payment/billing/service/resources/BillingResourceTest.java b/modules/payment/billing/service/src/test/java/com/funixproductions/api/payment/billing/service/resources/BillingResourceTest.java index aef59ee6..a75598fc 100644 --- a/modules/payment/billing/service/src/test/java/com/funixproductions/api/payment/billing/service/resources/BillingResourceTest.java +++ b/modules/payment/billing/service/src/test/java/com/funixproductions/api/payment/billing/service/resources/BillingResourceTest.java @@ -11,8 +11,8 @@ import com.funixproductions.core.crud.dtos.PageDTO; import com.funixproductions.core.tools.pdf.tools.VATInformation; import org.junit.jupiter.api.BeforeEach; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.core.io.ByteArrayResource; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import java.util.Date; import java.util.List; @@ -24,10 +24,10 @@ public abstract class BillingResourceTest { - @MockBean + @MockitoBean protected UserAuthClient userAuthClient; - @MockBean + @MockitoBean protected BillingCrudService billingCrudService; protected final BillingDTO billingDTO = new BillingDTO(); diff --git a/modules/payment/billing/service/src/test/java/com/funixproductions/api/payment/billing/service/services/BillingCrudServiceTest.java b/modules/payment/billing/service/src/test/java/com/funixproductions/api/payment/billing/service/services/BillingCrudServiceTest.java index 7718bdd9..ca6cd11e 100644 --- a/modules/payment/billing/service/src/test/java/com/funixproductions/api/payment/billing/service/services/BillingCrudServiceTest.java +++ b/modules/payment/billing/service/src/test/java/com/funixproductions/api/payment/billing/service/services/BillingCrudServiceTest.java @@ -19,7 +19,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import java.io.File; import java.time.LocalDate; @@ -41,10 +41,10 @@ class BillingCrudServiceTest { @Autowired private BillingRepository billingRepository; - @MockBean + @MockitoBean private CurrentSession currentSession; - @MockBean + @MockitoBean private GoogleGmailClient googleGmailClient; @BeforeEach diff --git a/modules/payment/paypal/service/src/test/java/com/funixproductions/api/payment/paypal/service/auth/services/PaypalAccessTokenServiceTest.java b/modules/payment/paypal/service/src/test/java/com/funixproductions/api/payment/paypal/service/auth/services/PaypalAccessTokenServiceTest.java index 1c6db719..e2124d75 100644 --- a/modules/payment/paypal/service/src/test/java/com/funixproductions/api/payment/paypal/service/auth/services/PaypalAccessTokenServiceTest.java +++ b/modules/payment/paypal/service/src/test/java/com/funixproductions/api/payment/paypal/service/auth/services/PaypalAccessTokenServiceTest.java @@ -12,7 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; @@ -28,7 +28,7 @@ @AutoConfigureMockMvc class PaypalAccessTokenServiceTest { - @MockBean + @MockitoBean private PaypalAuthClient paypalAuthClient; @Autowired diff --git a/modules/payment/paypal/service/src/test/java/com/funixproductions/api/payment/paypal/service/orders/resources/PaypalOrderResourceTest.java b/modules/payment/paypal/service/src/test/java/com/funixproductions/api/payment/paypal/service/orders/resources/PaypalOrderResourceTest.java index db8db630..c7ba2db3 100644 --- a/modules/payment/paypal/service/src/test/java/com/funixproductions/api/payment/paypal/service/orders/resources/PaypalOrderResourceTest.java +++ b/modules/payment/paypal/service/src/test/java/com/funixproductions/api/payment/paypal/service/orders/resources/PaypalOrderResourceTest.java @@ -17,8 +17,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; @@ -40,13 +40,13 @@ class PaypalOrderResourceTest { @Autowired private MockMvc mockMvc; - @MockBean + @MockitoBean private PaypalOrderService service; @Autowired private JsonHelper jsonHelper; - @MockBean + @MockitoBean private BillingFeignInternalClient billingClient; @BeforeEach diff --git a/modules/payment/paypal/service/src/test/java/com/funixproductions/api/payment/paypal/service/orders/services/PaypalOrderServiceTest.java b/modules/payment/paypal/service/src/test/java/com/funixproductions/api/payment/paypal/service/orders/services/PaypalOrderServiceTest.java index 61ec2290..710ce50c 100644 --- a/modules/payment/paypal/service/src/test/java/com/funixproductions/api/payment/paypal/service/orders/services/PaypalOrderServiceTest.java +++ b/modules/payment/paypal/service/src/test/java/com/funixproductions/api/payment/paypal/service/orders/services/PaypalOrderServiceTest.java @@ -13,7 +13,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import java.nio.charset.StandardCharsets; import java.util.HashMap; @@ -26,10 +26,10 @@ @AutoConfigureMockMvc class PaypalOrderServiceTest { - @MockBean + @MockitoBean private PaypalFeignOrderClient orderClient; - @MockBean + @MockitoBean private PaypalAccessTokenService tokenService; @Autowired diff --git a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/clients/TwitchTokenAuthClient.java b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/clients/TwitchTokenAuthClient.java index d02e72ed..99753213 100644 --- a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/clients/TwitchTokenAuthClient.java +++ b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/clients/TwitchTokenAuthClient.java @@ -22,4 +22,5 @@ public interface TwitchTokenAuthClient { @PostMapping("token") @Headers("Content-Type: application/x-www-form-urlencoded") TwitchTokenResponseDTO getToken(Map formParams); + } diff --git a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/clients/TwitchValidTokenClient.java b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/clients/TwitchValidTokenClient.java index a9b1d383..1b481e87 100644 --- a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/clients/TwitchValidTokenClient.java +++ b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/clients/TwitchValidTokenClient.java @@ -37,9 +37,9 @@ public TwitchValidationTokenResponseDTO makeHttpRequestValidation(final String a httpRequest.setHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE); httpRequest.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken); - try { + try (final HttpClient httpClient = HttpClient.newHttpClient()) { final HttpRequest request = httpRequest.build(); - final HttpResponse response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); + final HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); final int status = response.statusCode(); if (Integer.toString(status).startsWith("2")) { diff --git a/modules/twitch/auth/service/src/test/java/com/funixproductions/api/twitch/auth/service/resources/TwitchAuthResourceTest.java b/modules/twitch/auth/service/src/test/java/com/funixproductions/api/twitch/auth/service/resources/TwitchAuthResourceTest.java index 984e61f5..b1300a4e 100644 --- a/modules/twitch/auth/service/src/test/java/com/funixproductions/api/twitch/auth/service/resources/TwitchAuthResourceTest.java +++ b/modules/twitch/auth/service/src/test/java/com/funixproductions/api/twitch/auth/service/resources/TwitchAuthResourceTest.java @@ -18,8 +18,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.HttpHeaders; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; @@ -36,16 +36,16 @@ @RunWith(MockitoJUnitRunner.class) class TwitchAuthResourceTest { - @MockBean + @MockitoBean private UserAuthClient userAuthClient; - @MockBean + @MockitoBean private TwitchTokenAuthClient twitchTokenAuthClient; - @MockBean + @MockitoBean private TwitchValidTokenClient twitchValidTokenClient; - @MockBean + @MockitoBean private EncryptionClient encryptionClient; @Autowired diff --git a/modules/twitch/auth/service/src/test/java/com/funixproductions/api/twitch/auth/service/services/TestTwitchServerTokenService.java b/modules/twitch/auth/service/src/test/java/com/funixproductions/api/twitch/auth/service/services/TestTwitchServerTokenService.java index b738442a..52b0dbc0 100644 --- a/modules/twitch/auth/service/src/test/java/com/funixproductions/api/twitch/auth/service/services/TestTwitchServerTokenService.java +++ b/modules/twitch/auth/service/src/test/java/com/funixproductions/api/twitch/auth/service/services/TestTwitchServerTokenService.java @@ -9,7 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -18,7 +18,7 @@ @RunWith(MockitoJUnitRunner.class) class TestTwitchServerTokenService { - @MockBean + @MockitoBean private TwitchTokenAuthClient twitchTokenAuthClient; @Autowired diff --git a/modules/twitch/eventsub/client/src/main/java/com/funixproductions/api/twitch/eventsub/client/dtos/websocket/TwitchEventSubWebsocketMessage.java b/modules/twitch/eventsub/client/src/main/java/com/funixproductions/api/twitch/eventsub/client/dtos/websocket/TwitchEventSubWebsocketMessage.java index 089103ce..98087387 100644 --- a/modules/twitch/eventsub/client/src/main/java/com/funixproductions/api/twitch/eventsub/client/dtos/websocket/TwitchEventSubWebsocketMessage.java +++ b/modules/twitch/eventsub/client/src/main/java/com/funixproductions/api/twitch/eventsub/client/dtos/websocket/TwitchEventSubWebsocketMessage.java @@ -1,10 +1,14 @@ package com.funixproductions.api.twitch.eventsub.client.dtos.websocket; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; @Getter @Setter +@AllArgsConstructor +@NoArgsConstructor public class TwitchEventSubWebsocketMessage { private String streamerId; diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/security/WebSecurity.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/security/WebSecurity.java index 9a6f5dde..97deee08 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/security/WebSecurity.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/security/WebSecurity.java @@ -17,6 +17,7 @@ public class WebSecurity extends ApiWebSecurity { @Override public Customizer.AuthorizationManagerRequestMatcherRegistry> getUrlsMatchers() { return ex -> ex + .requestMatchers("/ws/public/**").permitAll() .requestMatchers("/twitch/eventsub/cb**").permitAll() .requestMatchers("/actuator/**").permitAll() .anyRequest().hasAuthority(UserRole.ADMIN.getRole()); diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubCallbackService.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubCallbackService.java index 86e6fb93..fe5cdb45 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubCallbackService.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubCallbackService.java @@ -1,21 +1,20 @@ package com.funixproductions.api.twitch.eventsub.service.services; -import com.funixproductions.api.twitch.eventsub.service.services.handler.TwitchEventSubHandlerService; +import com.funixproductions.api.twitch.eventsub.service.services.websocket.TwitchEventSubWebsocketService; import com.funixproductions.core.exceptions.ApiBadRequestException; +import com.google.common.base.Strings; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; -import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.TimeUnit; /** @@ -35,9 +34,11 @@ public class TwitchEventSubCallbackService { private static final String BODY_JSON_NOT_VALID = "Le body ne vient pas de twitch (malformé)."; private final TwitchEventSubHmacService hmacService; - private final TwitchEventSubHandlerService twitchEventSubHandlerService; + private final TwitchEventSubWebsocketService websocketService; - private final Map messagesIdsReceived = new HashMap<>(); + private final Cache messagesIdsTreated = CacheBuilder.newBuilder() + .expireAfterWrite(30, TimeUnit.MINUTES) + .build(); /** * Method called every time Twitch sends a call @@ -55,24 +56,31 @@ public String handleNewWebhook(final HttpServletRequest httpServletRequest, fina throw new ApiBadRequestException("Il manque le message type"); } - if (this.messagesIdsReceived.containsKey(messageId)) { - throw new ApiBadRequestException("Cette notification à déjà été traitée."); + if (Boolean.TRUE.equals(this.messagesIdsTreated.getIfPresent(messageId))) { + throw new ApiBadRequestException("Cette notification twitch a déjà été traitée."); } else { - hmacService.validEventMessage(httpServletRequest, body); - this.messagesIdsReceived.put(messageId, Instant.now()); + this.hmacService.validEventMessage(httpServletRequest, body); + this.messagesIdsTreated.put(messageId, true); final String bodyParsed = new String(body, StandardCharsets.UTF_8); switch (messageType) { case MESSAGE_TYPE_NOTIFICATION -> { - final JsonObject message = getTwitchMessage(bodyParsed); + final JsonObject message = this.getTwitchMessage(bodyParsed); final JsonElement eventElement = message.get("event"); if (eventElement.isJsonObject()) { - final String notificationType = getNotificationType(message); + final String notificationType = this.getNotificationType(message); final JsonObject event = eventElement.getAsJsonObject(); - - twitchEventSubHandlerService.receiveNewNotification(notificationType, event); + final String streamerId = this.getStreamerIdInNotification(event); + + if (!Strings.isNullOrEmpty(streamerId)) { + this.websocketService.newNotification( + notificationType, + streamerId, + event.toString() + ); + } } else { throw new ApiBadRequestException(BODY_JSON_NOT_VALID); } @@ -80,8 +88,9 @@ public String handleNewWebhook(final HttpServletRequest httpServletRequest, fina return "s"; } case MESSAGE_TYPE_VERIFICATION -> { - final JsonObject message = getTwitchMessage(bodyParsed); - return getChallenge(message); + return this.getChallenge( + this.getTwitchMessage(bodyParsed) + ); } case MESSAGE_TYPE_REVOCATION -> { return "s"; @@ -91,33 +100,6 @@ public String handleNewWebhook(final HttpServletRequest httpServletRequest, fina } } - /** - * Method used to clean the messages ids received - */ - @Scheduled(fixedRate = 10, timeUnit = TimeUnit.MINUTES) - public void cleanMessagesIds() { - final Instant now = Instant.now(); - final Map copyMap = new HashMap<>(this.messagesIdsReceived); - - for (final Map.Entry entry : copyMap.entrySet()) { - final Instant receivedAt = entry.getValue(); - - if (now.minus(30, ChronoUnit.MINUTES).isAfter(receivedAt)) { - this.messagesIdsReceived.remove(entry.getKey()); - } - } - } - - private JsonObject getTwitchMessage(final String object) { - final JsonElement element = JsonParser.parseString(object); - - if (element.isJsonObject()) { - return element.getAsJsonObject(); - } else { - throw new ApiBadRequestException(BODY_JSON_NOT_VALID); - } - } - private String getNotificationType(final JsonObject message) { final JsonElement subElement = message.get("subscription"); @@ -147,4 +129,25 @@ private String getChallenge(final JsonObject message) { } } + private JsonObject getTwitchMessage(final String object) { + final JsonElement element = JsonParser.parseString(object); + + if (element.isJsonObject()) { + return element.getAsJsonObject(); + } else { + throw new ApiBadRequestException(BODY_JSON_NOT_VALID); + } + } + + @Nullable + private String getStreamerIdInNotification(final JsonObject jsonObject) { + final JsonElement idJson = jsonObject.get("broadcaster_user_id"); + + if (idJson != null && idJson.isJsonPrimitive()) { + return idJson.getAsJsonPrimitive().getAsString(); + } else { + return null; + } + } + } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubHmacService.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubHmacService.java index 94e72ab4..f3418b2f 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubHmacService.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubHmacService.java @@ -5,6 +5,7 @@ import com.funixproductions.core.exceptions.ApiException; import com.funixproductions.core.tools.string.PasswordGenerator; import jakarta.servlet.http.HttpServletRequest; +import lombok.Getter; import org.apache.logging.log4j.util.Strings; import org.springframework.stereotype.Service; @@ -30,7 +31,12 @@ public class TwitchEventSubHmacService { public static final String TWITCH_MESSAGE_TIMESTAMP = "Twitch-Eventsub-Message-Timestamp"; public static final String TWITCH_MESSAGE_SIGNATURE = "Twitch-Eventsub-Message-Signature"; + /** + * key used to encode messages, twitch need this + */ + @Getter private final String key; + private final Mac mac; public TwitchEventSubHmacService(TwitchEventSubConfig twitchEventSubConfig) { @@ -77,18 +83,10 @@ public void validEventMessage(final HttpServletRequest request, final byte[] bod final byte[] expectedHmac = messageSignature.substring(HMAC_PREFIX.length()).getBytes(StandardCharsets.UTF_8); if (!MessageDigest.isEqual(twitchHmac, expectedHmac)) { - throw new ApiBadRequestException("La clé fournie en header ne correspond pas avec la clé de la funix api."); + throw new ApiBadRequestException("La clé fournie en header ne correspond pas avec la clé de la FunixProductions Api."); } } - /** - * Method to get the key used to encode messages, twitch need this - * @return String key - */ - public String getKey() { - return key; - } - private byte[] getHmacFromTwitch(final HttpServletRequest request, final byte[] body) { final byte[] messageId = request.getHeader(TWITCH_MESSAGE_ID).getBytes(StandardCharsets.UTF_8); final byte[] messageTs = request.getHeader(TWITCH_MESSAGE_TIMESTAMP).getBytes(StandardCharsets.UTF_8); diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceService.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceService.java index c58244e6..521b1596 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceService.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceService.java @@ -50,7 +50,8 @@ public void createSubscription(@NonNull final TwitchSubscription request) { this.eventSubReferenceClient.createSubscription( super.addBearerPrefix(tokenService.fetchServerToken()), MediaType.APPLICATION_JSON_VALUE, - request.getPayload()); + request.getPayload() + ); } catch (FeignException e) { throw super.handleFeignException(e); } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/handler/TwitchEventSubHandlerService.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/handler/TwitchEventSubHandlerService.java deleted file mode 100644 index 9c7d08de..00000000 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/handler/TwitchEventSubHandlerService.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.funixproductions.api.twitch.eventsub.service.services.handler; - -import com.funixproductions.api.twitch.eventsub.client.dtos.events.channel.TwitchEventChannelFollowDTO; -import com.funixproductions.api.twitch.eventsub.client.dtos.events.channel.TwitchEventChannelUpdateDTO; -import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; -import com.funixproductions.api.twitch.eventsub.service.services.websocket.TwitchEventSubWebsocketService; -import com.google.common.base.Strings; -import com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import lombok.RequiredArgsConstructor; -import org.springframework.lang.Nullable; -import org.springframework.stereotype.Service; - -/** - * Service used to handle new notifications from callback service like a new follower or sub - */ -@Service -@RequiredArgsConstructor -public class TwitchEventSubHandlerService { - - private final TwitchEventSubWebsocketService websocketService; - private final Gson gson = new Gson(); - - public void receiveNewNotification(final String notificationType, final JsonObject event) { - final String streamerId = getStreamerIdInNotification(event); - if (streamerId == null) { - return; - } - - websocketService.newNotification(notificationType, streamerId, event.toString()); - - if (notificationType.startsWith("channel") && !Strings.isNullOrEmpty(streamerId)) { - handleChannelNotification(notificationType, streamerId, event); - } - } - - private void handleChannelNotification(final String notificationType, final String streamerId, final JsonObject event) { - if (notificationType.equals(ChannelEventType.FOLLOW.getType())) { - final TwitchEventChannelFollowDTO followDTO = gson.fromJson(event, TwitchEventChannelFollowDTO.class); - - } else if (notificationType.equals(ChannelEventType.UPDATE.getType())) { - final TwitchEventChannelUpdateDTO updateDTO = gson.fromJson(event, TwitchEventChannelUpdateDTO.class); - } - } - - @Nullable - private String getStreamerIdInNotification(final JsonObject jsonObject) { - final JsonElement idJson = jsonObject.get("broadcaster_user_id"); - - if (idJson != null && idJson.isJsonPrimitive()) { - final JsonPrimitive id = idJson.getAsJsonPrimitive(); - return id.getAsString(); - } else { - return null; - } - } - -} diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/websocket/TwitchEventSubWebsocketService.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/websocket/TwitchEventSubWebsocketService.java index a04e52bf..917ec940 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/websocket/TwitchEventSubWebsocketService.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/websocket/TwitchEventSubWebsocketService.java @@ -1,9 +1,10 @@ package com.funixproductions.api.twitch.eventsub.service.services.websocket; import com.funixproductions.api.twitch.auth.client.clients.TwitchInternalAuthClient; -import com.funixproductions.api.twitch.auth.client.dtos.TwitchClientTokenDTO; import com.funixproductions.api.twitch.eventsub.client.dtos.websocket.TwitchEventSubWebsocketMessage; import com.funixproductions.core.tools.websocket.services.ApiWebsocketServerHandler; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import com.google.gson.Gson; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -11,8 +12,12 @@ import org.springframework.stereotype.Service; import org.springframework.web.socket.WebSocketSession; +import javax.annotation.Nullable; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; /** * Service who handles websocket data sending @@ -22,22 +27,27 @@ @RequiredArgsConstructor public class TwitchEventSubWebsocketService extends ApiWebsocketServerHandler { public static final String LISTEN_CALL_CLIENT = "listen"; + private final TwitchInternalAuthClient twitchInternalAuthClient; - private final Map sessionsMapsStreamersEvents = new HashMap<>(); + + private final Cache streamerIdCache = CacheBuilder.newBuilder() + .expireAfterWrite(30, TimeUnit.MINUTES) + .build(); + + private final Map> sessionsMapsStreamersEvents = new HashMap<>(); private final Gson gson = new Gson(); public void newNotification(final String notificationType, final String streamerId, final String data) { - final TwitchEventSubWebsocketMessage eventSubWebsocketMessage = new TwitchEventSubWebsocketMessage(); - eventSubWebsocketMessage.setStreamerId(streamerId); - eventSubWebsocketMessage.setNotificationType(notificationType); - eventSubWebsocketMessage.setEvent(data); - final String message = gson.toJson(eventSubWebsocketMessage); + final String message = gson.toJson(new TwitchEventSubWebsocketMessage( + streamerId, + notificationType, + data + )); + String sessionId; - for (final Map.Entry entry : this.sessionsMapsStreamersEvents.entrySet()) { - final String streamerIdEntry = entry.getValue(); - - if (streamerIdEntry.equals(streamerId)) { - final String sessionId = entry.getKey(); + for (final Map.Entry> entry : this.sessionsMapsStreamersEvents.entrySet()) { + if (entry.getValue().contains(streamerId)) { + sessionId = entry.getKey(); try { super.sendMessageToSessionId(sessionId, message); @@ -50,15 +60,16 @@ public void newNotification(final String notificationType, final String streamer @Override protected void newWebsocketMessage(@NonNull WebSocketSession session, @NonNull String message) { - if (message.startsWith(LISTEN_CALL_CLIENT)) { - final String[] data = message.split(":"); + final String[] data = message.split(":"); - if (data.length == 2) { - final String streamerUsername = data[1]; - final TwitchClientTokenDTO token = twitchInternalAuthClient.fetchTokenByStreamerName(streamerUsername); + if (data.length == 2 && data[0].equals(LISTEN_CALL_CLIENT)) { + final String streamerId = this.getStreamerIdByUsername(data[1]); + if (streamerId == null) return; - this.sessionsMapsStreamersEvents.put(session.getId(), token.getTwitchUserId()); - } + final List streamersEvents = this.sessionsMapsStreamersEvents.getOrDefault(session.getId(), new ArrayList<>()); + + streamersEvents.add(streamerId); + this.sessionsMapsStreamersEvents.put(session.getId(), streamersEvents); } } @@ -67,4 +78,23 @@ protected void onClientDisconnect(String sessionId) { this.sessionsMapsStreamersEvents.remove(sessionId); } + @Nullable + private String getStreamerIdByUsername(final String username) { + String streamerId = this.streamerIdCache.getIfPresent(username); + + if (streamerId != null) { + return streamerId; + } else { + try { + streamerId = twitchInternalAuthClient.fetchTokenByStreamerName(username).getTwitchUserId(); + this.streamerIdCache.put(username, streamerId); + + return streamerId; + } catch (Exception e) { + log.error("Impossible de récupérer le streamer id par le username.", e); + return null; + } + } + } + } diff --git a/modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubCallbackServiceTest.java b/modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubCallbackServiceTest.java index 50a4927c..951a9497 100644 --- a/modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubCallbackServiceTest.java +++ b/modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubCallbackServiceTest.java @@ -1,7 +1,6 @@ package com.funixproductions.api.twitch.eventsub.service.services; import com.funixproductions.api.twitch.eventsub.client.dtos.events.channel.TwitchEventChannelFollowDTO; -import com.funixproductions.api.twitch.eventsub.service.services.handler.TwitchEventSubHandlerService; import com.funixproductions.core.exceptions.ApiBadRequestException; import com.funixproductions.core.exceptions.ApiException; import com.google.gson.Gson; @@ -34,15 +33,11 @@ class TwitchEventSubCallbackServiceTest { @Mock private TwitchEventSubHmacService hmacService; - @Mock - private TwitchEventSubHandlerService handlerService; - private final Gson gson = new Gson(); @Test void testNewWebhookNotification() throws RuntimeException { doNothing().when(hmacService).validEventMessage(any(), any()); - doNothing().when(handlerService).receiveNewNotification(any(), any()); final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); httpServletRequest.addHeader(TwitchEventSubCallbackService.TWITCH_MESSAGE_ID, "10"); @@ -56,8 +51,6 @@ void testNewWebhookNotification() throws RuntimeException { assertThrows(ApiBadRequestException.class, () -> service.handleNewWebhook(httpServletRequest, gson.toJson(test).getBytes(StandardCharsets.UTF_8)) ); - - service.cleanMessagesIds(); } @Test @@ -89,7 +82,6 @@ void testNewWebhookRevocation() throws ApiException { void missingMessageIdOrMessageType() { assertThrows(ApiBadRequestException.class, () -> { doNothing().when(hmacService).validEventMessage(any(), any()); - doNothing().when(handlerService).receiveNewNotification(any(), any()); final MockHttpServletRequest request = new MockHttpServletRequest(); service.handleNewWebhook(request, "".getBytes()); @@ -104,7 +96,6 @@ void missingMessageIdOrMessageType() { void testMalformatedBodyNotification() { assertThrows(ApiBadRequestException.class, () -> { doNothing().when(hmacService).validEventMessage(any(), any()); - doNothing().when(handlerService).receiveNewNotification(any(), any()); final MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader(TwitchEventSubCallbackService.TWITCH_MESSAGE_ID, UUID.randomUUID().toString()); request.addHeader(TwitchEventSubCallbackService.TWITCH_MESSAGE_TYPE, TwitchEventSubCallbackService.MESSAGE_TYPE_NOTIFICATION); @@ -117,7 +108,6 @@ void testMalformatedBodyNotification() { void testMalformatedBodyVerification() { assertThrows(ApiBadRequestException.class, () -> { doNothing().when(hmacService).validEventMessage(any(), any()); - doNothing().when(handlerService).receiveNewNotification(any(), any()); final MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader(TwitchEventSubCallbackService.TWITCH_MESSAGE_ID, UUID.randomUUID().toString()); request.addHeader(TwitchEventSubCallbackService.TWITCH_MESSAGE_TYPE, TwitchEventSubCallbackService.MESSAGE_TYPE_VERIFICATION); diff --git a/modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceServiceTest.java b/modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceServiceTest.java index 3523911e..b38c801d 100644 --- a/modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceServiceTest.java +++ b/modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceServiceTest.java @@ -12,7 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; @@ -28,13 +28,13 @@ class TwitchEventSubReferenceServiceTest { @Autowired private TwitchEventSubReferenceService service; - @MockBean + @MockitoBean private TwitchEventSubHmacService hmacService; - @MockBean + @MockitoBean private TwitchInternalAuthClient tokenService; - @MockBean + @MockitoBean private TwitchEventSubReferenceClient referenceClient; @BeforeEach diff --git a/modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationServiceTest.java b/modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationServiceTest.java index 54fedd07..e5de779f 100644 --- a/modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationServiceTest.java +++ b/modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationServiceTest.java @@ -17,7 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import java.util.*; @@ -33,16 +33,16 @@ class TwitchEventSubRegistrationServiceTest { @Autowired private TwitchEventSubRegistrationService service; - @MockBean + @MockitoBean private TwitchUsersClient twitchReferenceUsersService; - @MockBean + @MockitoBean private TwitchEventSubReferenceService twitchEventSubReferenceService; - @MockBean + @MockitoBean private TwitchInternalAuthClient twitchServerTokenService; - @MockBean + @MockitoBean private TwitchEventSubStreamerRepository repository; private final int eventCount = ChannelEventType.values().length; diff --git a/modules/twitch/reference/service/src/test/java/com/funixproductions/api/twitch/reference/service/services/TwitchReferenceChannelPointsServiceTest.java b/modules/twitch/reference/service/src/test/java/com/funixproductions/api/twitch/reference/service/services/TwitchReferenceChannelPointsServiceTest.java index e6b70015..f285d41a 100644 --- a/modules/twitch/reference/service/src/test/java/com/funixproductions/api/twitch/reference/service/services/TwitchReferenceChannelPointsServiceTest.java +++ b/modules/twitch/reference/service/src/test/java/com/funixproductions/api/twitch/reference/service/services/TwitchReferenceChannelPointsServiceTest.java @@ -16,7 +16,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import java.time.Instant; import java.util.Date; @@ -33,13 +33,13 @@ @RunWith(MockitoJUnitRunner.class) class TwitchReferenceChannelPointsServiceTest { - @MockBean + @MockitoBean private TwitchReferenceChannelPointsClient client; @Autowired private TwitchReferenceChannelPointsService service; - @MockBean + @MockitoBean private TwitchInternalAuthClient internalAuthClient; @BeforeEach diff --git a/modules/twitch/reference/service/src/test/java/com/funixproductions/api/twitch/reference/service/services/TwitchReferenceChannelServiceTest.java b/modules/twitch/reference/service/src/test/java/com/funixproductions/api/twitch/reference/service/services/TwitchReferenceChannelServiceTest.java index ce1ca5bc..4ef99dff 100644 --- a/modules/twitch/reference/service/src/test/java/com/funixproductions/api/twitch/reference/service/services/TwitchReferenceChannelServiceTest.java +++ b/modules/twitch/reference/service/src/test/java/com/funixproductions/api/twitch/reference/service/services/TwitchReferenceChannelServiceTest.java @@ -8,7 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import java.util.ArrayList; @@ -22,7 +22,7 @@ @AutoConfigureMockMvc class TwitchReferenceChannelServiceTest { - @MockBean + @MockitoBean private TwitchReferenceChannelClient client; @Autowired diff --git a/modules/user/service/src/test/java/com/funixproductions/api/user/service/components/UserTestComponent.java b/modules/user/service/src/test/java/com/funixproductions/api/user/service/components/UserTestComponent.java index 756d7efb..b02b578a 100644 --- a/modules/user/service/src/test/java/com/funixproductions/api/user/service/components/UserTestComponent.java +++ b/modules/user/service/src/test/java/com/funixproductions/api/user/service/components/UserTestComponent.java @@ -12,9 +12,9 @@ import com.funixproductions.core.test.beans.JsonHelper; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -41,16 +41,16 @@ public abstract class UserTestComponent { @Autowired private PasswordEncoder passwordEncoder; - @MockBean + @MockitoBean protected EncryptionClient encryptionClient; - @MockBean + @MockitoBean protected GoogleRecaptchaInternalClient googleCaptchaService; - @MockBean + @MockitoBean protected InternalGoogleAuthClient googleAuthClient; - @MockBean + @MockitoBean protected GoogleGmailClient googleGmailClient; public static final String PASSWORD = "ousddffdi22AA" + UUID.randomUUID(); diff --git a/modules/user/service/src/test/java/com/funixproductions/api/user/service/ressources/TestUserUpdateSelfAccountResourceTest.java b/modules/user/service/src/test/java/com/funixproductions/api/user/service/ressources/TestUserUpdateSelfAccountResourceTest.java index 151ce068..74c67332 100644 --- a/modules/user/service/src/test/java/com/funixproductions/api/user/service/ressources/TestUserUpdateSelfAccountResourceTest.java +++ b/modules/user/service/src/test/java/com/funixproductions/api/user/service/ressources/TestUserUpdateSelfAccountResourceTest.java @@ -13,9 +13,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; @@ -39,7 +39,7 @@ class TestUserUpdateSelfAccountResourceTest extends UserTestComponent { @Autowired private JsonHelper jsonHelper; - @MockBean + @MockitoBean private UserValidationAccountService userValidationAccountService; @BeforeEach From 7df5bdee43cee6b19b187040072a06ed0af468b3 Mon Sep 17 00:00:00 2001 From: Antoine PRONNIER Date: Thu, 19 Dec 2024 14:12:18 +0100 Subject: [PATCH 2/6] WIP: Refacto Twitch module --- .../api/core/enums/FrontOrigins.java | 7 +- .../auth/client/clients/TwitchAuthClient.java | 12 ++- .../clients/TwitchInternalAuthClient.java | 16 +++- .../client/dtos/TwitchClientTokenDTO.java | 4 + .../client/enums/TwitchClientTokenType.java | 21 ++++- .../configurations/TwitchAuthConfig.java | 5 ++ .../service/entities/TwitchClientToken.java | 4 + .../TwitchClientTokenRepository.java | 7 +- .../service/resources/TwitchAuthResource.java | 59 ++++++++----- .../resources/TwitchInternalAuthResource.java | 23 +++-- .../services/TwitchClientTokenService.java | 84 ++++++++++++++----- .../main/resources/application-dev.properties | 1 + .../service/enums/ChannelEventType.java | 2 +- ...ription.java => AChannelSubscription.java} | 4 +- .../channel/ChannelBanSubscription.java | 2 +- .../channel/ChannelCheerSubscription.java | 5 +- .../channel/ChannelFollowSubscription.java | 2 +- .../ChannelHypeTrainBeginSubscription.java | 5 +- .../ChannelHypeTrainEndSubscription.java | 5 +- .../ChannelHypeTrainProgressSubscription.java | 5 +- .../channel/ChannelPollBeginSubscription.java | 5 +- .../channel/ChannelPollEndSubscription.java | 5 +- .../ChannelPollProgressSubscription.java | 5 +- .../ChannelPredictionBeginSubscription.java | 5 +- .../ChannelPredictionEndSubscription.java | 5 +- ...ChannelPredictionProgressSubscription.java | 5 +- .../ChannelRaidReceivedSubscription.java | 5 +- .../ChannelResubMessageSubscription.java | 2 +- .../channel/ChannelRewardGetSubscription.java | 5 +- .../ChannelShoutOutCreateSubscription.java | 5 +- .../ChannelShoutOutReceiveSubscription.java | 5 +- .../channel/ChannelSubGiftSubscription.java | 2 +- .../channel/ChannelSubSubscription.java | 5 +- .../channel/ChannelUpdateSubscription.java | 2 +- .../TwitchEventSubRegistrationService.java | 18 ++-- .../TwitchEventSubWebsocketService.java | 12 ++- .../TwitchEventSubReferenceServiceTest.java | 2 +- .../channel/TwitchChannelPointsResource.java | 3 +- .../channel/TwitchChannelResource.java | 7 +- .../resources/chat/TwitchChatResource.java | 5 +- 40 files changed, 280 insertions(+), 101 deletions(-) rename modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/{ChannelSubscription.java => AChannelSubscription.java} (79%) diff --git a/modules/core/src/main/java/com/funixproductions/api/core/enums/FrontOrigins.java b/modules/core/src/main/java/com/funixproductions/api/core/enums/FrontOrigins.java index e1152702..07f85f86 100644 --- a/modules/core/src/main/java/com/funixproductions/api/core/enums/FrontOrigins.java +++ b/modules/core/src/main/java/com/funixproductions/api/core/enums/FrontOrigins.java @@ -4,6 +4,8 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; +import javax.annotation.Nullable; + @Getter @RequiredArgsConstructor public enum FrontOrigins { @@ -11,7 +13,7 @@ public enum FrontOrigins { "funixproductions-dashboard", "Dashboard Funix Productions", "https://dashboard.funixproductions.com", - "http://localhost:4200" + "https://dev.dashboard.funixproductions.com" ), PACIFISTA_PUBLIC_WEB( "pacifista-public-web", @@ -25,7 +27,8 @@ public enum FrontOrigins { private final String domainProd; private final String domainDev; - public static FrontOrigins getRedirectAuthOrigin(String origin) { + @Nullable + public static FrontOrigins getRedirectAuthOrigin(@Nullable String origin) { if (Strings.isNullOrEmpty(origin)) { return null; } diff --git a/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/clients/TwitchAuthClient.java b/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/clients/TwitchAuthClient.java index ffec1a40..a0236240 100644 --- a/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/clients/TwitchAuthClient.java +++ b/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/clients/TwitchAuthClient.java @@ -1,10 +1,13 @@ package com.funixproductions.api.twitch.auth.client.clients; import com.funixproductions.api.twitch.auth.client.dtos.TwitchClientTokenDTO; +import lombok.NonNull; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; +import javax.annotation.Nullable; + @FeignClient( name = "TwitchAuthClient", url = "${funixproductions.api.twitch.auth.app-domain-url}", @@ -13,9 +16,14 @@ public interface TwitchAuthClient { @GetMapping("clientAuthUrl") - String getAuthClientUrl(@RequestParam(defaultValue = "VIEWER") String tokenType); + String getAuthClientUrl( + @RequestParam(defaultValue = "VIEWER") @NonNull String tokenType, + @RequestParam(required = false) @Nullable String origin + ); @GetMapping("accessToken") - TwitchClientTokenDTO getAccessToken(); + TwitchClientTokenDTO getAccessToken( + @RequestParam(defaultValue = "VIEWER") @NonNull String tokenType + ); } diff --git a/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/clients/TwitchInternalAuthClient.java b/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/clients/TwitchInternalAuthClient.java index 3e4ffdc6..980a4a43 100644 --- a/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/clients/TwitchInternalAuthClient.java +++ b/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/clients/TwitchInternalAuthClient.java @@ -1,6 +1,7 @@ package com.funixproductions.api.twitch.auth.client.clients; import com.funixproductions.api.twitch.auth.client.dtos.TwitchClientTokenDTO; +import lombok.NonNull; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -13,13 +14,22 @@ public interface TwitchInternalAuthClient { @GetMapping("client") - TwitchClientTokenDTO fetchToken(@RequestParam String userUUID); + TwitchClientTokenDTO fetchToken( + @RequestParam String userUUID, + @RequestParam(defaultValue = "VIEWER") @NonNull String tokenType + ); @GetMapping("clientByStreamerName") - TwitchClientTokenDTO fetchTokenByStreamerName(@RequestParam String streamerName); + TwitchClientTokenDTO fetchTokenByStreamerName( + @RequestParam String streamerName, + @RequestParam(defaultValue = "VIEWER") @NonNull String tokenType + ); @GetMapping("clientByStreamerId") - TwitchClientTokenDTO fetchTokenByStreamerId(@RequestParam String streamerId); + TwitchClientTokenDTO fetchTokenByStreamerId( + @RequestParam String streamerId, + @RequestParam(defaultValue = "VIEWER") @NonNull String tokenType + ); @GetMapping("server") String fetchServerToken(); diff --git a/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/dtos/TwitchClientTokenDTO.java b/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/dtos/TwitchClientTokenDTO.java index bda534b5..1638aaf5 100644 --- a/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/dtos/TwitchClientTokenDTO.java +++ b/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/dtos/TwitchClientTokenDTO.java @@ -1,5 +1,6 @@ package com.funixproductions.api.twitch.auth.client.dtos; +import com.funixproductions.api.twitch.auth.client.enums.TwitchClientTokenType; import com.funixproductions.core.crud.dtos.ApiDTO; import lombok.Getter; import lombok.Setter; @@ -12,6 +13,7 @@ @Getter @Setter public class TwitchClientTokenDTO extends ApiDTO { + private UUID userUuid; private String twitchUserId; @@ -24,6 +26,8 @@ public class TwitchClientTokenDTO extends ApiDTO { private Date expirationDateToken; + private TwitchClientTokenType tokenType; + public String getUserUuid() { if (this.userUuid == null) { return null; diff --git a/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/enums/TwitchClientTokenType.java b/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/enums/TwitchClientTokenType.java index 0c057ef1..ebd03bac 100644 --- a/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/enums/TwitchClientTokenType.java +++ b/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/enums/TwitchClientTokenType.java @@ -1,8 +1,27 @@ package com.funixproductions.api.twitch.auth.client.enums; +import com.google.common.base.Strings; + +import javax.annotation.Nullable; + public enum TwitchClientTokenType { + FUNIXGAMING, STREAMER, MODERATOR, BOT, - VIEWER + VIEWER; + + public static TwitchClientTokenType getTokenTypeByString(@Nullable final String tokenType) { + if (Strings.isNullOrEmpty(tokenType)) { + return TwitchClientTokenType.VIEWER; + } + + for (final TwitchClientTokenType token : TwitchClientTokenType.values()) { + if (token.name().equalsIgnoreCase(tokenType)) { + return token; + } + } + + return TwitchClientTokenType.VIEWER; + } } diff --git a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/configurations/TwitchAuthConfig.java b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/configurations/TwitchAuthConfig.java index 4c88f87e..f6da2df8 100644 --- a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/configurations/TwitchAuthConfig.java +++ b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/configurations/TwitchAuthConfig.java @@ -16,4 +16,9 @@ public class TwitchAuthConfig { */ private String appCallbackDomain = "https://api.funixproductions.com"; + /** + * To know if the API is in dev mode or not + */ + private Boolean devMode = false; + } diff --git a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/entities/TwitchClientToken.java b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/entities/TwitchClientToken.java index 0074d066..ab5cc868 100644 --- a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/entities/TwitchClientToken.java +++ b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/entities/TwitchClientToken.java @@ -1,6 +1,7 @@ package com.funixproductions.api.twitch.auth.service.entities; import com.funixproductions.api.encryption.client.utils.EncryptionString; +import com.funixproductions.api.twitch.auth.client.enums.TwitchClientTokenType; import com.funixproductions.core.crud.entities.ApiEntity; import jakarta.persistence.Column; import jakarta.persistence.Convert; @@ -43,6 +44,9 @@ public class TwitchClientToken extends ApiEntity { @Column(name = "expiration_date_token", nullable = false) private Date expirationDateToken; + @Column(name = "token_type", nullable = false) + private TwitchClientTokenType tokenType; + public boolean isUsable() { return Instant.now().isBefore(expirationDateToken.toInstant()); } diff --git a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/repositories/TwitchClientTokenRepository.java b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/repositories/TwitchClientTokenRepository.java index 8713e416..56587253 100644 --- a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/repositories/TwitchClientTokenRepository.java +++ b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/repositories/TwitchClientTokenRepository.java @@ -1,5 +1,6 @@ package com.funixproductions.api.twitch.auth.service.repositories; +import com.funixproductions.api.twitch.auth.client.enums.TwitchClientTokenType; import com.funixproductions.api.twitch.auth.service.entities.TwitchClientToken; import com.funixproductions.core.crud.repositories.ApiRepository; import org.springframework.stereotype.Repository; @@ -8,7 +9,7 @@ @Repository public interface TwitchClientTokenRepository extends ApiRepository { - Optional findTwitchClientTokenByUserUuid(String userUuid); - Optional findTwitchClientTokenByTwitchUsername(String username); - Optional findTwitchClientTokenByTwitchUserId(String streamerId); + Optional findTwitchClientTokenByUserUuidAndTokenType(String userUuid, TwitchClientTokenType tokenType); + Optional findTwitchClientTokenByTwitchUsernameAndTokenType(String username, TwitchClientTokenType tokenType); + Optional findTwitchClientTokenByTwitchUserIdAndTokenType(String streamerId, TwitchClientTokenType tokenType); } diff --git a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/resources/TwitchAuthResource.java b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/resources/TwitchAuthResource.java index 5fb135d7..797b1eb5 100644 --- a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/resources/TwitchAuthResource.java +++ b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/resources/TwitchAuthResource.java @@ -1,63 +1,84 @@ package com.funixproductions.api.twitch.auth.service.resources; +import com.funixproductions.api.core.enums.FrontOrigins; import com.funixproductions.api.twitch.auth.client.clients.TwitchAuthClient; import com.funixproductions.api.twitch.auth.client.dtos.TwitchClientTokenDTO; import com.funixproductions.api.twitch.auth.client.enums.TwitchClientTokenType; +import com.funixproductions.api.twitch.auth.service.configurations.TwitchAuthConfig; import com.funixproductions.api.twitch.auth.service.services.TwitchClientTokenService; import com.funixproductions.api.user.client.dtos.UserDTO; import com.funixproductions.api.user.client.security.CurrentSession; import com.funixproductions.core.exceptions.ApiBadRequestException; +import com.funixproductions.core.exceptions.ApiException; +import com.funixproductions.core.exceptions.ApiUnauthorizedException; import com.google.common.base.Strings; +import jakarta.servlet.http.HttpServletResponse; +import lombok.NonNull; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import javax.annotation.Nullable; + @RestController @RequestMapping("/twitch/auth") @RequiredArgsConstructor public class TwitchAuthResource implements TwitchAuthClient { private final TwitchClientTokenService twitchClientTokenService; + private final TwitchAuthConfig twitchAuthConfig; private final CurrentSession currentSession; @Override - public String getAuthClientUrl(String tokenType) { - return this.twitchClientTokenService.getAuthClientUrl(getTokenTypeByString(tokenType)); + public String getAuthClientUrl(@NonNull String tokenType, @Nullable String origin) { + return this.twitchClientTokenService.getAuthClientUrl( + TwitchClientTokenType.getTokenTypeByString(tokenType), + FrontOrigins.getRedirectAuthOrigin(origin) + ); } @Override - public TwitchClientTokenDTO getAccessToken() { + public TwitchClientTokenDTO getAccessToken(@NonNull String tokenType) { final UserDTO actualUser = currentSession.getCurrentUser(); if (actualUser == null) { throw new ApiBadRequestException("Vous n'êtes pas connecté à l'api."); } else { - return this.twitchClientTokenService.fetchToken(actualUser.getId()); + return this.twitchClientTokenService.fetchToken( + actualUser.getId(), + TwitchClientTokenType.getTokenTypeByString(tokenType) + ); } } @GetMapping("cb") - public String authClientCallback(@RequestParam(required = false) String code, - @RequestParam(required = false) String state, - @RequestParam(required = false) String error, - @RequestParam(required = false, name = "error_description") String errorMessage) { + public void authClientCallback(@RequestParam(required = false) String code, + @RequestParam(required = false) String state, + @RequestParam(required = false) String error, + @RequestParam(required = false, name = "error_description") String errorMessage, + HttpServletResponse response) { if (!Strings.isNullOrEmpty(error) || !Strings.isNullOrEmpty(errorMessage)) { - return "Une erreur Twitch est survenue. Veuillez vérifier que vous avez autorisé l'application FunixAPI sur Twitch."; + throw new ApiUnauthorizedException("Une erreur Twitch est survenue. Veuillez vérifier que vous avez autorisé l'application FunixProductions API sur Twitch."); } else { - this.twitchClientTokenService.registerNewAuthorizationAuthToken(code, state); - return "Votre compte est connecté avec Twitch ! Vous pouvez fermer cette fenêtre."; - } - } + final FrontOrigins origins = this.twitchClientTokenService.registerNewAuthorizationAuthToken(code, state); - private TwitchClientTokenType getTokenTypeByString(final String tokenType) { - for (final TwitchClientTokenType token : TwitchClientTokenType.values()) { - if (token.name().equalsIgnoreCase(tokenType)) { - return token; + try { + if (origins != null) { + response.sendRedirect(String.format( + "%s/callbacks/twitch", + Boolean.TRUE.equals(this.twitchAuthConfig.getDevMode()) ? origins.getDomainDev() : origins.getDomainProd() + )); + } else { + response.getWriter().write( + "Votre compte est connecté avec Twitch ! Vous pouvez fermer cette fenêtre." + ); + } + } catch (Exception e) { + throw new ApiException("Erreur interne lors de la redirection après la connexion avec Twitch. Veuillez recommencer.", e); } } - - return TwitchClientTokenType.VIEWER; } + } diff --git a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/resources/TwitchInternalAuthResource.java b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/resources/TwitchInternalAuthResource.java index 35f6d24b..d9fafd1c 100644 --- a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/resources/TwitchInternalAuthResource.java +++ b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/resources/TwitchInternalAuthResource.java @@ -2,9 +2,11 @@ import com.funixproductions.api.twitch.auth.client.clients.TwitchInternalAuthClient; import com.funixproductions.api.twitch.auth.client.dtos.TwitchClientTokenDTO; +import com.funixproductions.api.twitch.auth.client.enums.TwitchClientTokenType; import com.funixproductions.api.twitch.auth.service.services.TwitchClientTokenService; import com.funixproductions.api.twitch.auth.service.services.TwitchServerTokenService; import com.funixproductions.core.exceptions.ApiBadRequestException; +import lombok.NonNull; import lombok.RequiredArgsConstructor; import org.apache.logging.log4j.util.Strings; import org.springframework.web.bind.annotation.RequestMapping; @@ -21,22 +23,31 @@ public class TwitchInternalAuthResource implements TwitchInternalAuthClient { private final TwitchServerTokenService twitchServerTokenService; @Override - public TwitchClientTokenDTO fetchToken(String userUUID) { + public TwitchClientTokenDTO fetchToken(String userUUID, @NonNull String tokenType) { if (Strings.isBlank(userUUID)) { throw new ApiBadRequestException("Le userUUID ne doit pas être vide."); } - return twitchClientTokenService.fetchToken(UUID.fromString(userUUID)); + return twitchClientTokenService.fetchToken( + UUID.fromString(userUUID), + TwitchClientTokenType.getTokenTypeByString(tokenType) + ); } @Override - public TwitchClientTokenDTO fetchTokenByStreamerName(String streamerName) { - return twitchClientTokenService.fetchTokenByStreamerUsername(streamerName); + public TwitchClientTokenDTO fetchTokenByStreamerName(String streamerName, @NonNull String tokenType) { + return twitchClientTokenService.fetchTokenByStreamerUsername( + streamerName, + TwitchClientTokenType.getTokenTypeByString(tokenType) + ); } @Override - public TwitchClientTokenDTO fetchTokenByStreamerId(String streamerId) { - return twitchClientTokenService.fetchTokenByStreamerId(streamerId); + public TwitchClientTokenDTO fetchTokenByStreamerId(String streamerId, @NonNull String tokenType) { + return twitchClientTokenService.fetchTokenByStreamerId( + streamerId, + TwitchClientTokenType.getTokenTypeByString(tokenType) + ); } @Override diff --git a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/services/TwitchClientTokenService.java b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/services/TwitchClientTokenService.java index 19f8b04a..faa87e05 100644 --- a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/services/TwitchClientTokenService.java +++ b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/services/TwitchClientTokenService.java @@ -1,5 +1,6 @@ package com.funixproductions.api.twitch.auth.service.services; +import com.funixproductions.api.core.enums.FrontOrigins; import com.funixproductions.api.twitch.auth.client.configurations.TwitchApiConfig; import com.funixproductions.api.twitch.auth.client.dtos.TwitchClientTokenDTO; import com.funixproductions.api.twitch.auth.client.enums.TwitchClientTokenType; @@ -26,6 +27,7 @@ import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; +import javax.annotation.Nullable; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.time.Instant; @@ -55,6 +57,7 @@ public class TwitchClientTokenService { private static class CsrfUser { private final UserDTO user; private final TwitchClientTokenType tokenType; + private final @Nullable FrontOrigins frontOrigin; private final Instant createdAt; } @@ -85,21 +88,23 @@ public TwitchClientTokenService(TwitchAuthConfig twitchAuthConfig, * @param tokenType token type to determine the scopes permissions * @return url login */ - public String getAuthClientUrl(final TwitchClientTokenType tokenType) { + public String getAuthClientUrl(final TwitchClientTokenType tokenType, final FrontOrigins frontOrigin) { return "https://id.twitch.tv/oauth2/authorize" + "?response_type=code" + "&client_id=" + twitchApiConfig.getAppClientId() + "&redirect_uri=" + twitchAuthConfig.getAppCallbackDomain() + "/twitch/auth/cb" + "&scope=" + generateScopesForTokenType(tokenType) + - "&state=" + generateNewState(tokenType); + "&state=" + generateNewState(tokenType, frontOrigin); } /** * Called when we receive callback from twitch login * @param oAuthCode given by twitch * @param csrfToken echo back of the csrf we sended + * @return FrontOrigins si non null, redirige l'utilisateur sur une page web de la FunixProductions */ - public void registerNewAuthorizationAuthToken(final String oAuthCode, final String csrfToken) { + @Nullable + public FrontOrigins registerNewAuthorizationAuthToken(final String oAuthCode, final String csrfToken) { if (Strings.isNullOrEmpty(oAuthCode)) { throw new ApiBadRequestException("Il manque le oAuth code."); } else if (Strings.isNullOrEmpty(csrfToken)) { @@ -107,53 +112,69 @@ public void registerNewAuthorizationAuthToken(final String oAuthCode, final Stri } final CsrfUser csrfUser = this.csrfTokens.get(csrfToken); + if (csrfUser == null) { throw new ApiBadRequestException("Le csrf token est invalide. Veuillez vous reconnecter avec twitch."); } else { final UserDTO userDTO = csrfUser.getUser(); - final Optional searchToken = this.twitchClientTokenRepository.findTwitchClientTokenByUserUuid(userDTO.getId().toString()); + final Optional searchToken = this.twitchClientTokenRepository.findTwitchClientTokenByUserUuidAndTokenType( + userDTO.getId().toString(), + csrfUser.tokenType + ); searchToken.ifPresent(this.twitchClientTokenRepository::delete); - generateNewAccessToken(csrfUser, userDTO, oAuthCode); + + this.generateNewAccessToken(csrfUser, userDTO, oAuthCode); this.csrfTokens.remove(csrfToken); + + return csrfUser.getFrontOrigin(); } } - public TwitchClientTokenDTO fetchToken(final UUID userUuid) { + public TwitchClientTokenDTO fetchToken(final UUID userUuid, final TwitchClientTokenType tokenType) { if (userUuid == null) { throw new ApiBadRequestException("Pas de user uuid spécifié pour la récupération de tokens twitch."); } - final TwitchClientToken twitchClientToken = this.twitchClientTokenRepository.findTwitchClientTokenByUserUuid(userUuid.toString()).orElseThrow(() -> new ApiNotFoundException(String.format("L'utilisateur %s ne possède pas de tokens twitch.", userUuid))); + final TwitchClientToken twitchClientToken = this.twitchClientTokenRepository.findTwitchClientTokenByUserUuidAndTokenType( + userUuid.toString(), + tokenType + ).orElseThrow(() -> new ApiNotFoundException(String.format("L'utilisateur %s ne possède pas de tokens twitch de type %s.", userUuid, tokenType))); return refreshToken(twitchClientToken); } - public TwitchClientTokenDTO fetchTokenByStreamerUsername(final String streamerUsername) { + public TwitchClientTokenDTO fetchTokenByStreamerUsername(final String streamerUsername, final TwitchClientTokenType tokenType) { if (Strings.isNullOrEmpty(streamerUsername)) { throw new ApiBadRequestException("Pas de streamer username spécifié pour la récupération de tokens twitch."); } - final Optional twitchClientToken = this.twitchClientTokenRepository.findTwitchClientTokenByTwitchUsername(streamerUsername); + final Optional twitchClientToken = this.twitchClientTokenRepository.findTwitchClientTokenByTwitchUsernameAndTokenType( + streamerUsername, + tokenType + ); if (twitchClientToken.isPresent()) { return refreshToken(twitchClientToken.get()); } else { - throw new ApiNotFoundException(String.format("Le streamer %s ne possède pas de tokens twitch.", streamerUsername)); + throw new ApiNotFoundException(String.format("Le streamer %s ne possède pas de tokens twitch de type %s.", streamerUsername, tokenType)); } } - public TwitchClientTokenDTO fetchTokenByStreamerId(final String streamerId) { + public TwitchClientTokenDTO fetchTokenByStreamerId(final String streamerId, final TwitchClientTokenType tokenType) { if (Strings.isNullOrEmpty(streamerId)) { throw new ApiBadRequestException("Pas de streamer id spécifié pour la récupération de tokens twitch."); } - final Optional twitchClientToken = this.twitchClientTokenRepository.findTwitchClientTokenByTwitchUserId(streamerId); + final Optional twitchClientToken = this.twitchClientTokenRepository.findTwitchClientTokenByTwitchUserIdAndTokenType( + streamerId, + tokenType + ); if (twitchClientToken.isPresent()) { return refreshToken(twitchClientToken.get()); } else { - throw new ApiNotFoundException(String.format("Le streamer %s ne possède pas de tokens twitch.", streamerId)); + throw new ApiNotFoundException(String.format("Le streamer %s ne possède pas de tokens twitch de type %s.", streamerId, tokenType)); } } @@ -166,7 +187,7 @@ public TwitchClientTokenDTO fetchTokenByStreamerId(final String streamerId) { private String generateScopesForTokenType(final TwitchClientTokenType tokenType) { final Set scopes; - if (tokenType == TwitchClientTokenType.STREAMER) { + if (tokenType == TwitchClientTokenType.FUNIXGAMING) { scopes = Set.of( "bits:read", "channel:edit:commercial", @@ -211,6 +232,21 @@ private String generateScopesForTokenType(final TwitchClientTokenType tokenType) "moderator:read:shoutouts", "moderator:manage:shoutouts" ); + } else if (tokenType == TwitchClientTokenType.STREAMER) { + scopes = Set.of( + "user:read:email", + "user:read:follows", + "user:read:subscriptions", + "moderator:read:followers", + "moderator:read:chatters", + "bits:read", + "channel:read:hype_train", + "channel:read:polls", + "channel:read:predictions", + "channel:read:subscriptions", + "channel:read:redemptions", + "channel:manage:redemptions" + ); } else { scopes = Set.of( "channel:read:subscriptions", @@ -220,17 +256,25 @@ private String generateScopesForTokenType(final TwitchClientTokenType tokenType) ); } - final String scopeList = String.join(" ", scopes); - return URLEncoder.encode(scopeList, StandardCharsets.UTF_8); + return URLEncoder.encode( + String.join(" ", scopes), + StandardCharsets.UTF_8 + ); } - private String generateNewState(final TwitchClientTokenType tokenType) throws ApiBadRequestException { + private String generateNewState(final TwitchClientTokenType tokenType, + final @Nullable FrontOrigins frontOrigin) throws ApiBadRequestException { final UserDTO userDTO = currentSession.getCurrentUser(); if (userDTO == null) { - throw new ApiBadRequestException("Vous n'êtes pas connecté à la FunixAPI."); + throw new ApiBadRequestException("Vous n'êtes pas connecté à la FunixProductions API."); } - final CsrfUser csrfUser = new CsrfUser(userDTO, tokenType, Instant.now()); + final CsrfUser csrfUser = new CsrfUser( + userDTO, + tokenType, + frontOrigin, + Instant.now() + ); final String state = passwordGenerator.generateRandomPassword(); this.csrfTokens.put(state, csrfUser); @@ -299,6 +343,7 @@ private TwitchClientTokenDTO refreshToken(final TwitchClientToken token) { } final TwitchClientTokenDTO tokenToSend = twitchClientTokenMapper.toDto(twitchClientTokenRepository.save(token)); + tokenToSend.setScopes(scopes); return tokenToSend; } catch (FeignException e) { @@ -310,5 +355,4 @@ private TwitchClientTokenDTO refreshToken(final TwitchClientToken token) { } } - } diff --git a/modules/twitch/auth/service/src/main/resources/application-dev.properties b/modules/twitch/auth/service/src/main/resources/application-dev.properties index a68b994c..e4f997aa 100644 --- a/modules/twitch/auth/service/src/main/resources/application-dev.properties +++ b/modules/twitch/auth/service/src/main/resources/application-dev.properties @@ -1,2 +1,3 @@ twitch.api.auth.app-callback-domain=https://dev.api.funixproductions.com +twitch.api.auth.dev-mode=true sentry.environment=development diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/enums/ChannelEventType.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/enums/ChannelEventType.java index f4803788..a706925a 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/enums/ChannelEventType.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/enums/ChannelEventType.java @@ -109,6 +109,6 @@ public enum ChannelEventType { private final String type; private final String version; - private final Class clazz; + private final Class clazz; } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/AChannelSubscription.java similarity index 79% rename from modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelSubscription.java rename to modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/AChannelSubscription.java index 86771ba1..027a6193 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/AChannelSubscription.java @@ -3,11 +3,11 @@ import com.funixproductions.api.twitch.eventsub.service.requests.TwitchSubscription; import com.google.gson.JsonObject; -public abstract class ChannelSubscription extends TwitchSubscription { +public abstract class AChannelSubscription extends TwitchSubscription { private final String streamerId; - protected ChannelSubscription(String streamerId, String type, String version) { + protected AChannelSubscription(String streamerId, String type, String version) { super(type, version); this.streamerId = streamerId; } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelBanSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelBanSubscription.java index a06a0157..36ad0c58 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelBanSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelBanSubscription.java @@ -3,7 +3,7 @@ import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; -public class ChannelBanSubscription extends ChannelSubscription { +public class ChannelBanSubscription extends AChannelSubscription { public ChannelBanSubscription(final String streamerId) { super(streamerId, ChannelEventType.BAN.getType(), ChannelEventType.BAN.getVersion()); } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelCheerSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelCheerSubscription.java index 5259c4cd..8f62d159 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelCheerSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelCheerSubscription.java @@ -2,7 +2,10 @@ import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; -public class ChannelCheerSubscription extends ChannelSubscription { +/** + * Doc + */ +public class ChannelCheerSubscription extends AChannelSubscription { public ChannelCheerSubscription(final String streamerId) { super(streamerId, ChannelEventType.CHEER.getType(), ChannelEventType.CHEER.getVersion()); diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelFollowSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelFollowSubscription.java index 82a4f8b9..8010e9a4 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelFollowSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelFollowSubscription.java @@ -6,7 +6,7 @@ /** * Documentation */ -public class ChannelFollowSubscription extends ChannelSubscription { +public class ChannelFollowSubscription extends AChannelSubscription { public ChannelFollowSubscription(String streamerId) { super(streamerId, ChannelEventType.FOLLOW.getType(), ChannelEventType.FOLLOW.getVersion()); diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelHypeTrainBeginSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelHypeTrainBeginSubscription.java index 717026ce..b3edaa15 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelHypeTrainBeginSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelHypeTrainBeginSubscription.java @@ -3,7 +3,10 @@ import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; -public class ChannelHypeTrainBeginSubscription extends ChannelSubscription { +/** + * Doc + */ +public class ChannelHypeTrainBeginSubscription extends AChannelSubscription { public ChannelHypeTrainBeginSubscription(final String streamerId) { super(streamerId, ChannelEventType.HYPE_TRAIN_BEGIN.getType(), ChannelEventType.HYPE_TRAIN_BEGIN.getVersion()); } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelHypeTrainEndSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelHypeTrainEndSubscription.java index c23f25ab..8fc45be6 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelHypeTrainEndSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelHypeTrainEndSubscription.java @@ -3,7 +3,10 @@ import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; -public class ChannelHypeTrainEndSubscription extends ChannelSubscription { +/** + * Doc + */ +public class ChannelHypeTrainEndSubscription extends AChannelSubscription { public ChannelHypeTrainEndSubscription(final String streamerId) { super(streamerId, ChannelEventType.HYPE_TRAIN_END.getType(), ChannelEventType.HYPE_TRAIN_END.getVersion()); } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelHypeTrainProgressSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelHypeTrainProgressSubscription.java index 7e648c3f..19c86bb3 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelHypeTrainProgressSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelHypeTrainProgressSubscription.java @@ -3,7 +3,10 @@ import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; -public class ChannelHypeTrainProgressSubscription extends ChannelSubscription { +/** + * Doc + */ +public class ChannelHypeTrainProgressSubscription extends AChannelSubscription { public ChannelHypeTrainProgressSubscription(final String streamerId) { super(streamerId, ChannelEventType.HYPE_TRAIN_PROGRESS.getType(), ChannelEventType.HYPE_TRAIN_PROGRESS.getVersion()); } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPollBeginSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPollBeginSubscription.java index 533084db..e8b61d3c 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPollBeginSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPollBeginSubscription.java @@ -2,7 +2,10 @@ import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; -public class ChannelPollBeginSubscription extends ChannelSubscription { +/** + * Doc + */ +public class ChannelPollBeginSubscription extends AChannelSubscription { public ChannelPollBeginSubscription(final String streamerId) { super(streamerId, ChannelEventType.POLL_BEGIN.getType(), ChannelEventType.POLL_BEGIN.getVersion()); } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPollEndSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPollEndSubscription.java index 419b7aef..9dea683a 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPollEndSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPollEndSubscription.java @@ -2,7 +2,10 @@ import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; -public class ChannelPollEndSubscription extends ChannelSubscription { +/** + * Doc + */ +public class ChannelPollEndSubscription extends AChannelSubscription { public ChannelPollEndSubscription(final String streamerId) { super(streamerId, ChannelEventType.POLL_END.getType(), ChannelEventType.POLL_END.getVersion()); } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPollProgressSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPollProgressSubscription.java index 4d1ba77f..9966b9fe 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPollProgressSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPollProgressSubscription.java @@ -2,7 +2,10 @@ import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; -public class ChannelPollProgressSubscription extends ChannelSubscription { +/** + * Doc + */ +public class ChannelPollProgressSubscription extends AChannelSubscription { public ChannelPollProgressSubscription(final String streamerId) { super(streamerId, ChannelEventType.POLL_PROGRESS.getType(), ChannelEventType.POLL_PROGRESS.getVersion()); diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPredictionBeginSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPredictionBeginSubscription.java index 6860fb79..a73c4b8b 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPredictionBeginSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPredictionBeginSubscription.java @@ -2,7 +2,10 @@ import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; -public class ChannelPredictionBeginSubscription extends ChannelSubscription { +/** + * Doc + */ +public class ChannelPredictionBeginSubscription extends AChannelSubscription { public ChannelPredictionBeginSubscription(final String streamerId) { super(streamerId, ChannelEventType.PREDICTION_BEGIN.getType(), ChannelEventType.PREDICTION_BEGIN.getVersion()); } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPredictionEndSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPredictionEndSubscription.java index 909816fc..eb0b87bd 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPredictionEndSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPredictionEndSubscription.java @@ -2,7 +2,10 @@ import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; -public class ChannelPredictionEndSubscription extends ChannelSubscription { +/** + * Doc + */ +public class ChannelPredictionEndSubscription extends AChannelSubscription { public ChannelPredictionEndSubscription(final String streamerId) { super(streamerId, ChannelEventType.PREDICTION_END.getType(), ChannelEventType.PREDICTION_END.getVersion()); } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPredictionProgressSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPredictionProgressSubscription.java index e245b9b8..30b1134c 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPredictionProgressSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelPredictionProgressSubscription.java @@ -2,7 +2,10 @@ import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; -public class ChannelPredictionProgressSubscription extends ChannelSubscription { +/** + * Doc + */ +public class ChannelPredictionProgressSubscription extends AChannelSubscription { public ChannelPredictionProgressSubscription(final String streamerId) { super(streamerId, ChannelEventType.PREDICTION_PROGRESS.getType(), ChannelEventType.PREDICTION_PROGRESS.getVersion()); } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelRaidReceivedSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelRaidReceivedSubscription.java index 49862738..7613c160 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelRaidReceivedSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelRaidReceivedSubscription.java @@ -3,7 +3,10 @@ import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; import com.google.gson.JsonObject; -public class ChannelRaidReceivedSubscription extends ChannelSubscription { +/** + * Doc + */ +public class ChannelRaidReceivedSubscription extends AChannelSubscription { public ChannelRaidReceivedSubscription(final String streamerId) { super(streamerId, ChannelEventType.RAID_RECEIVED.getType(), ChannelEventType.RAID_RECEIVED.getVersion()); } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelResubMessageSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelResubMessageSubscription.java index 21f2d5ab..a0dd6831 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelResubMessageSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelResubMessageSubscription.java @@ -5,7 +5,7 @@ /** * Doc */ -public class ChannelResubMessageSubscription extends ChannelSubscription { +public class ChannelResubMessageSubscription extends AChannelSubscription { public ChannelResubMessageSubscription(final String streamerId) { super(streamerId, ChannelEventType.RESUB_MESSAGE.getType(), ChannelEventType.RESUB_MESSAGE.getVersion()); diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelRewardGetSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelRewardGetSubscription.java index 6d294b70..1e4ed36a 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelRewardGetSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelRewardGetSubscription.java @@ -2,7 +2,10 @@ import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; -public class ChannelRewardGetSubscription extends ChannelSubscription { +/** + * Doc + */ +public class ChannelRewardGetSubscription extends AChannelSubscription { public ChannelRewardGetSubscription(final String streamerId) { super(streamerId, ChannelEventType.POINTS_REWARD_GET.getType(), ChannelEventType.POINTS_REWARD_GET.getVersion()); diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelShoutOutCreateSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelShoutOutCreateSubscription.java index 8561bb4b..192f19e1 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelShoutOutCreateSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelShoutOutCreateSubscription.java @@ -3,7 +3,10 @@ import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; import com.google.gson.JsonObject; -public class ChannelShoutOutCreateSubscription extends ChannelSubscription { +/** + * Doc + */ +public class ChannelShoutOutCreateSubscription extends AChannelSubscription { public ChannelShoutOutCreateSubscription(final String streamerId) { super(streamerId, ChannelEventType.SHOUTOUT_CREATE.getType(), ChannelEventType.SHOUTOUT_CREATE.getVersion()); diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelShoutOutReceiveSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelShoutOutReceiveSubscription.java index 9b423639..9ebdb151 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelShoutOutReceiveSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelShoutOutReceiveSubscription.java @@ -3,7 +3,10 @@ import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; import com.google.gson.JsonObject; -public class ChannelShoutOutReceiveSubscription extends ChannelSubscription { +/** + * Doc + */ +public class ChannelShoutOutReceiveSubscription extends AChannelSubscription { public ChannelShoutOutReceiveSubscription(final String streamerId) { super(streamerId, ChannelEventType.SHOUTOUT_RECEIVE.getType(), ChannelEventType.SHOUTOUT_RECEIVE.getVersion()); diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelSubGiftSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelSubGiftSubscription.java index 9113064f..55d13832 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelSubGiftSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelSubGiftSubscription.java @@ -5,7 +5,7 @@ /** * Doc */ -public class ChannelSubGiftSubscription extends ChannelSubscription { +public class ChannelSubGiftSubscription extends AChannelSubscription { public ChannelSubGiftSubscription(final String streamerId) { super(streamerId, ChannelEventType.SUB_GIFT.getType(), ChannelEventType.SUB_GIFT.getVersion()); } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelSubSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelSubSubscription.java index ab134777..8a8ef83b 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelSubSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelSubSubscription.java @@ -3,7 +3,10 @@ import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; -public class ChannelSubSubscription extends ChannelSubscription { +/** + * Doc + */ +public class ChannelSubSubscription extends AChannelSubscription { public ChannelSubSubscription(String streamerId) { super(streamerId, ChannelEventType.SUBSCRIPTION.getType(), ChannelEventType.SUBSCRIPTION.getVersion()); diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelUpdateSubscription.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelUpdateSubscription.java index 16071ed3..ef5a8360 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelUpdateSubscription.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/requests/channel/ChannelUpdateSubscription.java @@ -6,7 +6,7 @@ /** * Documentation */ -public class ChannelUpdateSubscription extends ChannelSubscription { +public class ChannelUpdateSubscription extends AChannelSubscription { public ChannelUpdateSubscription(final String streamerId) { super(streamerId, ChannelEventType.UPDATE.getType(), ChannelEventType.UPDATE.getVersion()); diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationService.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationService.java index a1549cef..f1102b57 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationService.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationService.java @@ -7,7 +7,7 @@ import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; import com.funixproductions.api.twitch.eventsub.service.repositories.TwitchEventSubStreamerRepository; import com.funixproductions.api.twitch.eventsub.service.requests.TwitchSubscription; -import com.funixproductions.api.twitch.eventsub.service.requests.channel.ChannelSubscription; +import com.funixproductions.api.twitch.eventsub.service.requests.channel.AChannelSubscription; import com.funixproductions.api.twitch.reference.client.clients.users.TwitchUsersClient; import com.funixproductions.api.twitch.reference.client.dtos.responses.TwitchDataResponseDTO; import com.funixproductions.api.twitch.reference.client.dtos.responses.user.TwitchUserDTO; @@ -24,10 +24,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.concurrent.TimeUnit; /** @@ -121,12 +118,11 @@ public void removeStreamerSubscriptions(final String streamerUsername, final Str */ @Async public void updateSubscriptions(final String streamerUsername, final String streamerId) { - final List subscriptions = new ArrayList<>(); - subscriptions.addAll(generateChannelSubscriptions(streamerId)); - + final Collection subscriptions = this.generateChannelSubscriptions(streamerId); if (this.streamerIdsCreating.contains(streamerId)) { return; } + this.streamerIdsCreating.add(streamerId); try { @@ -156,7 +152,7 @@ public void updateSubscriptions(final String streamerUsername, final String stre @Scheduled(fixedRate = 10, timeUnit = TimeUnit.MINUTES) public void checkStreamersSubscriptions() { for (final TwitchEventSubStreamer subStreamer : repository.findAll()) { - updateSubscriptions("streamerId:" + subStreamer.getStreamerId(), subStreamer.getStreamerId()); + this.updateSubscriptions("streamerId:" + subStreamer.getStreamerId(), subStreamer.getStreamerId()); } } @@ -235,8 +231,8 @@ private List generateChannelSubscriptions(final String strea final List subscriptions = new ArrayList<>(); for (ChannelEventType eventType : ChannelEventType.values()) { - final Constructor constructor = eventType.getClazz().getConstructor(String.class); - final ChannelSubscription channelSubscription = constructor.newInstance(streamerId); + final Constructor constructor = eventType.getClazz().getConstructor(String.class); + final AChannelSubscription channelSubscription = constructor.newInstance(streamerId); subscriptions.add(channelSubscription); } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/websocket/TwitchEventSubWebsocketService.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/websocket/TwitchEventSubWebsocketService.java index 917ec940..9e4227b9 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/websocket/TwitchEventSubWebsocketService.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/websocket/TwitchEventSubWebsocketService.java @@ -1,6 +1,7 @@ package com.funixproductions.api.twitch.eventsub.service.services.websocket; import com.funixproductions.api.twitch.auth.client.clients.TwitchInternalAuthClient; +import com.funixproductions.api.twitch.auth.client.enums.TwitchClientTokenType; import com.funixproductions.api.twitch.eventsub.client.dtos.websocket.TwitchEventSubWebsocketMessage; import com.funixproductions.core.tools.websocket.services.ApiWebsocketServerHandler; import com.google.common.cache.Cache; @@ -62,8 +63,11 @@ public void newNotification(final String notificationType, final String streamer protected void newWebsocketMessage(@NonNull WebSocketSession session, @NonNull String message) { final String[] data = message.split(":"); - if (data.length == 2 && data[0].equals(LISTEN_CALL_CLIENT)) { - final String streamerId = this.getStreamerIdByUsername(data[1]); + if (data.length == 3 && data[0].equals(LISTEN_CALL_CLIENT)) { + final String streamerId = this.getStreamerIdByUsername( + data[1], + TwitchClientTokenType.getTokenTypeByString(data[2]) + ); if (streamerId == null) return; final List streamersEvents = this.sessionsMapsStreamersEvents.getOrDefault(session.getId(), new ArrayList<>()); @@ -79,14 +83,14 @@ protected void onClientDisconnect(String sessionId) { } @Nullable - private String getStreamerIdByUsername(final String username) { + private String getStreamerIdByUsername(final String username, final TwitchClientTokenType tokenType) { String streamerId = this.streamerIdCache.getIfPresent(username); if (streamerId != null) { return streamerId; } else { try { - streamerId = twitchInternalAuthClient.fetchTokenByStreamerName(username).getTwitchUserId(); + streamerId = twitchInternalAuthClient.fetchTokenByStreamerName(username, tokenType.name()).getTwitchUserId(); this.streamerIdCache.put(username, streamerId); return streamerId; diff --git a/modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceServiceTest.java b/modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceServiceTest.java index b38c801d..0b78511c 100644 --- a/modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceServiceTest.java +++ b/modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceServiceTest.java @@ -41,7 +41,7 @@ class TwitchEventSubReferenceServiceTest { void setupMocks() { when(hmacService.getKey()).thenReturn("key"); when(tokenService.fetchServerToken()).thenReturn("token"); - when(tokenService.fetchToken(anyString())).thenReturn(new TwitchClientTokenDTO()); + when(tokenService.fetchToken(anyString(), anyString())).thenReturn(new TwitchClientTokenDTO()); when(referenceClient.getSubscriptions(any(), any(), any(), any(), any())).thenReturn(new TwitchEventSubListDTO()); doNothing().when(referenceClient).createSubscription(any(), any(), any()); doNothing().when(referenceClient).deleteSubscription(any(), any()); diff --git a/modules/twitch/reference/service/src/main/java/com/funixproductions/api/twitch/reference/service/resources/channel/TwitchChannelPointsResource.java b/modules/twitch/reference/service/src/main/java/com/funixproductions/api/twitch/reference/service/resources/channel/TwitchChannelPointsResource.java index 1ffad6c1..7b2eab53 100644 --- a/modules/twitch/reference/service/src/main/java/com/funixproductions/api/twitch/reference/service/resources/channel/TwitchChannelPointsResource.java +++ b/modules/twitch/reference/service/src/main/java/com/funixproductions/api/twitch/reference/service/resources/channel/TwitchChannelPointsResource.java @@ -2,6 +2,7 @@ import com.funixproductions.api.twitch.auth.client.clients.TwitchInternalAuthClient; import com.funixproductions.api.twitch.auth.client.dtos.TwitchClientTokenDTO; +import com.funixproductions.api.twitch.auth.client.enums.TwitchClientTokenType; import com.funixproductions.api.twitch.reference.client.clients.channel.TwitchChannelPointsClient; import com.funixproductions.api.twitch.reference.client.dtos.responses.TwitchDataResponseDTO; import com.funixproductions.api.twitch.reference.client.dtos.responses.channel.chat.TwitchChannelRewardDTO; @@ -22,7 +23,7 @@ public class TwitchChannelPointsResource implements TwitchChannelPointsClient { @Override public TwitchDataResponseDTO getChannelRewards(String userId) { try { - final TwitchClientTokenDTO tokenDTO = this.internalAuthClient.fetchToken(userId); + final TwitchClientTokenDTO tokenDTO = this.internalAuthClient.fetchToken(userId, TwitchClientTokenType.STREAMER.name()); return service.getChannelRewards( tokenDTO.getAccessToken(), diff --git a/modules/twitch/reference/service/src/main/java/com/funixproductions/api/twitch/reference/service/resources/channel/TwitchChannelResource.java b/modules/twitch/reference/service/src/main/java/com/funixproductions/api/twitch/reference/service/resources/channel/TwitchChannelResource.java index 0631088d..42f2fbdf 100644 --- a/modules/twitch/reference/service/src/main/java/com/funixproductions/api/twitch/reference/service/resources/channel/TwitchChannelResource.java +++ b/modules/twitch/reference/service/src/main/java/com/funixproductions/api/twitch/reference/service/resources/channel/TwitchChannelResource.java @@ -2,6 +2,7 @@ import com.funixproductions.api.twitch.auth.client.clients.TwitchInternalAuthClient; import com.funixproductions.api.twitch.auth.client.dtos.TwitchClientTokenDTO; +import com.funixproductions.api.twitch.auth.client.enums.TwitchClientTokenType; import com.funixproductions.api.twitch.reference.client.clients.channel.TwitchChannelClient; import com.funixproductions.api.twitch.reference.client.dtos.requests.TwitchChannelUpdateDTO; import com.funixproductions.api.twitch.reference.client.dtos.responses.TwitchDataResponseDTO; @@ -41,7 +42,7 @@ public TwitchDataResponseDTO getChannelInformation(List getChannelVips(String maximumReturned, String after, List userIds, String userId) { try { - final TwitchClientTokenDTO tokenDTO = this.internalAuthClient.fetchToken(userId); + final TwitchClientTokenDTO tokenDTO = this.internalAuthClient.fetchToken(userId, TwitchClientTokenType.FUNIXGAMING.name()); return service.getChannelVips( tokenDTO.getAccessToken(), @@ -77,7 +78,7 @@ public TwitchDataResponseDTO getChannelVips(String maximum @Override public TwitchDataResponseDTO getChannelFollowers(String maximumReturned, String after, String userTwitchId, String userAppId) { try { - final TwitchClientTokenDTO tokenDTO = this.internalAuthClient.fetchToken(userAppId); + final TwitchClientTokenDTO tokenDTO = this.internalAuthClient.fetchToken(userAppId, TwitchClientTokenType.STREAMER.name()); return service.getChannelFollowers( tokenDTO.getAccessToken(), diff --git a/modules/twitch/reference/service/src/main/java/com/funixproductions/api/twitch/reference/service/resources/chat/TwitchChatResource.java b/modules/twitch/reference/service/src/main/java/com/funixproductions/api/twitch/reference/service/resources/chat/TwitchChatResource.java index 020f286d..b96b91f7 100644 --- a/modules/twitch/reference/service/src/main/java/com/funixproductions/api/twitch/reference/service/resources/chat/TwitchChatResource.java +++ b/modules/twitch/reference/service/src/main/java/com/funixproductions/api/twitch/reference/service/resources/chat/TwitchChatResource.java @@ -2,6 +2,7 @@ import com.funixproductions.api.twitch.auth.client.clients.TwitchInternalAuthClient; import com.funixproductions.api.twitch.auth.client.dtos.TwitchClientTokenDTO; +import com.funixproductions.api.twitch.auth.client.enums.TwitchClientTokenType; import com.funixproductions.api.twitch.reference.client.clients.chat.TwitchChatClient; import com.funixproductions.api.twitch.reference.client.dtos.requests.TwitchChatAnnouncement; import com.funixproductions.api.twitch.reference.client.dtos.responses.TwitchDataResponseDTO; @@ -25,7 +26,7 @@ public TwitchDataResponseDTO getChannelChatters(Intege String paginationCursor, String userId) { try { - final TwitchClientTokenDTO tokenDTO = this.internalAuthClient.fetchToken(userId); + final TwitchClientTokenDTO tokenDTO = this.internalAuthClient.fetchToken(userId, TwitchClientTokenType.STREAMER.name()); return service.getChannelChatters( tokenDTO.getAccessToken(), @@ -44,7 +45,7 @@ public TwitchDataResponseDTO getChannelChatters(Intege @Override public void sendChatAnnouncement(TwitchChatAnnouncement announcement, String userId) { try { - final TwitchClientTokenDTO tokenDTO = this.internalAuthClient.fetchToken(userId); + final TwitchClientTokenDTO tokenDTO = this.internalAuthClient.fetchToken(userId, TwitchClientTokenType.FUNIXGAMING.name()); service.sendChatAnnouncement( tokenDTO.getAccessToken(), From f4fa44df0d02acf690a23afab8c7683d9575c7b0 Mon Sep 17 00:00:00 2001 From: Antoine PRONNIER Date: Thu, 19 Dec 2024 21:04:44 +0100 Subject: [PATCH 3/6] WIP: Refacto Twitch module --- Dockerfile | 2 +- .../client/enums/TwitchClientTokenType.java | 14 +-- .../services/TwitchClientTokenService.java | 35 +++++--- .../client/clients/TwitchEventSubClient.java | 17 +--- .../clients/TwitchEventSubInternalClient.java | 30 +++++++ .../entities/TwitchEventSubStreamer.java | 20 ----- .../TwitchEventSubStreamerRepository.java | 15 ---- .../TwitchEventSubInternalResource.java | 26 ++++++ .../resources/TwitchEventSubResource.java | 12 --- .../service/security/WebSecurity.java | 1 + .../TwitchEventSubReferenceService.java | 8 +- .../TwitchEventSubRegistrationService.java | 88 +------------------ .../services/TwitchReferenceService.java | 9 +- 13 files changed, 104 insertions(+), 173 deletions(-) create mode 100644 modules/twitch/eventsub/client/src/main/java/com/funixproductions/api/twitch/eventsub/client/clients/TwitchEventSubInternalClient.java delete mode 100644 modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/entities/TwitchEventSubStreamer.java delete mode 100644 modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/repositories/TwitchEventSubStreamerRepository.java create mode 100644 modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/resources/TwitchEventSubInternalResource.java diff --git a/Dockerfile b/Dockerfile index 48d8917c..1b56a63a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM openjdk:21-slim +FROM amazoncorretto:21-alpine ARG service_name ARG service_base_dir diff --git a/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/enums/TwitchClientTokenType.java b/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/enums/TwitchClientTokenType.java index ebd03bac..1562c8e7 100644 --- a/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/enums/TwitchClientTokenType.java +++ b/modules/twitch/auth/client/src/main/java/com/funixproductions/api/twitch/auth/client/enums/TwitchClientTokenType.java @@ -1,15 +1,19 @@ package com.funixproductions.api.twitch.auth.client.enums; import com.google.common.base.Strings; +import lombok.AllArgsConstructor; +import lombok.Getter; import javax.annotation.Nullable; +@Getter +@AllArgsConstructor public enum TwitchClientTokenType { - FUNIXGAMING, - STREAMER, - MODERATOR, - BOT, - VIEWER; + FUNIXGAMING(true), + STREAMER(true), + VIEWER(false); + + private boolean needRegisterEvents; public static TwitchClientTokenType getTokenTypeByString(@Nullable final String tokenType) { if (Strings.isNullOrEmpty(tokenType)) { diff --git a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/services/TwitchClientTokenService.java b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/services/TwitchClientTokenService.java index faa87e05..ad32cd47 100644 --- a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/services/TwitchClientTokenService.java +++ b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/services/TwitchClientTokenService.java @@ -112,23 +112,25 @@ public FrontOrigins registerNewAuthorizationAuthToken(final String oAuthCode, fi } final CsrfUser csrfUser = this.csrfTokens.get(csrfToken); - if (csrfUser == null) { throw new ApiBadRequestException("Le csrf token est invalide. Veuillez vous reconnecter avec twitch."); - } else { - final UserDTO userDTO = csrfUser.getUser(); - final Optional searchToken = this.twitchClientTokenRepository.findTwitchClientTokenByUserUuidAndTokenType( - userDTO.getId().toString(), - csrfUser.tokenType - ); - - searchToken.ifPresent(this.twitchClientTokenRepository::delete); + } + final UserDTO userDTO = csrfUser.getUser(); + final TwitchClientTokenType tokenType = csrfUser.getTokenType(); + final Optional searchToken = this.twitchClientTokenRepository.findTwitchClientTokenByUserUuidAndTokenType( + userDTO.getId().toString(), + csrfUser.tokenType + ); - this.generateNewAccessToken(csrfUser, userDTO, oAuthCode); - this.csrfTokens.remove(csrfToken); + searchToken.ifPresent(this.twitchClientTokenRepository::delete); - return csrfUser.getFrontOrigin(); + final TwitchClientToken generatedToken = this.generateNewAccessToken(csrfUser, userDTO, oAuthCode); + if (tokenType.isNeedRegisterEvents()) { + this.createEventSub(generatedToken.getTwitchUsername()); } + + this.csrfTokens.remove(csrfToken); + return csrfUser.getFrontOrigin(); } public TwitchClientTokenDTO fetchToken(final UUID userUuid, final TwitchClientTokenType tokenType) { @@ -281,7 +283,7 @@ private String generateNewState(final TwitchClientTokenType tokenType, return state; } - private void generateNewAccessToken(final CsrfUser csrfUser, final UserDTO user, final String oAuthToken) { + private TwitchClientToken generateNewAccessToken(final CsrfUser csrfUser, final UserDTO user, final String oAuthToken) { final Map formData = new HashMap<>(); formData.put("client_id", twitchApiConfig.getAppClientId()); formData.put("client_secret", twitchApiConfig.getAppClientSecret()); @@ -305,7 +307,8 @@ private void generateNewAccessToken(final CsrfUser csrfUser, final UserDTO user, twitchClientToken.setAccessToken(tokenResponseDTO.getAccessToken()); twitchClientToken.setRefreshToken(tokenResponseDTO.getRefreshToken()); twitchClientToken.setExpirationDateToken(Date.from(Instant.now().plusSeconds(tokenResponseDTO.getExpiresIn() - 60L))); - this.twitchClientTokenRepository.save(twitchClientToken); + + return this.twitchClientTokenRepository.save(twitchClientToken); } catch (FeignException e) { if (e.status() == HttpStatus.UNAUTHORIZED.value()) { throw new ApiForbiddenException(String.format("L'utilisateur %s à retiré l'accès à la FunixAPI sur twitch.", csrfUser.getUser().getUsername())); @@ -355,4 +358,8 @@ private TwitchClientTokenDTO refreshToken(final TwitchClientToken token) { } } + private void createEventSub(final String streamerName) { + + } + } diff --git a/modules/twitch/eventsub/client/src/main/java/com/funixproductions/api/twitch/eventsub/client/clients/TwitchEventSubClient.java b/modules/twitch/eventsub/client/src/main/java/com/funixproductions/api/twitch/eventsub/client/clients/TwitchEventSubClient.java index ec6bb6af..b0591a1d 100644 --- a/modules/twitch/eventsub/client/src/main/java/com/funixproductions/api/twitch/eventsub/client/clients/TwitchEventSubClient.java +++ b/modules/twitch/eventsub/client/src/main/java/com/funixproductions/api/twitch/eventsub/client/clients/TwitchEventSubClient.java @@ -2,7 +2,8 @@ import com.funixproductions.api.twitch.eventsub.client.dtos.TwitchEventSubListDTO; import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; @FeignClient( name = "TwitchEventSubClient", @@ -33,18 +34,4 @@ TwitchEventSubListDTO getSubscriptions( @RequestParam(name = "after", required = false) String after ); - /** - * Documentation Doc remove - * @param streamerUsername streamer username who has created streamer token with funix api - */ - @DeleteMapping - void deleteSubscription(@RequestParam(name = "streamer_username") String streamerUsername); - - /** - * Documentation Create sub - * @param streamerUsername streamer username who has created streamer token with funix api - */ - @PostMapping - void createSubscription(@RequestBody String streamerUsername); - } diff --git a/modules/twitch/eventsub/client/src/main/java/com/funixproductions/api/twitch/eventsub/client/clients/TwitchEventSubInternalClient.java b/modules/twitch/eventsub/client/src/main/java/com/funixproductions/api/twitch/eventsub/client/clients/TwitchEventSubInternalClient.java new file mode 100644 index 00000000..be9d2257 --- /dev/null +++ b/modules/twitch/eventsub/client/src/main/java/com/funixproductions/api/twitch/eventsub/client/clients/TwitchEventSubInternalClient.java @@ -0,0 +1,30 @@ +package com.funixproductions.api.twitch.eventsub.client.clients; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient( + name = "TwitchEventSubInternalClient", + url = "${funixproductions.api.twitch.eventsub.app-domain-url}", + path = "/kubeinternal/twitch/eventsub/" +) +public interface TwitchEventSubInternalClient { + + /** + * Documentation Create sub + * @param streamerUsername streamer username who has created streamer token with funix api + */ + @PostMapping + void createSubscription(@RequestBody String streamerUsername); + + /** + * Documentation Doc remove + * @param streamerUsername streamer username who has created streamer token with funix api + */ + @DeleteMapping + void deleteSubscription(@RequestParam(name = "streamer_username") String streamerUsername); + +} diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/entities/TwitchEventSubStreamer.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/entities/TwitchEventSubStreamer.java deleted file mode 100644 index 0e4e11d0..00000000 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/entities/TwitchEventSubStreamer.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.funixproductions.api.twitch.eventsub.service.entities; - -import com.funixproductions.core.crud.entities.ApiEntity; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -@Entity(name = "twitch_event_sub_streamers") -public class TwitchEventSubStreamer extends ApiEntity { - - /** - * Streamer twitch id - */ - @Column(nullable = false, unique = true, name = "streamer_id") - private String streamerId; - -} diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/repositories/TwitchEventSubStreamerRepository.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/repositories/TwitchEventSubStreamerRepository.java deleted file mode 100644 index 9d695c7c..00000000 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/repositories/TwitchEventSubStreamerRepository.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.funixproductions.api.twitch.eventsub.service.repositories; - -import com.funixproductions.api.twitch.eventsub.service.entities.TwitchEventSubStreamer; -import com.funixproductions.core.crud.repositories.ApiRepository; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -@Repository -public interface TwitchEventSubStreamerRepository extends ApiRepository { - - Optional findTwitchEventSubStreamerByStreamerId(String streamerId); - void deleteTwitchEventSubStreamersByStreamerId(String streamerId); - -} diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/resources/TwitchEventSubInternalResource.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/resources/TwitchEventSubInternalResource.java new file mode 100644 index 00000000..0446652d --- /dev/null +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/resources/TwitchEventSubInternalResource.java @@ -0,0 +1,26 @@ +package com.funixproductions.api.twitch.eventsub.service.resources; + +import com.funixproductions.api.twitch.eventsub.client.clients.TwitchEventSubInternalClient; +import com.funixproductions.api.twitch.eventsub.service.services.TwitchEventSubRegistrationService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/kubeinternal/twitch/eventsub") +@RequiredArgsConstructor +public class TwitchEventSubInternalResource implements TwitchEventSubInternalClient { + + private final TwitchEventSubRegistrationService twitchEventSubRegistrationService; + + @Override + public void createSubscription(String streamerUsername) { + twitchEventSubRegistrationService.createSubscription(streamerUsername); + } + + @Override + public void deleteSubscription(String streamerUsername) { + twitchEventSubRegistrationService.removeSubscription(streamerUsername); + } + +} diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/resources/TwitchEventSubResource.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/resources/TwitchEventSubResource.java index c14cee27..58fa6f47 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/resources/TwitchEventSubResource.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/resources/TwitchEventSubResource.java @@ -4,7 +4,6 @@ import com.funixproductions.api.twitch.eventsub.client.dtos.TwitchEventSubListDTO; import com.funixproductions.api.twitch.eventsub.service.services.TwitchEventSubCallbackService; import com.funixproductions.api.twitch.eventsub.service.services.TwitchEventSubReferenceService; -import com.funixproductions.api.twitch.eventsub.service.services.TwitchEventSubRegistrationService; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PostMapping; @@ -19,23 +18,12 @@ public class TwitchEventSubResource implements TwitchEventSubClient { private final TwitchEventSubCallbackService twitchEventSubCallbackService; private final TwitchEventSubReferenceService twitchEventSubReferenceService; - private final TwitchEventSubRegistrationService twitchEventSubRegistrationService; @Override public TwitchEventSubListDTO getSubscriptions(String status, String type, String userId, String after) { return twitchEventSubReferenceService.getSubscriptions(status, type, userId, after); } - @Override - public void deleteSubscription(String streamerUsername) { - twitchEventSubRegistrationService.removeSubscription(streamerUsername); - } - - @Override - public void createSubscription(String streamerUsername) { - twitchEventSubRegistrationService.createSubscription(streamerUsername); - } - @PostMapping("cb") public String handleTwitchCallback(@RequestBody final byte[] body, final HttpServletRequest servletRequest) { diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/security/WebSecurity.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/security/WebSecurity.java index 97deee08..24ca8e7a 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/security/WebSecurity.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/security/WebSecurity.java @@ -17,6 +17,7 @@ public class WebSecurity extends ApiWebSecurity { @Override public Customizer.AuthorizationManagerRequestMatcherRegistry> getUrlsMatchers() { return ex -> ex + .requestMatchers("/kubeinternal/**").permitAll() .requestMatchers("/ws/public/**").permitAll() .requestMatchers("/twitch/eventsub/cb**").permitAll() .requestMatchers("/actuator/**").permitAll() diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceService.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceService.java index 521b1596..a1fe794d 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceService.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceService.java @@ -6,9 +6,11 @@ import com.funixproductions.api.twitch.eventsub.service.configs.TwitchEventSubConfig; import com.funixproductions.api.twitch.eventsub.service.requests.TwitchSubscription; import com.funixproductions.api.twitch.reference.client.services.TwitchReferenceService; +import com.funixproductions.core.exceptions.ApiException; import feign.FeignException; import lombok.NonNull; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; @@ -17,6 +19,7 @@ * Service who encapsulates the twitch client who calls the twitch api */ @Service +@Slf4j(topic = "TwitchEventSubReferenceService") @RequiredArgsConstructor public class TwitchEventSubReferenceService extends TwitchReferenceService { @@ -53,7 +56,10 @@ public void createSubscription(@NonNull final TwitchSubscription request) { request.getPayload() ); } catch (FeignException e) { - throw super.handleFeignException(e); + final int statusCode = e.status(); + + if (statusCode == Status) + log.error("Create subscription error twitch.", new ApiException()); } } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationService.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationService.java index f1102b57..1e2cf7d7 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationService.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationService.java @@ -1,11 +1,7 @@ package com.funixproductions.api.twitch.eventsub.service.services; -import com.funixproductions.api.twitch.auth.client.clients.TwitchInternalAuthClient; import com.funixproductions.api.twitch.eventsub.client.dtos.TwitchEventSubListDTO; -import com.funixproductions.api.twitch.eventsub.service.clients.TwitchEventSubReferenceClient; -import com.funixproductions.api.twitch.eventsub.service.entities.TwitchEventSubStreamer; import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; -import com.funixproductions.api.twitch.eventsub.service.repositories.TwitchEventSubStreamerRepository; import com.funixproductions.api.twitch.eventsub.service.requests.TwitchSubscription; import com.funixproductions.api.twitch.eventsub.service.requests.channel.AChannelSubscription; import com.funixproductions.api.twitch.reference.client.clients.users.TwitchUsersClient; @@ -13,19 +9,15 @@ import com.funixproductions.api.twitch.reference.client.dtos.responses.user.TwitchUserDTO; import com.funixproductions.core.exceptions.ApiBadRequestException; import com.funixproductions.core.exceptions.ApiException; -import com.google.common.base.Strings; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.lang.Nullable; import org.springframework.scheduling.annotation.Async; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.*; -import java.util.concurrent.TimeUnit; /** * Service dedicated to the handling of register and removing streamer subscriptions events @@ -38,11 +30,7 @@ public class TwitchEventSubRegistrationService { public static final String TWITCH_SUBSCRIPTION_ACTIVE_STATUS = "enabled"; - private final TwitchEventSubStreamerRepository repository; - - private final TwitchEventSubReferenceClient twitchReferenceUsersService; private final TwitchEventSubReferenceService twitchEventSubReferenceService; - private final TwitchInternalAuthClient twitchServerTokenService; private final TwitchUsersClient twitchUsersClient; private final Set streamerIdsCreating = new HashSet<>(); @@ -56,15 +44,8 @@ public class TwitchEventSubRegistrationService { */ public void createSubscription(final String streamerUsername) throws ApiBadRequestException { final String streamerId = getUserIdFromUsername(streamerUsername); - if (repository.findTwitchEventSubStreamerByStreamerId(streamerId).isPresent()) { - throw new ApiBadRequestException(String.format("Le streamer %s possède déjà son lot de twitch subs.", streamerUsername)); - } updateSubscriptions(streamerUsername, streamerId); - - final TwitchEventSubStreamer eventSubStreamer = new TwitchEventSubStreamer(); - eventSubStreamer.setStreamerId(streamerId); - repository.save(eventSubStreamer); } /** @@ -76,9 +57,7 @@ public void createSubscription(final String streamerUsername) throws ApiBadReque @Transactional public void removeSubscription(final String streamerUsername) throws ApiBadRequestException { final String streamerId = getUserIdFromUsername(streamerUsername); - removeStreamerSubscriptions(streamerUsername, streamerId); - repository.deleteTwitchEventSubStreamersByStreamerId(streamerId); } /** @@ -119,55 +98,15 @@ public void removeStreamerSubscriptions(final String streamerUsername, final Str @Async public void updateSubscriptions(final String streamerUsername, final String streamerId) { final Collection subscriptions = this.generateChannelSubscriptions(streamerId); - if (this.streamerIdsCreating.contains(streamerId)) { - return; - } - + if (this.streamerIdsCreating.contains(streamerId)) return; this.streamerIdsCreating.add(streamerId); - try { - final List activeSubs = getActualEventsForStreamer(streamerId); - TwitchEventSubListDTO.TwitchEventSub actualActiveSub; - - for (final TwitchSubscription subscription : subscriptions) { - actualActiveSub = isNewSubscriptionIsAlreadyActivated(subscription, activeSubs); - - if (actualActiveSub != null) { - if (!Strings.isNullOrEmpty(actualActiveSub.getStatus()) && !actualActiveSub.getStatus().equals(TWITCH_SUBSCRIPTION_ACTIVE_STATUS)) { - deleteSubscriptionRequest(actualActiveSub, streamerId, streamerUsername); - } - } else { - createNewSubscriptionRequest(subscription, streamerUsername); - } - } - - } catch (InterruptedException interruptedException) { - this.streamerIdsCreating.remove(streamerId); - Thread.currentThread().interrupt(); - throw new ApiException("Thread coupé lors de la récupération des events streamer twitch avant de lui subscribe."); + for (final TwitchSubscription subscription : subscriptions) { + createNewSubscriptionRequest(subscription, streamerUsername); } this.streamerIdsCreating.remove(streamerId); } - @Scheduled(fixedRate = 10, timeUnit = TimeUnit.MINUTES) - public void checkStreamersSubscriptions() { - for (final TwitchEventSubStreamer subStreamer : repository.findAll()) { - this.updateSubscriptions("streamerId:" + subStreamer.getStreamerId(), subStreamer.getStreamerId()); - } - } - - @Nullable - private TwitchEventSubListDTO.TwitchEventSub isNewSubscriptionIsAlreadyActivated(final TwitchSubscription newSub, - final List activeSubs) { - for (final TwitchEventSubListDTO.TwitchEventSub activeSub : activeSubs) { - if (!Strings.isNullOrEmpty(activeSub.getType()) && activeSub.getType().equals(newSub.getType())) { - return activeSub; - } - } - - return null; - } - private void createNewSubscriptionRequest(final TwitchSubscription subscription, final String streamerUsername) { try { this.twitchEventSubReferenceService.createSubscription(subscription); @@ -194,27 +133,6 @@ private void deleteSubscriptionRequest(final TwitchEventSubListDTO.TwitchEventSu } } - private List getActualEventsForStreamer(final String streamerId) throws InterruptedException, ApiException { - final List toSend = new ArrayList<>(); - - TwitchEventSubListDTO subs = this.twitchEventSubReferenceService.getSubscriptions(null, null, streamerId, null); - boolean continueLoop = true; - String pagination; - - while (continueLoop) { - toSend.addAll(subs.getData()); - - pagination = subs.hasPagination(); - if (pagination == null) { - continueLoop = false; - } else { - Thread.sleep(400); - subs = this.twitchEventSubReferenceService.getSubscriptions(null, null, streamerId, pagination); - } - } - return toSend; - } - private String getUserIdFromUsername(final String username) { final TwitchDataResponseDTO userList = this.twitchUsersClient.getUsersByName(List.of(username)); diff --git a/modules/twitch/reference/client/src/main/java/com/funixproductions/api/twitch/reference/client/services/TwitchReferenceService.java b/modules/twitch/reference/client/src/main/java/com/funixproductions/api/twitch/reference/client/services/TwitchReferenceService.java index f703afb4..400fc8f1 100644 --- a/modules/twitch/reference/client/src/main/java/com/funixproductions/api/twitch/reference/client/services/TwitchReferenceService.java +++ b/modules/twitch/reference/client/src/main/java/com/funixproductions/api/twitch/reference/client/services/TwitchReferenceService.java @@ -1,7 +1,6 @@ package com.funixproductions.api.twitch.reference.client.services; -import com.funixproductions.core.exceptions.ApiBadRequestException; -import com.funixproductions.core.exceptions.ApiException; +import com.funixproductions.core.exceptions.*; import feign.FeignException; public abstract class TwitchReferenceService { @@ -20,9 +19,9 @@ protected ApiException handleFeignException(final FeignException e) throws ApiBa switch (code) { case 400 -> throw new ApiBadRequestException(String.format("Erreur lors de la requête Twitch: %s.", errorMessage), e); - case 401 -> throw new ApiBadRequestException(String.format("Votre token d'accès Twitch est invalide. Erreur: %s", errorMessage), e); - case 403 -> throw new ApiBadRequestException(String.format("Accès refusé à la ressource Twitch. Erreur: %s", errorMessage), e); - case 404 -> throw new ApiBadRequestException(String.format("Ressouce Twitch introuvable. Erreur: %s", errorMessage), e); + case 401 -> throw new ApiUnauthorizedException(String.format("Votre token d'accès Twitch est invalide. Erreur: %s", errorMessage), e); + case 403 -> throw new ApiForbiddenException(String.format("Accès refusé à la ressource Twitch. Erreur: %s", errorMessage), e); + case 404 -> throw new ApiNotFoundException(String.format("Ressouce Twitch introuvable. Erreur: %s", errorMessage), e); case 409 -> throw new ApiBadRequestException(String.format("Dupplication d'entitées chez twitch. Erreur: %s", errorMessage), e); case 429 -> throw new ApiBadRequestException("Vous faites trop de requêtes à Twitch.", e); default -> throw new ApiBadRequestException(String.format("Erreur fatale twitch: %s", errorMessage), e); From ea367f18e58af0353221042a4f5ea1d41dded287 Mon Sep 17 00:00:00 2001 From: Antoine PRONNIER Date: Mon, 23 Dec 2024 00:07:35 +0100 Subject: [PATCH 4/6] WIP: Refacto Twitch module --- .../clients/TwitchEventSubInternalClient.java | 9 - .../service/enums/TwitchEventStatus.java | 50 +++++ .../TwitchEventSubInternalResource.java | 5 - .../TwitchEventSubReferenceService.java | 32 +++- .../TwitchEventSubRegistrationService.java | 176 +++++++++--------- 5 files changed, 170 insertions(+), 102 deletions(-) create mode 100644 modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/enums/TwitchEventStatus.java diff --git a/modules/twitch/eventsub/client/src/main/java/com/funixproductions/api/twitch/eventsub/client/clients/TwitchEventSubInternalClient.java b/modules/twitch/eventsub/client/src/main/java/com/funixproductions/api/twitch/eventsub/client/clients/TwitchEventSubInternalClient.java index be9d2257..38cb8bfa 100644 --- a/modules/twitch/eventsub/client/src/main/java/com/funixproductions/api/twitch/eventsub/client/clients/TwitchEventSubInternalClient.java +++ b/modules/twitch/eventsub/client/src/main/java/com/funixproductions/api/twitch/eventsub/client/clients/TwitchEventSubInternalClient.java @@ -1,10 +1,8 @@ package com.funixproductions.api.twitch.eventsub.client.clients; import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestParam; @FeignClient( name = "TwitchEventSubInternalClient", @@ -20,11 +18,4 @@ public interface TwitchEventSubInternalClient { @PostMapping void createSubscription(@RequestBody String streamerUsername); - /** - * Documentation Doc remove - * @param streamerUsername streamer username who has created streamer token with funix api - */ - @DeleteMapping - void deleteSubscription(@RequestParam(name = "streamer_username") String streamerUsername); - } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/enums/TwitchEventStatus.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/enums/TwitchEventStatus.java new file mode 100644 index 00000000..7beb6d80 --- /dev/null +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/enums/TwitchEventStatus.java @@ -0,0 +1,50 @@ +package com.funixproductions.api.twitch.eventsub.service.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * Doc + */ +@Getter +@AllArgsConstructor +public enum TwitchEventStatus { + /** + * Twitch has verified your callback and is able to send you notifications. + */ + ENABLED("enabled"), + /** + * Twitch is verifying that you own the callback specified in the create subscription request. For information about how it does this, see Verifying your callback. Used only for webhook subscriptions. + */ + CALLBACK_VERIFICATION_PENDING("webhook_callback_verification_pending"), + /** + * witch failed to verify that you own the callback specified in the create subscription request. Fix your event handler to correctly respond to the challenge, and then try subscribing again. Used only for webhook subscriptions. + */ + CALLBACK_VERIFICATION_FAILED("webhook_callback_verification_failed"), + /** + * Twitch revoked your subscription because the notification delivery failure rate was too high. Used only for webhook subscriptions. + */ + NOTIFICATION_FAILURES_EXCEEDED("notification_failures_exceeded"), + /** + * Twitch revoked your subscription because the users in the condition object revoked their authorization letting you get events on their behalf, or changed their password. + */ + AUTHORIZATION_REVOKED("authorization_revoked"), + /** + * The moderator that authorized the subscription is no longer one of the broadcaster’s moderators. + */ + MODERATOR_REMOVED("moderator_removed"), + /** + * Twitch revoked your subscription because the users in the condition object are no longer Twitch users. + */ + USER_REMOVED("user_removed"), + /** + * Twitch revoked your subscription because the subscription to subscription type and version is no longer supported. + */ + VERSION_REMOVED("version_removed"), + /** + * Twitch revoked your subscription because the beta subscription type was undergoing maintenance. + */ + BETA_MAINTENANCE("beta_maintenance"); + + private final String status; +} diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/resources/TwitchEventSubInternalResource.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/resources/TwitchEventSubInternalResource.java index 0446652d..cceef613 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/resources/TwitchEventSubInternalResource.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/resources/TwitchEventSubInternalResource.java @@ -18,9 +18,4 @@ public void createSubscription(String streamerUsername) { twitchEventSubRegistrationService.createSubscription(streamerUsername); } - @Override - public void deleteSubscription(String streamerUsername) { - twitchEventSubRegistrationService.removeSubscription(streamerUsername); - } - } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceService.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceService.java index a1fe794d..2ed5bc50 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceService.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubReferenceService.java @@ -6,6 +6,7 @@ import com.funixproductions.api.twitch.eventsub.service.configs.TwitchEventSubConfig; import com.funixproductions.api.twitch.eventsub.service.requests.TwitchSubscription; import com.funixproductions.api.twitch.reference.client.services.TwitchReferenceService; +import com.funixproductions.core.exceptions.ApiBadRequestException; import com.funixproductions.core.exceptions.ApiException; import feign.FeignException; import lombok.NonNull; @@ -45,7 +46,13 @@ public TwitchEventSubListDTO getSubscriptions(@Nullable String status, } } - public void createSubscription(@NonNull final TwitchSubscription request) { + /** + * Create a subscription to twitch eventsub + * @param request the subscription request + * @throws ApiException when error + * @throws ApiBadRequestException when conflicts + */ + public void createSubscription(@NonNull final TwitchSubscription request) throws ApiException, ApiBadRequestException { request.setEventUrlCallback(twitchEventSubConfig.getDomainUrlAppCallback() + "/twitch/eventsub/cb"); request.setSecretHmacKey(hmacService.getKey()); @@ -58,8 +65,27 @@ public void createSubscription(@NonNull final TwitchSubscription request) { } catch (FeignException e) { final int statusCode = e.status(); - if (statusCode == Status) - log.error("Create subscription error twitch.", new ApiException()); + if (statusCode == 400) { + throw new ApiException("Erreur 400 Bad Request de Twitch lors de la création d'une subscription Twitch.", e); + } else if (statusCode == 401) { + throw new ApiException("Erreur 401 Unauthorized de Twitch lors de la création d'une subscription Twitch.", e); + } else if (statusCode == 403) { + throw new ApiException("Erreur 403 Forbidden de Twitch lors de la création d'une subscription Twitch.", e); + } else if (statusCode == 404) { + throw new ApiException("Erreur 404 Not Found de Twitch lors de la création d'une subscription Twitch.", e); + } else if (statusCode == 429) { + throw new ApiException("Erreur 429 Too Many Requests (rate limit) de Twitch lors de la création d'une subscription Twitch.", e); + } else if (statusCode == 409) { + throw new ApiException(String.format( + "Erreur 409 Conflict de Twitch lors de la création d'une subscription Twitch. Event %s déjà existant.", + request.getType() + ), e); + } else { + throw new ApiException(String.format( + "Erreur inconnue (code: %d) lors de la création d'une subscription Twitch.", + statusCode + ), e); + } } } diff --git a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationService.java b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationService.java index 1e2cf7d7..dc554ac2 100644 --- a/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationService.java +++ b/modules/twitch/eventsub/service/src/main/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationService.java @@ -2,6 +2,7 @@ import com.funixproductions.api.twitch.eventsub.client.dtos.TwitchEventSubListDTO; import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; +import com.funixproductions.api.twitch.eventsub.service.enums.TwitchEventStatus; import com.funixproductions.api.twitch.eventsub.service.requests.TwitchSubscription; import com.funixproductions.api.twitch.eventsub.service.requests.channel.AChannelSubscription; import com.funixproductions.api.twitch.reference.client.clients.users.TwitchUsersClient; @@ -9,15 +10,17 @@ import com.funixproductions.api.twitch.reference.client.dtos.responses.user.TwitchUserDTO; import com.funixproductions.core.exceptions.ApiBadRequestException; import com.funixproductions.core.exceptions.ApiException; -import jakarta.transaction.Transactional; +import feign.FeignException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; +import javax.annotation.Nullable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.*; +import java.util.concurrent.TimeUnit; /** * Service dedicated to the handling of register and removing streamer subscriptions events @@ -28,13 +31,11 @@ @RequiredArgsConstructor public class TwitchEventSubRegistrationService { - public static final String TWITCH_SUBSCRIPTION_ACTIVE_STATUS = "enabled"; - private final TwitchEventSubReferenceService twitchEventSubReferenceService; private final TwitchUsersClient twitchUsersClient; - private final Set streamerIdsCreating = new HashSet<>(); - private final Set streamerIdsRemoving = new HashSet<>(); + private final Queue subscriptions = new LinkedList<>(); + private final Queue unsubscriptions = new LinkedList<>(); /** * Method called by the ressource to create the event list subscriptions for a streamer @@ -42,105 +43,82 @@ public class TwitchEventSubRegistrationService { * @param streamerUsername streamer name like funixgaming to create his subscriptions * @throws ApiBadRequestException when error */ - public void createSubscription(final String streamerUsername) throws ApiBadRequestException { - final String streamerId = getUserIdFromUsername(streamerUsername); + public void createSubscription(final String streamerUsername) { + final String streamerId = this.getUserIdFromUsername(streamerUsername); - updateSubscriptions(streamerUsername, streamerId); + this.subscriptions.addAll( + this.generateChannelSubscriptions(streamerId) + ); } - /** - * Method called by the ressource to remove a subscription list for a streamer - * Calls inside an async function who process the task (high CPU demand due to delay to not spamm twitch api) - * @param streamerUsername streamer name like funixgaming to remove his subscriptions - * @throws ApiBadRequestException when error - */ - @Transactional - public void removeSubscription(final String streamerUsername) throws ApiBadRequestException { - final String streamerId = getUserIdFromUsername(streamerUsername); - removeStreamerSubscriptions(streamerUsername, streamerId); + @Scheduled(fixedRate = 1, timeUnit = TimeUnit.SECONDS) + public void handleOnePendingSubscriptionFromQueue() { + final TwitchSubscription subscription = this.subscriptions.poll(); + + if (subscription != null) { + this.twitchEventSubReferenceService.createSubscription(subscription); + } } - /** - * Async method to remove all the streamer subscriptions from twitch - * @param streamerUsername streamer username used for logging - * @param streamerId streamer id - */ - @Async - public void removeStreamerSubscriptions(final String streamerUsername, final String streamerId) { - if (this.streamerIdsRemoving.contains(streamerId)) { + @Scheduled(fixedRate = 2, timeUnit = TimeUnit.SECONDS) + public void handleOnePendingUnsubscriptionFromQueue() { + final String subscriptionId = this.unsubscriptions.poll(); + + if (subscriptionId != null) { + this.twitchEventSubReferenceService.deleteSubscription(subscriptionId); + } + } + + @Scheduled(fixedRate = 10, timeUnit = TimeUnit.MINUTES) + public void fetchEventsThatNeedUpdate() { + final List events = this.getEventsThatNeedUpdate(); + if (events.isEmpty()) { return; } - this.streamerIdsRemoving.add(streamerId); - try { - for (final TwitchEventSubListDTO.TwitchEventSub sub : getActualEventsForStreamer(streamerId)) { - deleteSubscriptionRequest(sub, streamerId, streamerUsername); + for (TwitchEventSubListDTO.TwitchEventSub event : events) { + final TwitchSubscription subscription = this.generateSubscriptionFromEvent(event); + + if (subscription != null) { + this.subscriptions.add(subscription); + this.unsubscriptions.add(event.getId()); } - } catch (InterruptedException e) { - this.streamerIdsRemoving.remove(streamerId); - Thread.currentThread().interrupt(); - throw new ApiException(String.format("Thread coupé pendant la récupération des twitch events pour le streamer %s.", streamerUsername), e); - } catch (ApiException e) { - log.error("Une erreur est survenue lors de la récupération des events twitch pour le streamer {}. Erreur: {}", streamerUsername, e.getMessage()); } - - this.streamerIdsRemoving.remove(streamerId); - log.info("Streamer {} ne possède plus le hook des events twitch sur la funix api.", streamerUsername); } - /** - * Async method to run the whole subscriptions creation listed in the enums defined here:
- * Enums: {@link com.funixproductions.api.twitch.eventsub.service.requests}
- * It will check if the subscriptions you create are not aleready activated. - * @param streamerUsername streamer twitch username used for logging - * @param streamerId streamer twitch id - */ - @Async - public void updateSubscriptions(final String streamerUsername, final String streamerId) { - final Collection subscriptions = this.generateChannelSubscriptions(streamerId); - if (this.streamerIdsCreating.contains(streamerId)) return; - this.streamerIdsCreating.add(streamerId); + @Nullable + private TwitchSubscription generateSubscriptionFromEvent(TwitchEventSubListDTO.TwitchEventSub event) { + final TwitchEventSubListDTO.Condition condition = event.getCondition(); + if (condition == null) { + return null; + } + final String streamerId = condition.getStreamerId(); + if (streamerId == null) { + return null; + } + + final List subscriptions = this.generateChannelSubscriptions(streamerId); for (final TwitchSubscription subscription : subscriptions) { - createNewSubscriptionRequest(subscription, streamerUsername); + if (subscription.getType().equals(event.getType())) { + return subscription; + } } - this.streamerIdsCreating.remove(streamerId); - } - private void createNewSubscriptionRequest(final TwitchSubscription subscription, final String streamerUsername) { - try { - this.twitchEventSubReferenceService.createSubscription(subscription); - Thread.sleep(400); - log.info("CREATION TWITCH EVENT Le streamer {} possède désormais le hook twitch event {} sur la funix api.", streamerUsername, subscription.getType()); - } catch (InterruptedException interruptedException) { - Thread.currentThread().interrupt(); - throw new ApiException(String.format("Thread tué lors de la création de subscription pour le streamer %s.", streamerUsername), interruptedException); - } catch (ApiException apiException) { - log.error("Impossible de créer le hook avec l'event de type {} pour le streamer {}. Erreur: {}", subscription.getType(), streamerUsername, apiException.getMessage(), apiException); - } + return null; } - private void deleteSubscriptionRequest(final TwitchEventSubListDTO.TwitchEventSub sub, final String streamerId, final String streamerUsername) { + private String getUserIdFromUsername(final String username) throws ApiBadRequestException { try { - this.twitchEventSubReferenceService.deleteSubscription(sub.getId()); - Thread.sleep(400); - log.info("SUPPRESSION TWITCH EVENT Le streamer {} ne recevera plus de notifications pour l'event {}.", streamerId, sub.getType()); - } catch (InterruptedException interruptedException) { - Thread.currentThread().interrupt(); - throw new ApiException(String.format("Thread tué lors de la suppression d'une subscription type %s pour le streamer %s.", sub.getType(), streamerUsername), interruptedException); - } catch (ApiBadRequestException e) { - log.error("Une erreur est survenue lors de la suppression d'un event twitch pour le streamer {}. Event id {} Event type {}. Erreur: {}", streamerUsername, sub.getId(), sub.getType(), e.getMessage(), e); - } - } - - private String getUserIdFromUsername(final String username) { - final TwitchDataResponseDTO userList = this.twitchUsersClient.getUsersByName(List.of(username)); + final TwitchDataResponseDTO userList = this.twitchUsersClient.getUsersByName(List.of(username)); - if (userList.getData().isEmpty()) { - throw new ApiBadRequestException(String.format("Le streamer %s n'existe pas sur twitch.", username)); - } else { - final TwitchUserDTO twitchUserDTO = userList.getData().get(0); - return twitchUserDTO.getId(); + try { + return userList.getData().getFirst().getId(); + } catch (NoSuchElementException e) { + throw new ApiBadRequestException(String.format("Le streamer %s n'existe pas sur twitch.", username)); + } + } catch (FeignException e) { + throw new ApiException(String.format("Erreur interne lors de la récupération de l'id du streamer %s.", username), e); } } @@ -160,4 +138,32 @@ private List generateChannelSubscriptions(final String strea } } + private List getEventsThatNeedUpdate() throws ApiException { + try { + final List toSend = new ArrayList<>(); + + TwitchEventSubListDTO subs = this.twitchEventSubReferenceService.getSubscriptions( + TwitchEventStatus.VERSION_REMOVED.getStatus(), null, null, null + ); + String pagination; + + while (true) { + toSend.addAll(subs.getData()); + pagination = subs.hasPagination(); + + if (pagination == null) { + break; + } else { + subs = this.twitchEventSubReferenceService.getSubscriptions( + TwitchEventStatus.VERSION_REMOVED.getStatus(), null, null, pagination + ); + } + } + + return toSend; + } catch (Exception e) { + throw new ApiException("Erreur lors de la récupération des events Twitch qui demandent une mise à jour.", e); + } + } + } From 2166ffb7dfcdcdd7f2d9ebfb058132702e6ef6f1 Mon Sep 17 00:00:00 2001 From: Antoine PRONNIER Date: Mon, 23 Dec 2024 15:48:28 +0100 Subject: [PATCH 5/6] Removed useless files --- .../service/entities/TwitchClientToken.java | 5 +- ...TwitchEventSubRegistrationServiceTest.java | 166 ------------------ .../migration/V11__refacto_twitch_module.sql | 5 + 3 files changed, 7 insertions(+), 169 deletions(-) delete mode 100644 modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationServiceTest.java create mode 100644 modules/user/service/src/main/resources/db/migration/V11__refacto_twitch_module.sql diff --git a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/entities/TwitchClientToken.java b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/entities/TwitchClientToken.java index ab5cc868..d867edff 100644 --- a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/entities/TwitchClientToken.java +++ b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/entities/TwitchClientToken.java @@ -3,9 +3,7 @@ import com.funixproductions.api.encryption.client.utils.EncryptionString; import com.funixproductions.api.twitch.auth.client.enums.TwitchClientTokenType; import com.funixproductions.core.crud.entities.ApiEntity; -import jakarta.persistence.Column; -import jakarta.persistence.Convert; -import jakarta.persistence.Entity; +import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; @@ -44,6 +42,7 @@ public class TwitchClientToken extends ApiEntity { @Column(name = "expiration_date_token", nullable = false) private Date expirationDateToken; + @Enumerated(EnumType.STRING) @Column(name = "token_type", nullable = false) private TwitchClientTokenType tokenType; diff --git a/modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationServiceTest.java b/modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationServiceTest.java deleted file mode 100644 index e5de779f..00000000 --- a/modules/twitch/eventsub/service/src/test/java/com/funixproductions/api/twitch/eventsub/service/services/TwitchEventSubRegistrationServiceTest.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.funixproductions.api.twitch.eventsub.service.services; - -import com.funixproductions.api.twitch.auth.client.clients.TwitchInternalAuthClient; -import com.funixproductions.api.twitch.eventsub.client.dtos.TwitchEventSubListDTO; -import com.funixproductions.api.twitch.eventsub.service.entities.TwitchEventSubStreamer; -import com.funixproductions.api.twitch.eventsub.service.enums.ChannelEventType; -import com.funixproductions.api.twitch.eventsub.service.repositories.TwitchEventSubStreamerRepository; -import com.funixproductions.api.twitch.reference.client.clients.users.TwitchUsersClient; -import com.funixproductions.api.twitch.reference.client.dtos.responses.TwitchDataResponseDTO; -import com.funixproductions.api.twitch.reference.client.dtos.responses.user.TwitchUserDTO; -import com.funixproductions.core.exceptions.ApiBadRequestException; -import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.bean.override.mockito.MockitoBean; - -import java.util.*; - -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -@Slf4j -@SpringBootTest -@AutoConfigureMockMvc -@RunWith(MockitoJUnitRunner.class) -class TwitchEventSubRegistrationServiceTest { - - @Autowired - private TwitchEventSubRegistrationService service; - - @MockitoBean - private TwitchUsersClient twitchReferenceUsersService; - - @MockitoBean - private TwitchEventSubReferenceService twitchEventSubReferenceService; - - @MockitoBean - private TwitchInternalAuthClient twitchServerTokenService; - - @MockitoBean - private TwitchEventSubStreamerRepository repository; - - private final int eventCount = ChannelEventType.values().length; - - @BeforeEach - void setupMocks() { - setupTwitchEventSubReferenceServiceMocks(); - setupTwitchReferenceUsersServiceMocks(); - setupTwitchServerTokenServiceMocks(); - } - - @Test - void updateSubscriptionAsyncMethodTest() { - log.info("--- STARTED TEST updateSubscriptionAsyncMethodTest ---"); - service.updateSubscriptions(UUID.randomUUID().toString(), UUID.randomUUID().toString()); - log.info("--- DONE TEST updateSubscriptionAsyncMethodTest ---"); - - verify(twitchEventSubReferenceService, times(eventCount)).createSubscription(any()); - } - - @Test - void removeStreamerSubscriptionsTest() { - log.info("--- STARTED TEST removeStreamerSubscriptions ---"); - service.removeStreamerSubscriptions(UUID.randomUUID().toString(), UUID.randomUUID().toString()); - log.info("--- DONE TEST removeStreamerSubscriptions ---"); - - verify(twitchEventSubReferenceService, times(8)).deleteSubscription(any()); - } - - @Test - void checkStreamerSubscriptionsTest() { - final TwitchEventSubStreamer subStreamer = new TwitchEventSubStreamer(); - subStreamer.setStreamerId(UUID.randomUUID().toString()); - repository.save(subStreamer); - - service.checkStreamersSubscriptions(); - } - - @Test - void createSubscriptionsFromResource() { - final String streamerUsername = UUID.randomUUID().toString(); - - log.info("--- STARTED TEST createSubscriptionsFromResource ---"); - service.createSubscription(streamerUsername); - log.info("--- DONE TEST createSubscriptionsFromResource ---"); - - try { - service.createSubscription(streamerUsername); - } catch (ApiBadRequestException ignored) { - } - } - - @Test - void deleteSubscriptionsFromResource() { - final String streamerUsername = UUID.randomUUID().toString(); - - log.info("--- STARTED TEST createSubscriptionsFromResource ---"); - service.removeSubscription(streamerUsername); - log.info("--- DONE TEST createSubscriptionsFromResource ---"); - } - - private void setupTwitchEventSubReferenceServiceMocks() { - final Random random = new Random(); - final List eventSubs = new ArrayList<>(); - - for (int i = 0; i < 3; ++i) { - final TwitchEventSubListDTO.TwitchEventSub eventSub = new TwitchEventSubListDTO.TwitchEventSub(); - eventSub.setCondition(new TwitchEventSubListDTO.Condition()); - eventSub.setCost(random.nextInt()); - eventSub.setType(UUID.randomUUID().toString()); - eventSub.setStatus("active"); - eventSub.setId(UUID.randomUUID().toString()); - eventSub.setCreatedAt(new Date()); - eventSub.setVersion(UUID.randomUUID().toString()); - eventSubs.add(eventSub); - } - final TwitchEventSubListDTO.TwitchEventSub eventSub = new TwitchEventSubListDTO.TwitchEventSub(); - eventSub.setCondition(new TwitchEventSubListDTO.Condition()); - eventSub.setCost(random.nextInt()); - eventSub.setType(UUID.randomUUID().toString()); - eventSub.setStatus("not-active"); - eventSub.setId(UUID.randomUUID().toString()); - eventSub.setCreatedAt(new Date()); - eventSub.setVersion(UUID.randomUUID().toString()); - eventSubs.add(eventSub); - - final TwitchEventSubListDTO twitchEventSubListDTONoPage = new TwitchEventSubListDTO(); - twitchEventSubListDTONoPage.setPagination(null); - twitchEventSubListDTONoPage.setTotal(4); - twitchEventSubListDTONoPage.setTotalCost(10); - twitchEventSubListDTONoPage.setMaxTotalCost(100); - twitchEventSubListDTONoPage.setData(eventSubs); - - final TwitchEventSubListDTO twitchEventSubListDTOWithPage = new TwitchEventSubListDTO(); - twitchEventSubListDTOWithPage.setPagination(new TwitchEventSubListDTO.Pagination()); - twitchEventSubListDTOWithPage.getPagination().setCursor("data"); - twitchEventSubListDTOWithPage.setTotal(4); - twitchEventSubListDTOWithPage.setTotalCost(10); - twitchEventSubListDTOWithPage.setMaxTotalCost(100); - twitchEventSubListDTOWithPage.setData(eventSubs); - - when(twitchEventSubReferenceService.getSubscriptions(any(), any(), anyString(), isNull())).thenReturn(twitchEventSubListDTOWithPage); - when(twitchEventSubReferenceService.getSubscriptions(any(), any(), anyString(), anyString())).thenReturn(twitchEventSubListDTONoPage); - doNothing().when(twitchEventSubReferenceService).createSubscription(any()); - doNothing().when(twitchEventSubReferenceService).deleteSubscription(any()); - } - - private void setupTwitchReferenceUsersServiceMocks() { - final TwitchDataResponseDTO userList = new TwitchDataResponseDTO<>(); - final TwitchUserDTO userDTO = new TwitchUserDTO(); - userDTO.setId(UUID.randomUUID().toString()); - userList.setData(List.of(userDTO)); - - when(twitchReferenceUsersService.getUsersByName(anyList())).thenReturn(userList); - } - - private void setupTwitchServerTokenServiceMocks() { - when(twitchServerTokenService.fetchServerToken()).thenReturn(UUID.randomUUID().toString()); - } - -} diff --git a/modules/user/service/src/main/resources/db/migration/V11__refacto_twitch_module.sql b/modules/user/service/src/main/resources/db/migration/V11__refacto_twitch_module.sql new file mode 100644 index 00000000..799d0610 --- /dev/null +++ b/modules/user/service/src/main/resources/db/migration/V11__refacto_twitch_module.sql @@ -0,0 +1,5 @@ +TRUNCATE TABLE twitch_client_tokens; + +ALTER TABLE twitch_client_tokens ADD COLUMN token_type VARCHAR(255) NOT NULL default 'VIEWER'; + +DROP TABLE twitch_event_sub_streamers; \ No newline at end of file From 23ce0454c9aa8e18ec2b12bb629000def11d935e Mon Sep 17 00:00:00 2001 From: Antoine PRONNIER Date: Mon, 23 Dec 2024 22:21:25 +0100 Subject: [PATCH 6/6] Fix tests --- .../services/TwitchClientTokenService.java | 2 ++ .../resources/TwitchAuthResourceTest.java | 2 +- ...itchReferenceChannelPointsServiceTest.java | 34 +------------------ 3 files changed, 4 insertions(+), 34 deletions(-) diff --git a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/services/TwitchClientTokenService.java b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/services/TwitchClientTokenService.java index ad32cd47..4d72c786 100644 --- a/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/services/TwitchClientTokenService.java +++ b/modules/twitch/auth/service/src/main/java/com/funixproductions/api/twitch/auth/service/services/TwitchClientTokenService.java @@ -115,6 +115,7 @@ public FrontOrigins registerNewAuthorizationAuthToken(final String oAuthCode, fi if (csrfUser == null) { throw new ApiBadRequestException("Le csrf token est invalide. Veuillez vous reconnecter avec twitch."); } + final UserDTO userDTO = csrfUser.getUser(); final TwitchClientTokenType tokenType = csrfUser.getTokenType(); final Optional searchToken = this.twitchClientTokenRepository.findTwitchClientTokenByUserUuidAndTokenType( @@ -306,6 +307,7 @@ private TwitchClientToken generateNewAccessToken(final CsrfUser csrfUser, final twitchClientToken.setOAuthCode(oAuthToken); twitchClientToken.setAccessToken(tokenResponseDTO.getAccessToken()); twitchClientToken.setRefreshToken(tokenResponseDTO.getRefreshToken()); + twitchClientToken.setTokenType(csrfUser.getTokenType()); twitchClientToken.setExpirationDateToken(Date.from(Instant.now().plusSeconds(tokenResponseDTO.getExpiresIn() - 60L))); return this.twitchClientTokenRepository.save(twitchClientToken); diff --git a/modules/twitch/auth/service/src/test/java/com/funixproductions/api/twitch/auth/service/resources/TwitchAuthResourceTest.java b/modules/twitch/auth/service/src/test/java/com/funixproductions/api/twitch/auth/service/resources/TwitchAuthResourceTest.java index b1300a4e..3d3fd720 100644 --- a/modules/twitch/auth/service/src/test/java/com/funixproductions/api/twitch/auth/service/resources/TwitchAuthResourceTest.java +++ b/modules/twitch/auth/service/src/test/java/com/funixproductions/api/twitch/auth/service/resources/TwitchAuthResourceTest.java @@ -127,7 +127,7 @@ void testTwitchCallbackRoute() throws Exception { mockMvc.perform(get("/twitch/auth/cb?code=dfggopaizepoaznqsdlb&state=codeAuPifNonValide")).andExpect(status().isBadRequest()); result = mockMvc.perform(get("/twitch/auth/cb?error=error_custom&error_description=Une erreur custom faite exprès.")) - .andExpect(status().isOk()) + .andExpect(status().isUnauthorized()) .andReturn(); responseCallback = result.getResponse().getContentAsString(); assertTrue(responseCallback.contains("erreur")); diff --git a/modules/twitch/reference/service/src/test/java/com/funixproductions/api/twitch/reference/service/services/TwitchReferenceChannelPointsServiceTest.java b/modules/twitch/reference/service/src/test/java/com/funixproductions/api/twitch/reference/service/services/TwitchReferenceChannelPointsServiceTest.java index f285d41a..1d65f613 100644 --- a/modules/twitch/reference/service/src/test/java/com/funixproductions/api/twitch/reference/service/services/TwitchReferenceChannelPointsServiceTest.java +++ b/modules/twitch/reference/service/src/test/java/com/funixproductions/api/twitch/reference/service/services/TwitchReferenceChannelPointsServiceTest.java @@ -51,7 +51,7 @@ void setupMocks() { twitchClientTokenDTO.setExpirationDateToken(Date.from(Instant.now().plusSeconds(1000))); when(internalAuthClient.fetchServerToken()).thenReturn(UUID.randomUUID().toString()); - when(internalAuthClient.fetchToken(anyString())).thenReturn(twitchClientTokenDTO); + when(internalAuthClient.fetchToken(anyString(), anyString())).thenReturn(twitchClientTokenDTO); } @Test @@ -68,38 +68,6 @@ void testThrow400() { assertThrows(ApiBadRequestException.class, () -> service.getChannelRewards("token", "10")); } - @Test - void testThrow401() { - final FeignException exception = new FeignException.Unauthorized("test error", mockRequest(), null, null); - when(client.getChannelRewards(anyString(), anyString())).thenThrow(exception); - - assertThrows(ApiBadRequestException.class, () -> service.getChannelRewards("token", "10")); - } - - @Test - void testThrow403() { - final FeignException exception = new FeignException.Forbidden("test error", mockRequest(), null, null); - when(client.getChannelRewards(anyString(), anyString())).thenThrow(exception); - - assertThrows(ApiBadRequestException.class, () -> service.getChannelRewards("token", "10")); - } - - @Test - void testThrow404() { - final FeignException exception = new FeignException.NotFound("test error", mockRequest(), null, null); - when(client.getChannelRewards(anyString(), anyString())).thenThrow(exception); - - assertThrows(ApiBadRequestException.class, () -> service.getChannelRewards("token", "10")); - } - - @Test - void testThrow429() { - final FeignException exception = new FeignException.TooManyRequests("test error", mockRequest(), null, null); - when(client.getChannelRewards(anyString(), anyString())).thenThrow(exception); - - assertThrows(ApiBadRequestException.class, () -> service.getChannelRewards("token", "10")); - } - private Request mockRequest() { return Request.create(Request.HttpMethod.GET, "mockUrl", new HashMap<>(), null, new RequestTemplate()); }