diff --git a/src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/techlog/query/JdbcCatalogQueryAdapter.java b/src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/techlog/query/JdbcCatalogQueryAdapter.java
index d7eb6c2..5b11fae 100644
--- a/src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/techlog/query/JdbcCatalogQueryAdapter.java
+++ b/src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/techlog/query/JdbcCatalogQueryAdapter.java
@@ -68,11 +68,25 @@ public class JdbcCatalogQueryAdapter implements CatalogQueryPort {
.list();
}
+ /**
+ * 프로젝트의 공개 경로를 함께 싣는다.
+ *
+ *
여기서 {@code publicPath} 를 늘 null 로 두었더니 Decision 의 즉시 미리보기가 어떤 문서에서도 열리지 않았다. Decision 의 공개
+ * 주소는 자기 slug 가 아니라 {@code <프로젝트 경로>/decisions#} 라, 렌더 모델이 프로젝트 경로를 요구한다 — 그것이 비어 있으면
+ * "PROJECT public path is required" 로 미리보기 전체가 멈춘다. 화면에는 무엇이 모자란지 나오지 않는다.
+ *
+ * 게시되지 않은 프로젝트는 여전히 null 이다. 그때는 공개 주소가 실제로 없고, 없는 주소를 지어내면 미리보기가 보여 주는 링크가 게시 뒤에 달라진다.
+ */
private List searchProjects(String pattern, int limit) {
return jdbcClient
.sql(
- "SELECT id, name, updated_at FROM project "
- + "WHERE lower(name) LIKE :pattern ORDER BY name LIMIT :limit")
+ "SELECT pr.id, pr.name, pr.updated_at, p.navigation_path"
+ + " FROM project pr"
+ + " LEFT JOIN public_resource_projection p"
+ + " ON p.resource_type = 'PROJECT' AND p.resource_id = pr.id"
+ + " AND "
+ + PUBLICLY_VISIBLE
+ + " WHERE lower(pr.name) LIKE :pattern ORDER BY pr.name LIMIT :limit")
.param("pattern", pattern)
.param("limit", limit)
.query(
@@ -82,7 +96,7 @@ public class JdbcCatalogQueryAdapter implements CatalogQueryPort {
CatalogEntryType.PROJECT,
rs.getString("name"),
"PROJECT",
- null,
+ rs.getString("navigation_path"),
"project:" + rs.getTimestamp("updated_at").toInstant()))
.list();
}
diff --git a/src/adapter/outbound/persistence-jpa/src/postgresqlIntegrationTest/java/dev/caskeleton/adapter/outbound/persistence/techlog/management/ManagementPersistenceIntegrationTest.java b/src/adapter/outbound/persistence-jpa/src/postgresqlIntegrationTest/java/dev/caskeleton/adapter/outbound/persistence/techlog/management/ManagementPersistenceIntegrationTest.java
index f987730..ea7d843 100644
--- a/src/adapter/outbound/persistence-jpa/src/postgresqlIntegrationTest/java/dev/caskeleton/adapter/outbound/persistence/techlog/management/ManagementPersistenceIntegrationTest.java
+++ b/src/adapter/outbound/persistence-jpa/src/postgresqlIntegrationTest/java/dev/caskeleton/adapter/outbound/persistence/techlog/management/ManagementPersistenceIntegrationTest.java
@@ -785,4 +785,34 @@ class ManagementPersistenceIntegrationTest {
.single())
.isZero();
}
+
+ /**
+ * Decision 의 공개 주소는 자기 slug 가 아니라 프로젝트 주소 아래에 있다. 그래서 편집기가 Decision 을 미리 그리려면 이 목록이 프로젝트의 공개 경로를
+ * 알고 있어야 한다 — 한동안 늘 null 이었고, 그동안 Decision 은 어떤 문서에서도 즉시 미리보기가 열리지 않았다.
+ */
+ @Test
+ void projectCatalogCarriesThePublicPathOfPublishedProjectsOnly() {
+ ProjectEditView open = projects.create(new CreateProjectCommand("Catalog Published", "test"));
+ jdbcClient
+ .sql("UPDATE project SET slug = 'catalog-published' WHERE id = :id")
+ .param("id", open.id())
+ .update();
+ ProjectEditView loaded = projects.find(open.id()).orElseThrow();
+ assertThat(projects.publish(loaded.id(), loaded.version(), "PUBLIC", "test")).isPresent();
+
+ ProjectEditView hidden = projects.create(new CreateProjectCommand("Catalog Hidden", "test"));
+
+ var entries = catalog.search(CatalogEntryType.PROJECT, "catalog", null, 50).items();
+
+ assertThat(entries)
+ .filteredOn(entry -> entry.id().equals(open.id()))
+ .singleElement()
+ .satisfies(
+ entry -> assertThat(entry.publicPath()).isEqualTo("/projects/catalog-published"));
+ // 게시되지 않은 프로젝트는 공개 주소가 실제로 없다. 지어내면 미리보기가 보여 준 링크가 게시 뒤에 달라진다.
+ assertThat(entries)
+ .filteredOn(entry -> entry.id().equals(hidden.id()))
+ .singleElement()
+ .satisfies(entry -> assertThat(entry.publicPath()).isNull());
+ }
}