-
Notifications
You must be signed in to change notification settings - Fork 2
feat: 노션 이미지 url 저장방식 변경 - #202 #204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ee4cb57
5dd9e26
bf70c51
d080bf5
810cb94
f360cd2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package com.permitseoul.permitserver.domain.admin.timetable.base.core.exception; | ||
|
|
||
| import com.permitseoul.permitserver.domain.admin.base.AdminBaseException; | ||
|
|
||
| public class NotionPublicUrlNotFoundException extends AdminBaseException { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package com.permitseoul.permitserver.domain.admin.timetable.base.core.exception; | ||
|
|
||
| import com.permitseoul.permitserver.domain.admin.base.AdminBaseException; | ||
|
|
||
| public class NotionUrlMalformedException extends AdminBaseException { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,19 @@ | ||
| package com.permitseoul.permitserver.domain.admin.timetable.block.core.strategy.impl; | ||
|
|
||
| import com.permitseoul.permitserver.domain.admin.timetable.base.core.exception.NotionUrlMalformedException; | ||
| import com.permitseoul.permitserver.domain.admin.timetable.block.api.dto.NotionTimetableBlockUpdateWebhookRequest; | ||
| import com.permitseoul.permitserver.domain.admin.timetable.block.core.strategy.domain.NotionTimetableBlockWebhookType; | ||
| import com.permitseoul.permitserver.domain.admin.timetable.block.core.strategy.NotionTimetableBlockUpdateWebhookStrategy; | ||
| import com.permitseoul.permitserver.domain.admin.timetable.blockmedia.core.component.AdminTimetableBlockMediaSaver; | ||
| import com.permitseoul.permitserver.domain.admin.timetable.blockmedia.core.component.AdminTimetableBlockMediaRemover; | ||
| import com.permitseoul.permitserver.domain.admin.util.NotionImageUrlUtil; | ||
| import com.permitseoul.permitserver.domain.eventtimetable.block.core.component.AdminTimetableBlockRetriever; | ||
| import com.permitseoul.permitserver.domain.eventtimetable.block.core.domain.TimetableBlock; | ||
| import com.permitseoul.permitserver.domain.eventtimetable.blockmedia.domain.entity.TimetableBlockMediaEntity; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| import java.net.MalformedURLException; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
|
|
@@ -30,6 +33,17 @@ public NotionTimetableBlockWebhookType getType() { | |
| @Override | ||
| public void updateNotionTimetableBlockByNotionWebhook(final NotionTimetableBlockUpdateWebhookRequest request) { | ||
| final String rowId = request.data().id(); | ||
| final String publicUrl = request.data().publicUrl(); | ||
|
|
||
| final String host; | ||
| try { | ||
| host = new java.net.URL(publicUrl).getHost(); | ||
| } catch (MalformedURLException e) { | ||
| throw new NotionUrlMalformedException(); | ||
| } | ||
|
Comment on lines
+36
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. host 값 검증과 코드 중복을 확인하세요. 두 가지 개선이 필요합니다:
🔎 제안하는 개선 방안방안 1: Host 검증 추가 final String host;
try {
host = new java.net.URL(publicUrl).getHost();
+ if (host == null || host.isBlank()) {
+ throw new NotionUrlMalformedException();
+ }
} catch (MalformedURLException e) {
throw new NotionUrlMalformedException();
}방안 2: 공통 메서드 추출 (선호)
public static String extractHostFromPublicUrl(final String publicUrl) {
if (publicUrl == null || publicUrl.isBlank()) {
throw new NotionPublicUrlNotFoundException();
}
try {
final String host = new URL(publicUrl).getHost();
if (host == null || host.isBlank()) {
throw new NotionUrlMalformedException();
}
return host;
} catch (MalformedURLException e) {
throw new NotionUrlMalformedException();
}
}그리고 이 메서드를 🤖 Prompt for AI Agents |
||
| if (host == null) { | ||
| throw new NotionUrlMalformedException(); | ||
| } | ||
|
|
||
| final TimetableBlock block = adminTimetableBlockRetriever.findTimetableBlockByNotionTimetableBlockRowId(rowId); | ||
| final long timetableBlockId = block.getTimetableBlockId(); | ||
|
|
@@ -42,16 +56,17 @@ public void updateNotionTimetableBlockByNotionWebhook(final NotionTimetableBlock | |
|
|
||
| int sequence = 0; | ||
| final List<TimetableBlockMediaEntity> medias = new ArrayList<>(); | ||
| for (NotionTimetableBlockUpdateWebhookRequest.NotionFileValue file : mediaProp.files()) { | ||
| String url = null; | ||
| if (file.file() != null && file.file().url() != null) { | ||
| url = file.file().url(); | ||
| } | ||
| if (url == null || url.isBlank()) { | ||
| continue; | ||
| } | ||
|
|
||
| medias.add(TimetableBlockMediaEntity.create(timetableBlockId, sequence++, url)); | ||
|
|
||
| for (NotionTimetableBlockUpdateWebhookRequest.NotionFileValue fileItem : mediaProp.files()) { | ||
| if (fileItem == null || fileItem.file() == null) continue; | ||
|
|
||
| final String original = fileItem.file().url(); | ||
| if (original == null || original.isBlank()) continue; | ||
|
|
||
| final String proxyUrl = NotionImageUrlUtil.buildProxyUrl(host, rowId, original); | ||
| if (proxyUrl == null || proxyUrl.isBlank()) continue; | ||
|
|
||
| medias.add(TimetableBlockMediaEntity.create(timetableBlockId, sequence++, proxyUrl)); | ||
| } | ||
| adminTimetableBlockMediaSaver.saveAllBlockMedia(medias); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package com.permitseoul.permitserver.domain.admin.util; | ||
|
|
||
| import lombok.AccessLevel; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| import java.net.URLEncoder; | ||
| import java.nio.charset.StandardCharsets; | ||
|
|
||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||
| public final class NotionImageUrlUtil { | ||
|
|
||
| private static final String HTTPS = "https://"; | ||
| private static final String IMAGE_PATH = "/image/"; | ||
| private static final String QUERY_PREFIX = "?table=block&id="; | ||
| private static final String CACHE_SUFFIX = "&cache=v2"; | ||
|
|
||
| public static String buildProxyUrl(final String publishedHost, | ||
| final String pageId, | ||
| final String originalUrl) { | ||
| if (publishedHost == null || publishedHost.isBlank()) return null; | ||
| if (pageId == null || pageId.isBlank()) return null; | ||
| if (originalUrl == null || originalUrl.isBlank()) return null; | ||
|
|
||
| // presigned query 제거하는 과정 | ||
| final String baseUrl = originalUrl.split("\\?")[0]; | ||
| return HTTPS + publishedHost | ||
| + IMAGE_PATH + encodeURIComponent(baseUrl) | ||
| + QUERY_PREFIX + pageId | ||
| + CACHE_SUFFIX; | ||
| } | ||
|
|
||
| private static String encodeURIComponent(final String s) { | ||
| String encoded = URLEncoder.encode(s, StandardCharsets.UTF_8); | ||
| return encoded.replace("+", "%20") | ||
| .replace("%21", "!") | ||
| .replace("%27", "'") | ||
| .replace("%28", "(") | ||
| .replace("%29", ")") | ||
| .replace("%2A", "*") | ||
| .replace("%7E", "~"); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,9 @@ public record NotionTimetableDatasourceResponse( | |
| public record NotionPage( | ||
| String id, | ||
| Parent parent, | ||
| NotionProperties properties | ||
| NotionProperties properties, | ||
| @JsonProperty("public_url") | ||
| String publicUrl | ||
|
Comment on lines
+13
to
+15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: rg -n -A5 -B5 "publicUrl|public_url" --type javaRepository: PERMIT-SEOUL/permit-server Length of output: 10825 NotionTimetableBlockMediaUpdateStrategyImpl에서 publicUrl null 체크 누락
🤖 Prompt for AI Agents |
||
| ) {} | ||
|
|
||
| public record Parent( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -88,6 +88,7 @@ public enum ErrorCode implements ApiCode { | |
| NOT_FOUND_NOTION_DATABASE_SOURCE(HttpStatus.NOT_FOUND, 40427, "Notion 데이터베이스 소스를 찾을 수 없습니다."), | ||
| NOT_FOUND_COUPON(HttpStatus.NOT_FOUND, 40428, "coupon을 찾을 수 없습니다."), | ||
| NOT_FOUND_SITE_MAP_IMAGE(HttpStatus.NOT_FOUND, 40429, "sitemap image를 찾을 수 없습니다."), | ||
| NOT_FOUND_NOTION_PUBLIC_ID(HttpStatus.NOT_FOUND, 40430, "public url을 찾을 수 없습니다."), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 에러 메시지 수정 필요. 에러 코드는 🔎 수정 제안- NOT_FOUND_NOTION_PUBLIC_ID(HttpStatus.NOT_FOUND, 40430, "public url을 찾을 수 있습니다."),
+ NOT_FOUND_NOTION_PUBLIC_ID(HttpStatus.NOT_FOUND, 40430, "public url을 찾을 수 없습니다."),
🤖 Prompt for AI Agents |
||
|
|
||
|
|
||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.