diff --git a/app/src/main/java/com/swoosh/microblog/data/api/GhostApiService.kt b/app/src/main/java/com/swoosh/microblog/data/api/GhostApiService.kt index cd41155..f3d240c 100644 --- a/app/src/main/java/com/swoosh/microblog/data/api/GhostApiService.kt +++ b/app/src/main/java/com/swoosh/microblog/data/api/GhostApiService.kt @@ -1,5 +1,7 @@ package com.swoosh.microblog.data.api +import com.swoosh.microblog.data.model.PageWrapper +import com.swoosh.microblog.data.model.PagesResponse import com.swoosh.microblog.data.model.PostWrapper import com.swoosh.microblog.data.model.PostsResponse import okhttp3.MultipartBody @@ -40,6 +42,28 @@ interface GhostApiService { @GET("ghost/api/admin/users/me/") suspend fun getCurrentUser(): Response + // --- Pages --- + + @GET("ghost/api/admin/pages/") + suspend fun getPages( + @Query("limit") limit: String = "all", + @Query("formats") formats: String = "html,plaintext,mobiledoc" + ): Response + + @POST("ghost/api/admin/pages/") + @Headers("Content-Type: application/json") + suspend fun createPage(@Body body: PageWrapper): Response + + @PUT("ghost/api/admin/pages/{id}/") + @Headers("Content-Type: application/json") + suspend fun updatePage( + @Path("id") id: String, + @Body body: PageWrapper + ): Response + + @DELETE("ghost/api/admin/pages/{id}/") + suspend fun deletePage(@Path("id") id: String): Response + @Multipart @POST("ghost/api/admin/images/upload/") suspend fun uploadImage( diff --git a/app/src/main/java/com/swoosh/microblog/data/model/PageModels.kt b/app/src/main/java/com/swoosh/microblog/data/model/PageModels.kt new file mode 100644 index 0000000..4524b67 --- /dev/null +++ b/app/src/main/java/com/swoosh/microblog/data/model/PageModels.kt @@ -0,0 +1,26 @@ +package com.swoosh.microblog.data.model + +data class PagesResponse( + val pages: List, + val meta: Meta? +) + +data class PageWrapper( + val pages: List +) + +data class GhostPage( + val id: String? = null, + val title: String? = null, + val slug: String? = null, + val url: String? = null, + val html: String? = null, + val plaintext: String? = null, + val mobiledoc: String? = null, + val status: String? = null, + val feature_image: String? = null, + val custom_excerpt: String? = null, + val created_at: String? = null, + val updated_at: String? = null, + val published_at: String? = null +) diff --git a/app/src/test/java/com/swoosh/microblog/data/model/PageModelsTest.kt b/app/src/test/java/com/swoosh/microblog/data/model/PageModelsTest.kt new file mode 100644 index 0000000..e3a704c --- /dev/null +++ b/app/src/test/java/com/swoosh/microblog/data/model/PageModelsTest.kt @@ -0,0 +1,162 @@ +package com.swoosh.microblog.data.model + +import com.google.gson.Gson +import org.junit.Assert.* +import org.junit.Test + +class PageModelsTest { + + private val gson = Gson() + + // --- GhostPage defaults --- + + @Test + fun `GhostPage all fields default to null`() { + val page = GhostPage() + assertNull(page.id) + assertNull(page.title) + assertNull(page.slug) + assertNull(page.url) + assertNull(page.html) + assertNull(page.plaintext) + assertNull(page.mobiledoc) + assertNull(page.status) + assertNull(page.feature_image) + assertNull(page.custom_excerpt) + assertNull(page.created_at) + assertNull(page.updated_at) + assertNull(page.published_at) + } + + @Test + fun `GhostPage stores all fields correctly`() { + val page = GhostPage( + id = "page-1", + title = "About", + slug = "about", + url = "https://blog.example.com/about/", + html = "

About us

", + plaintext = "About us", + mobiledoc = """{"version":"0.3.1"}""", + status = "published", + feature_image = "https://blog.example.com/img.jpg", + custom_excerpt = "Learn more about us", + created_at = "2024-01-01T00:00:00.000Z", + updated_at = "2024-06-15T12:00:00.000Z", + published_at = "2024-01-02T00:00:00.000Z" + ) + assertEquals("page-1", page.id) + assertEquals("About", page.title) + assertEquals("about", page.slug) + assertEquals("https://blog.example.com/about/", page.url) + assertEquals("

About us

", page.html) + assertEquals("About us", page.plaintext) + assertEquals("published", page.status) + assertEquals("https://blog.example.com/img.jpg", page.feature_image) + assertEquals("Learn more about us", page.custom_excerpt) + assertEquals("2024-01-01T00:00:00.000Z", page.created_at) + assertEquals("2024-06-15T12:00:00.000Z", page.updated_at) + assertEquals("2024-01-02T00:00:00.000Z", page.published_at) + } + + // --- GSON serialization --- + + @Test + fun `GhostPage serializes to JSON correctly`() { + val page = GhostPage( + id = "abc123", + title = "Contact", + slug = "contact", + status = "published" + ) + val json = gson.toJson(page) + assertTrue(json.contains("\"id\":\"abc123\"")) + assertTrue(json.contains("\"title\":\"Contact\"")) + assertTrue(json.contains("\"slug\":\"contact\"")) + assertTrue(json.contains("\"status\":\"published\"")) + } + + @Test + fun `GhostPage deserializes from JSON correctly`() { + val json = """{"id":"xyz","title":"FAQ","slug":"faq","status":"draft"}""" + val page = gson.fromJson(json, GhostPage::class.java) + assertEquals("xyz", page.id) + assertEquals("FAQ", page.title) + assertEquals("faq", page.slug) + assertEquals("draft", page.status) + } + + @Test + fun `GhostPage deserializes with missing optional fields`() { + val json = """{"id":"test"}""" + val page = gson.fromJson(json, GhostPage::class.java) + assertEquals("test", page.id) + assertNull(page.title) + assertNull(page.slug) + assertNull(page.html) + assertNull(page.status) + } + + // --- PagesResponse --- + + @Test + fun `PagesResponse deserializes with pages and pagination`() { + val json = """{ + "pages": [{"id": "1", "title": "About"}, {"id": "2", "title": "Contact"}], + "meta": {"pagination": {"page": 1, "limit": 15, "pages": 1, "total": 2, "next": null, "prev": null}} + }""" + val response = gson.fromJson(json, PagesResponse::class.java) + assertEquals(2, response.pages.size) + assertEquals("1", response.pages[0].id) + assertEquals("About", response.pages[0].title) + assertEquals("2", response.pages[1].id) + assertEquals("Contact", response.pages[1].title) + assertEquals(1, response.meta?.pagination?.page) + assertEquals(2, response.meta?.pagination?.total) + assertNull(response.meta?.pagination?.next) + } + + @Test + fun `PagesResponse deserializes with empty pages list`() { + val json = """{"pages": [], "meta": null}""" + val response = gson.fromJson(json, PagesResponse::class.java) + assertTrue(response.pages.isEmpty()) + assertNull(response.meta) + } + + // --- PageWrapper --- + + @Test + fun `PageWrapper wraps pages for API request`() { + val wrapper = PageWrapper(listOf(GhostPage(title = "New Page", status = "draft"))) + val json = gson.toJson(wrapper) + assertTrue(json.contains("\"pages\"")) + assertTrue(json.contains("\"New Page\"")) + } + + @Test + fun `PageWrapper serializes single page correctly`() { + val page = GhostPage( + title = "About Us", + slug = "about-us", + status = "published", + mobiledoc = """{"version":"0.3.1","atoms":[],"cards":[],"markups":[],"sections":[[1,"p",[[0,[],0,"Welcome"]]]]}""" + ) + val wrapper = PageWrapper(listOf(page)) + val json = gson.toJson(wrapper) + assertTrue(json.contains("\"title\":\"About Us\"")) + assertTrue(json.contains("\"slug\":\"about-us\"")) + assertTrue(json.contains("\"status\":\"published\"")) + } + + @Test + fun `GhostPage updated_at is preserved for PUT requests`() { + val page = GhostPage( + id = "page-1", + title = "Updated Title", + updated_at = "2024-06-15T12:00:00.000Z" + ) + val json = gson.toJson(page) + assertTrue(json.contains("\"updated_at\":\"2024-06-15T12:00:00.000Z\"")) + } +}