in-370-외부-api-호출-bulkhead-패턴-사용 -> develop#223
Hidden character warning
Conversation
Walkthrough외부 API 클라이언트에 Resilience4j 제한과 fallback 예외 매핑을 적용하고, YouTube 분석 및 업로드 간격 계산을 가상 스레드 기반 비동기 처리로 전환했습니다. 관련 설정과 동시성·예외 처리 테스트도 추가 또는 수정했습니다. Changes외부 API 제한 및 병렬 처리
VideoServiceTest 호출 방식 변경
AnalyticsCalculatorTest 인자 수정
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Caller
participant YoutubeDataApiClient
participant Resilience4j
participant ExternalApiLimitExceptionMapper
Caller->>YoutubeDataApiClient: getYoutubeChannels(...)
YoutubeDataApiClient->>Resilience4j: Bulkhead/RateLimiter 검사
Resilience4j-->>YoutubeDataApiClient: 제한 예외
YoutubeDataApiClient->>ExternalApiLimitExceptionMapper: 예외 변환
ExternalApiLimitExceptionMapper-->>Caller: ApiException(EXTERNAL_API_RATE_LIMITED)
sequenceDiagram
participant Service as YoutubeChannelAnalyticsFetchService
participant Executor as externalApiExecutor
participant ApiClient as YoutubeAnalyticsApiClient
Service->>Executor: 영상 분석 비동기 배치 실행
Executor->>ApiClient: getYoutubeAnalytics(...)
ApiClient-->>Executor: 응답 또는 예외
Executor-->>Service: join 결과
Service->>Service: 결과 병합
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
src/main/java/com/example/inflace/global/client/YoutubeAnalyticsApiClient.java (1)
98-107: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueFallback 로그 메시지가 API 호출 실패 상황에서 오해의 소지가 있습니다.
youtubeAnalyticsFallback은 Bulkhead/RateLimiter 거부뿐 아니라 API 호출 자체가 실패(4xx/5xx)한 경우에도 호출됩니다. 현재 로그 메시지 "blocked or failed before execution"는 후자의 경우 실제로는 API 호출이 실행되었음에도 "before execution"으로 표현되어 디버깅 시 혼란을 줄 수 있습니다.🔧 제안: 로그 메시지 개선
log.warn("YouTube Analytics API call blocked or failed before execution. googleId={} metrics={} dimensions={}", googleId, request.formattedMetricsList(), request.dimensions(), throwable);- log.warn("YouTube Analytics API call blocked or failed before execution. googleId={} metrics={} dimensions={}", - googleId, request.formattedMetricsList(), request.dimensions(), throwable); + log.warn("YouTube Analytics API call rejected or failed. googleId={} metrics={} dimensions={}", + googleId, request.formattedMetricsList(), request.dimensions(), throwable);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/example/inflace/global/client/YoutubeAnalyticsApiClient.java` around lines 98 - 107, The fallback log message in youtubeAnalyticsFallback is misleading because it says the call was “blocked or failed before execution” even when the API request actually ran and then failed. Update the log text in YoutubeAnalyticsApiClient.youtubeAnalyticsFallback to clearly distinguish pre-execution rejections from downstream API failures, while still logging googleId, request.formattedMetricsList(), request.dimensions(), and the throwable for debugging.src/main/java/com/example/inflace/domain/channel/service/sync/YoutubeChannelAnalyticsFetchService.java (1)
613-619: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
chunked메서드가YoutubeDataApiClient에도 동일하게 존재하여 중복됩니다.그래프 컨텍스트에 따르면
YoutubeDataApiClient.java의 613-619번 라인에 동일한 구현의 privatechunked메서드가 있습니다. 두 클래스에서 같은 유틸리티 로직이 중복되고 있으므로 공용 유틸 클래스로 추출을 고려해 보세요.♻️ 제안: 공용 유틸 클래스로 추출
- private <T> List<List<T>> chunked(List<T> values, int size) { - List<List<T>> chunks = new ArrayList<>(); - for (int start = 0; start < values.size(); start += size) { - chunks.add(values.subList(start, Math.min(values.size(), start + size))); - } - return chunks; - }// 예: src/main/java/com/example/inflace/global/util/ListUtils.java public final class ListUtils { private ListUtils() {} public static <T> List<List<T>> chunked(List<T> values, int size) { List<List<T>> chunks = new ArrayList<>(); for (int start = 0; start < values.size(); start += size) { chunks.add(values.subList(start, Math.min(values.size(), start + size))); } return chunks; } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/example/inflace/domain/channel/service/sync/YoutubeChannelAnalyticsFetchService.java` around lines 613 - 619, `chunked` 로직이 `YoutubeChannelAnalyticsFetchService`와 `YoutubeDataApiClient`에 중복되어 있으니, 두 곳의 private 메서드를 공용 유틸로 합치세요. `chunked`를 `ListUtils` 같은 재사용 가능한 클래스의 static 메서드로 추출하고, `YoutubeChannelAnalyticsFetchService`와 `YoutubeDataApiClient`는 그 공용 메서드를 호출하도록 변경해 중복 구현을 제거하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@build.gradle`:
- Around line 28-29: Update the resilience4j dependency to the latest stable
release by changing the io.github.resilience4j:resilience4j-spring-boot3 version
from 2.2.0 to 2.4.0 in build.gradle. Keep the existing Spring Boot actuator
dependency unchanged and make sure the version bump is applied wherever the
resilience4j artifact is declared.
In
`@src/main/java/com/example/inflace/domain/brandcollaboration/service/BrandCollaborationService.java`:
- Around line 275-294: `computeAvgUploadDays` is waiting indefinitely on YouTube
requests, so add an actual response timeout instead of relying on the
rate-limiter setting alone. Update the async flow around
`CompletableFuture.supplyAsync(...)` and `CompletableFuture::join` to use
`orTimeout()` (or equivalent) for the upload-day computation, and configure the
`RestClient` used by `computeUploadDays` with a read timeout so slow HTTP
responses fail fast. Keep the existing exception handling/logging in
`BrandCollaborationService` to surface timeout failures clearly.
In `@src/main/java/com/example/inflace/global/client/YoutubeDataApiClient.java`:
- Around line 90-91: The current `@RateLimiter` on YoutubeDataApiClient only
limits each method invocation once, so it does not control the individual API
requests made inside the pagination/chunk loops. Review getMyVideoIds and
getYoutubeVideos and move rate limiting to the actual YouTube API call points
inside those loops, or switch to HTTP-client-level throttling, while keeping
`@Bulkhead` on the outer methods for concurrency control. Ensure the
fallbackMethod wiring still works after refactoring and that each remote call is
counted against the limiter rather than the enclosing method.
---
Nitpick comments:
In
`@src/main/java/com/example/inflace/domain/channel/service/sync/YoutubeChannelAnalyticsFetchService.java`:
- Around line 613-619: `chunked` 로직이 `YoutubeChannelAnalyticsFetchService`와
`YoutubeDataApiClient`에 중복되어 있으니, 두 곳의 private 메서드를 공용 유틸로 합치세요. `chunked`를
`ListUtils` 같은 재사용 가능한 클래스의 static 메서드로 추출하고,
`YoutubeChannelAnalyticsFetchService`와 `YoutubeDataApiClient`는 그 공용 메서드를 호출하도록
변경해 중복 구현을 제거하세요.
In
`@src/main/java/com/example/inflace/global/client/YoutubeAnalyticsApiClient.java`:
- Around line 98-107: The fallback log message in youtubeAnalyticsFallback is
misleading because it says the call was “blocked or failed before execution”
even when the API request actually ran and then failed. Update the log text in
YoutubeAnalyticsApiClient.youtubeAnalyticsFallback to clearly distinguish
pre-execution rejections from downstream API failures, while still logging
googleId, request.formattedMetricsList(), request.dimensions(), and the
throwable for debugging.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d20eef6f-4ac0-4735-9e4c-49f7b89a4081
📒 Files selected for processing (15)
build.gradlesrc/main/java/com/example/inflace/domain/brandcollaboration/service/BrandCollaborationService.javasrc/main/java/com/example/inflace/domain/channel/service/sync/YoutubeChannelAnalyticsFetchService.javasrc/main/java/com/example/inflace/global/client/YoutubeAnalyticsApiClient.javasrc/main/java/com/example/inflace/global/client/YoutubeDataApiClient.javasrc/main/java/com/example/inflace/global/client/YoutubeSearchApiClient.javasrc/main/java/com/example/inflace/global/config/ExternalApiExecutorConfig.javasrc/main/java/com/example/inflace/global/exception/ErrorDefine.javasrc/main/java/com/example/inflace/global/exception/ExternalApiLimitExceptionMapper.javasrc/main/java/com/example/inflace/infra/openai/service/OpenAiService.javasrc/main/resources/application.ymlsrc/test/java/com/example/inflace/domain/channel/service/sync/YoutubeChannelAnalyticsFetchServiceTest.javasrc/test/java/com/example/inflace/domain/video/service/VideoServiceTest.javasrc/test/java/com/example/inflace/global/exception/ExternalApiLimitExceptionMapperTest.javasrc/test/java/com/example/inflace/global/util/AnalyticsCalculatorTest.java
| List<CompletableFuture<Double>> futures = channelMap.values().stream() | ||
| .filter(c -> c.contentDetails() != null && c.contentDetails().relatedPlaylists() != null) | ||
| .map(c -> c.contentDetails().relatedPlaylists().uploads()) | ||
| .filter(StringUtils::hasText) | ||
| .mapToDouble(playlistId -> { | ||
| List<Instant> instants = youtubeDataApiClient | ||
| .getRecentUploadDates(playlistId, RECENT_UPLOADS_COUNT) | ||
| .stream().map(Instant::parse).sorted().toList(); | ||
| if (instants.size() < 2) return Double.NaN; | ||
| long spanSeconds = instants.getLast().getEpochSecond() - instants.getFirst().getEpochSecond(); | ||
| return spanSeconds / 86400.0 / (instants.size() - 1); | ||
| }) | ||
| .map(playlistId -> CompletableFuture | ||
| .supplyAsync(() -> computeUploadDays(playlistId), externalApiExecutor) | ||
| .exceptionally(exception -> { | ||
| log.warn("Failed to compute average upload days. playlistId={}", playlistId, exception); | ||
| return Double.NaN; | ||
| })) | ||
| .toList(); | ||
|
|
||
| OptionalDouble avg = futures.stream() | ||
| .map(CompletableFuture::join) | ||
| .mapToDouble(Double::doubleValue) | ||
| .filter(d -> !Double.isNaN(d) && d > 0) | ||
| .average(); | ||
|
|
||
| return avg.isPresent() ? avg.getAsDouble() : null; | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## file list\n'
git ls-files | rg '^src/main/java/com/example/inflace/domain/brandcollaboration/service/BrandCollaborationService\.java$|^src/main/java/.*RestClient.*|^src/main/java/.*YouTube.*|^src/main/java/.*Executor.*|^src/main/java/.*Http.*|^src/main/resources/.*application.*'
printf '\n## outline BrandCollaborationService\n'
ast-grep outline src/main/java/com/example/inflace/domain/brandcollaboration/service/BrandCollaborationService.java --view expanded || true
printf '\n## relevant snippets\n'
sed -n '1,220p' src/main/java/com/example/inflace/domain/brandcollaboration/service/BrandCollaborationService.java
printf '\n--- middle ---\n'
sed -n '220,360p' src/main/java/com/example/inflace/domain/brandcollaboration/service/BrandCollaborationService.java
printf '\n## timeout/search terms\n'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' \
'orTimeout|completeOnTimeout|readTimeout|connectTimeout|timeout|RestClient|WebClient|HttpClient|ExecutorService|externalApiExecutor|computeUploadDays|analyzeTrends' \
src/main/java src/main/resourcesRepository: JECT-Study/inflace-4th-server
Length of output: 25367
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## BrandCollaborationService outline\n'
ast-grep outline src/main/java/com/example/inflace/domain/brandcollaboration/service/BrandCollaborationService.java --view expanded || true
printf '\n## BrandCollaborationService relevant lines\n'
sed -n '240,320p' src/main/java/com/example/inflace/domain/brandcollaboration/service/BrandCollaborationService.java
printf '\n## search for timeout and async config\n'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' \
'orTimeout|completeOnTimeout|readTimeout|connectTimeout|timeout|externalApiExecutor|computeUploadDays|analyzeTrends|RestClient|WebClient|HttpClient|ExecutorService' \
src/main/java src/main/resourcesRepository: JECT-Study/inflace-4th-server
Length of output: 11566
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## RestClientConfig\n'
sed -n '1,120p' src/main/java/com/example/inflace/global/config/RestClientConfig.java
printf '\n## YoutubeDataApiClient\n'
sed -n '1,260p' src/main/java/com/example/inflace/global/client/YoutubeDataApiClient.java
printf '\n## YoutubeSearchApiClient\n'
sed -n '1,220p' src/main/java/com/example/inflace/global/client/YoutubeSearchApiClient.java
printf '\n## GoogleApiClient\n'
sed -n '1,220p' src/main/java/com/example/inflace/global/client/GoogleApiClient.java
printf '\n## any timeout-related config files\n'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' \
'setReadTimeout|setConnectTimeout|readTimeout|connectTimeout|timeout-duration|timeout\(' \
src/main/java src/main/resourcesRepository: JECT-Study/inflace-4th-server
Length of output: 18032
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## RestClientConfig\n'
sed -n '1,120p' src/main/java/com/example/inflace/global/config/RestClientConfig.java
printf '\n## YoutubeDataApiClient\n'
sed -n '1,260p' src/main/java/com/example/inflace/global/client/YoutubeDataApiClient.java
printf '\n## YoutubeSearchApiClient\n'
sed -n '1,220p' src/main/java/com/example/inflace/global/client/YoutubeSearchApiClient.java
printf '\n## GoogleApiClient\n'
sed -n '1,220p' src/main/java/com/example/inflace/global/client/GoogleApiClient.java
printf '\n## timeout-related settings\n'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' \
'setReadTimeout|setConnectTimeout|readTimeout|connectTimeout|timeout-duration|timeout\(' \
src/main/java src/main/resourcesRepository: JECT-Study/inflace-4th-server
Length of output: 18024
computeAvgUploadDays에 응답 제한을 두세요.
CompletableFuture::join()이 RestClient.builder().build()로 만든 YouTube 호출을 그대로 기다립니다. application.yml의 timeout-duration은 rate limiter 대기 제한이라 HTTP 응답 지연을 막지 못하므로, orTimeout()과 RestClient read timeout을 함께 넣어야 합니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/main/java/com/example/inflace/domain/brandcollaboration/service/BrandCollaborationService.java`
around lines 275 - 294, `computeAvgUploadDays` is waiting indefinitely on
YouTube requests, so add an actual response timeout instead of relying on the
rate-limiter setting alone. Update the async flow around
`CompletableFuture.supplyAsync(...)` and `CompletableFuture::join` to use
`orTimeout()` (or equivalent) for the upload-day computation, and configure the
`RestClient` used by `computeUploadDays` with a read timeout so slow HTTP
responses fail fast. Keep the existing exception handling/logging in
`BrandCollaborationService` to surface timeout failures clearly.
| @Bulkhead(name = "youtube-data", fallbackMethod = "youtubeDataStringListFallback") | ||
| @RateLimiter(name = "youtube-data", fallbackMethod = "youtubeDataStringListFallback") |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
RateLimiter가 내부 루프의 개별 API 호출을 정확히 제어하지 못합니다.
getMyVideoIds(라인 92)는 페이지네이션 while 루프에서 API를 여러 번 호출하지만, @RateLimiter는 메서드 호출당 1 permit만 소비합니다. getYoutubeVideos(라인 141)도 청크 루프에서 동일한 패턴입니다.
예시: 500개 영상이 있는 플레이리스트에 getMyVideoIds를 호출하면 10개 API 호출이 발생하지만 rate limiter에는 1 permit만 카운트됩니다. rate limit이 20/s일 때 실제 API 호출 속도는 200/s에 도달할 수 있어 YouTube Data API 할당량(10,000 units/day)을 50초 이내에 소진할 수 있습니다.
@Bulkhead는 동기 호출이므로 동시 HTTP 요청을 16개로 제한하는 데 효과적이지만, @RateLimiter는 버스트 속도를 실제 API 호출 단위가 아닌 메서드 호출 단위로만 제한합니다.
후속 작업에서 HTTP 클라이언트 레벨 rate limiting 또는 루프 내 호출당 rate limiter 적용을 검토하세요.
Also applies to: 139-140
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/java/com/example/inflace/global/client/YoutubeDataApiClient.java`
around lines 90 - 91, The current `@RateLimiter` on YoutubeDataApiClient only
limits each method invocation once, so it does not control the individual API
requests made inside the pagination/chunk loops. Review getMyVideoIds and
getYoutubeVideos and move rate limiting to the actual YouTube API call points
inside those loops, or switch to HTTP-client-level throttling, while keeping
`@Bulkhead` on the outer methods for concurrency control. Ensure the
fallbackMethod wiring still works after refactoring and that each remote call is
counted against the limiter rather than the enclosing method.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
src/main/java/com/example/inflace/global/config/RestClientConfig.java (2)
17-17: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value
SimpleClientHttpRequestFactory는 커넥션 풀링을 지원하지 않습니다.
SimpleClientHttpRequestFactory는 내부적으로HttpURLConnection을 사용하여 요청마다 새 TCP 연결을 생성합니다. 이 PR에서 여러 외부 API 클라이언트가 Bulkhead 기반 가상 스레드 병렬 실행으로 동시 요청을 보내므로, 커넥션 재사용이 성능에 도움이 될 수 있습니다. 프로덕션 환경에서는JdkClientHttpRequestFactory또는HttpComponentsClientHttpRequestFactory(Apache HttpClient 의존성 필요)로 전환을 고려해 보세요.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/example/inflace/global/config/RestClientConfig.java` at line 17, Update RestClientConfig to replace SimpleClientHttpRequestFactory with a connection-pooling request factory, preferably JdkClientHttpRequestFactory or HttpComponentsClientHttpRequestFactory, and configure the corresponding client while preserving the existing RestClient behavior.
12-23: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win타임아웃 값을
application.yml로 외부화하는 것을 권장합니다.이 PR은 Resilience4j Bulkhead/RateLimiter 설정을
application.yml에 외부화하면서 "운영 부하에 따라 제한 값을 조정할 수 있다"는 목표를 밝히고 있습니다. 하지만 RestClient의 connect/read 타임아웃은 상수로 하드코딩되어 있어, 운영 중 타임아웃 조정 시 재배포가 필요합니다. Resilience4j 설정과 일관성 있게@Value또는@ConfigurationProperties로 주입하는 것이 좋습니다.♻️ 제안: application.yml 기반 타임아웃 주입
+import org.springframework.beans.factory.annotation.Value; + `@Configuration` public class RestClientConfig { - private static final Duration CONNECT_TIMEOUT = Duration.ofSeconds(2); - private static final Duration READ_TIMEOUT = Duration.ofSeconds(5); + `@Value`("${rest-client.connect-timeout:2s}") + private Duration connectTimeout; + + `@Value`("${rest-client.read-timeout:5s}") + private Duration readTimeout; `@Bean` public RestClient restClient() { SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); - requestFactory.setConnectTimeout(CONNECT_TIMEOUT); - requestFactory.setReadTimeout(READ_TIMEOUT); + requestFactory.setConnectTimeout(connectTimeout); + requestFactory.setReadTimeout(readTimeout); return RestClient.builder() .requestFactory(requestFactory) .build(); } }
application.yml에 추가:rest-client: connect-timeout: 2s read-timeout: 5s🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/example/inflace/global/config/RestClientConfig.java` around lines 12 - 23, Update RestClientConfig.restClient to read connect and read timeout values from application.yml instead of the hardcoded CONNECT_TIMEOUT and READ_TIMEOUT constants, using `@Value` or a dedicated `@ConfigurationProperties` binding for the rest-client settings. Preserve the existing SimpleClientHttpRequestFactory configuration while allowing both durations to be changed through external configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/main/java/com/example/inflace/global/config/RestClientConfig.java`:
- Line 17: Update RestClientConfig to replace SimpleClientHttpRequestFactory
with a connection-pooling request factory, preferably
JdkClientHttpRequestFactory or HttpComponentsClientHttpRequestFactory, and
configure the corresponding client while preserving the existing RestClient
behavior.
- Around line 12-23: Update RestClientConfig.restClient to read connect and read
timeout values from application.yml instead of the hardcoded CONNECT_TIMEOUT and
READ_TIMEOUT constants, using `@Value` or a dedicated `@ConfigurationProperties`
binding for the rest-client settings. Preserve the existing
SimpleClientHttpRequestFactory configuration while allowing both durations to be
changed through external configuration.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 81daf1ab-650e-4b8e-b277-0d4846f4454f
📒 Files selected for processing (6)
build.gradlesrc/main/java/com/example/inflace/domain/brandcollaboration/service/BrandCollaborationService.javasrc/main/java/com/example/inflace/domain/channel/service/sync/YoutubeChannelAnalyticsFetchService.javasrc/main/java/com/example/inflace/global/config/RestClientConfig.javasrc/main/resources/application.ymlsrc/test/java/com/example/inflace/domain/channel/service/sync/YoutubeChannelAnalyticsFetchServiceTest.java
🚧 Files skipped from review as they are similar to previous changes (5)
- build.gradle
- src/main/resources/application.yml
- src/test/java/com/example/inflace/domain/channel/service/sync/YoutubeChannelAnalyticsFetchServiceTest.java
- src/main/java/com/example/inflace/domain/channel/service/sync/YoutubeChannelAnalyticsFetchService.java
- src/main/java/com/example/inflace/domain/brandcollaboration/service/BrandCollaborationService.java
Issue
comment
Summary by CodeRabbit