diff --git a/migrations/20230714095746_add-default-collection.down.sql b/migrations/20230714095746_add-default-collection.down.sql new file mode 100644 index 0000000..d9b4486 --- /dev/null +++ b/migrations/20230714095746_add-default-collection.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE sites +DROP COLUMN default_collection; diff --git a/migrations/20230714095746_add-default-collection.up.sql b/migrations/20230714095746_add-default-collection.up.sql new file mode 100644 index 0000000..efb4fae --- /dev/null +++ b/migrations/20230714095746_add-default-collection.up.sql @@ -0,0 +1,4 @@ +ALTER TABLE sites +ADD COLUMN default_collection UUID REFERENCES collections ( + id +) ON DELETE SET NULL; diff --git a/sqlx-data.json b/sqlx-data.json index 5616124..1286cf3 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -1,215 +1,6 @@ { "db": "PostgreSQL", - "11e96cfd8c2736f13ce55975ea910dd68640f6f14e38a4b3342d514804e3de27": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Uuid" - ] - } - }, - "query": "DELETE FROM sessions WHERE id = $1" - }, - "1d21e65cb21ec57986f154395627de9aefbf77f88c892c5e093787ec53d623d3": { - "describe": { - "columns": [ - { - "name": "id", - "ordinal": 0, - "type_info": "Uuid" - }, - { - "name": "slug", - "ordinal": 1, - "type_info": "Varchar" - }, - { - "name": "name", - "ordinal": 2, - "type_info": "Varchar" - }, - { - "name": "parent", - "ordinal": 3, - "type_info": "Uuid" - } - ], - "nullable": [ - false, - false, - false, - true - ], - "parameters": { - "Left": [ - "Uuid" - ] - } - }, - "query": "SELECT id, slug, name, parent FROM collections WHERE site = $1" - }, - "28ec2833283df836e457161f44f0fbc75ed37dcc0ba63eb09bf285aae2f7a380": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid", - "Varchar", - "Varchar" - ] - } - }, - "query": "INSERT INTO collections (id, site, slug, name)\n VALUES ($1, $2, $3, $4)" - }, - "29e0259def5c6fdc34c6a18345150d2714736b91c1f3040fcb07d77cbe2552e1": { - "describe": { - "columns": [ - { - "name": "name", - "ordinal": 0, - "type_info": "Varchar" - }, - { - "name": "owner", - "ordinal": 1, - "type_info": "Varchar" - }, - { - "name": "owner_display_name", - "ordinal": 2, - "type_info": "Varchar" - }, - { - "name": "title", - "ordinal": 3, - "type_info": "Varchar" - }, - { - "name": "description", - "ordinal": 4, - "type_info": "Text" - }, - { - "name": "created_at", - "ordinal": 5, - "type_info": "Timestamp" - }, - { - "name": "collections!: Vec", - "ordinal": 6, - "type_info": "RecordArray" - } - ], - "nullable": [ - false, - false, - null, - false, - true, - false, - null - ], - "parameters": { - "Left": [ - "Text" - ] - } - }, - "query": "SELECT\n sites.name,\n users.name as owner,\n COALESCE(users.display_name,users.name) as owner_display_name,\n title,\n description,\n sites.created_at,\n array_agg(row(collections.slug, collections.name)) as \"collections!: Vec\"\n FROM sites\n JOIN\n users ON sites.owner = users.id\n JOIN\n collections ON collections.site = sites.id\n WHERE\n sites.name = $1\n AND sites.deleted_at IS NULL\n GROUP BY\n sites.name, users.name, users.display_name, title, description, sites.created_at" - }, - "35deaad55b84124dcba02c555718fe815003e3f50b1c8ee2fc7e540009fa5aef": { - "describe": { - "columns": [ - { - "name": "empty", - "ordinal": 0, - "type_info": "Bool" - } - ], - "nullable": [ - null - ], - "parameters": { - "Left": [] - } - }, - "query": "SELECT CASE WHEN EXISTS(SELECT 1 FROM users) THEN false ELSE true END AS empty;" - }, - "39237d1e40ce33ce386320212a53986cb21949dd07014772a150242caf4fa610": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid", - "Uuid", - "Varchar", - "Varchar", - "Text", - "VarcharArray", - "Jsonb", - "UuidArray", - "Bool" - ] - } - }, - "query": "INSERT INTO pages (id, author, site, slug, title, description, tags, blocks, collections, published)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)" - }, - "562254387ad52ad5ee3cf806085873485d6b8a0b372ac59a0796b9f0c8910cf2": { - "describe": { - "columns": [ - { - "name": "id", - "ordinal": 0, - "type_info": "Uuid" - } - ], - "nullable": [ - false - ], - "parameters": { - "Left": [ - "Uuid", - "Uuid", - "Varchar", - "Timestamp", - "Timestamp" - ] - } - }, - "query": "INSERT INTO sessions (id, actor, secret, created_at, expires_at) VALUES ($1, $2, $3, $4, $5) RETURNING id" - }, - "61fcf083e6abcfed9480164f3ed4ff2f396c8041a30df002f6e4af920b41f4e4": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Text", - "Uuid" - ] - } - }, - "query": "UPDATE sites SET deleted_at = NOW() WHERE name = $1 AND owner = $2 AND deleted_at IS NULL" - }, - "742b9eef92ad404b3b1ec285ce8b99e945b1b910f25bff376466c704a79fc9c8": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Timestamp" - ] - } - }, - "query": "DELETE FROM sessions WHERE expires_at < $1" - }, - "7aff3e9becb9da6bfba6d50a364fd56f46b739d6e7a5742e5efe1d19858ee427": { + "0e52c31f550313507192bd0436e469dc2b01e67bd19ecdd26e663fbb5af8929b": { "describe": { "columns": [ { @@ -283,14 +74,231 @@ ], "parameters": { "Left": [ - "Text", - "Text", + "Uuid", + "Uuid", "Timestamp", "Int8" ] } }, - "query": "SELECT\n COALESCE(users.display_name,users.name) as author_display_name,\n users.name as author_username,\n pages.slug,\n pages.title,\n pages.description,\n pages.tags,\n pages.blocks as \"blocks!: Json>\",\n pages.created_at,\n pages.modified_at,\n pages.published,\n pages.collections\n FROM pages\n JOIN\n users ON author = users.id\n JOIN\n sites ON site = sites.id\n JOIN\n collections ON collections.slug = $2 AND collections.site = sites.id\n WHERE\n sites.name = $1\n AND collections.id = ANY(collections)\n AND pages.deleted_at IS NULL\n AND published = true\n AND pages.created_at <= $3\n ORDER BY created_at DESC\n LIMIT $4\n " + "query": "SELECT\n COALESCE(users.display_name,users.name) as author_display_name,\n users.name as author_username,\n pages.slug,\n pages.title,\n pages.description,\n pages.tags,\n pages.blocks as \"blocks!: Json>\",\n pages.created_at,\n pages.modified_at,\n pages.published,\n pages.collections\n FROM pages\n JOIN\n users ON author = users.id\n WHERE\n pages.site = $1\n AND $2 = ANY(collections)\n AND pages.deleted_at IS NULL\n AND published = true\n AND pages.created_at <= $3\n ORDER BY created_at DESC\n LIMIT $4\n " + }, + "11e96cfd8c2736f13ce55975ea910dd68640f6f14e38a4b3342d514804e3de27": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Uuid" + ] + } + }, + "query": "DELETE FROM sessions WHERE id = $1" + }, + "1d21e65cb21ec57986f154395627de9aefbf77f88c892c5e093787ec53d623d3": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Uuid" + }, + { + "name": "slug", + "ordinal": 1, + "type_info": "Varchar" + }, + { + "name": "name", + "ordinal": 2, + "type_info": "Varchar" + }, + { + "name": "parent", + "ordinal": 3, + "type_info": "Uuid" + } + ], + "nullable": [ + false, + false, + false, + true + ], + "parameters": { + "Left": [ + "Uuid" + ] + } + }, + "query": "SELECT id, slug, name, parent FROM collections WHERE site = $1" + }, + "35deaad55b84124dcba02c555718fe815003e3f50b1c8ee2fc7e540009fa5aef": { + "describe": { + "columns": [ + { + "name": "empty", + "ordinal": 0, + "type_info": "Bool" + } + ], + "nullable": [ + null + ], + "parameters": { + "Left": [] + } + }, + "query": "SELECT CASE WHEN EXISTS(SELECT 1 FROM users) THEN false ELSE true END AS empty;" + }, + "39237d1e40ce33ce386320212a53986cb21949dd07014772a150242caf4fa610": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Uuid", + "Uuid", + "Uuid", + "Varchar", + "Varchar", + "Text", + "VarcharArray", + "Jsonb", + "UuidArray", + "Bool" + ] + } + }, + "query": "INSERT INTO pages (id, author, site, slug, title, description, tags, blocks, collections, published)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)" + }, + "3b27a97c644fda93d0e9b605b606509b4cf77a1877949d0e614a8725666e7e71": { + "describe": { + "columns": [ + { + "name": "name", + "ordinal": 0, + "type_info": "Varchar" + }, + { + "name": "owner", + "ordinal": 1, + "type_info": "Varchar" + }, + { + "name": "owner_display_name", + "ordinal": 2, + "type_info": "Varchar" + }, + { + "name": "title", + "ordinal": 3, + "type_info": "Varchar" + }, + { + "name": "description", + "ordinal": 4, + "type_info": "Text" + }, + { + "name": "created_at", + "ordinal": 5, + "type_info": "Timestamp" + }, + { + "name": "default_collection", + "ordinal": 6, + "type_info": "Uuid" + }, + { + "name": "collections!: Vec", + "ordinal": 7, + "type_info": "RecordArray" + } + ], + "nullable": [ + false, + false, + null, + false, + true, + false, + true, + null + ], + "parameters": { + "Left": [ + "Text" + ] + } + }, + "query": "SELECT\n sites.name,\n users.name as owner,\n COALESCE(users.display_name,users.name) as owner_display_name,\n title,\n description,\n sites.created_at,\n default_collection,\n array_agg(row(collections.id, collections.slug, collections.name, parent)) as \"collections!: Vec\"\n FROM sites\n JOIN\n users ON sites.owner = users.id\n JOIN\n collections ON collections.site = sites.id\n WHERE\n sites.name = $1\n AND sites.deleted_at IS NULL\n GROUP BY\n sites.name, users.name, users.display_name, title, description, sites.created_at, default_collection" + }, + "562254387ad52ad5ee3cf806085873485d6b8a0b372ac59a0796b9f0c8910cf2": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Uuid" + } + ], + "nullable": [ + false + ], + "parameters": { + "Left": [ + "Uuid", + "Uuid", + "Varchar", + "Timestamp", + "Timestamp" + ] + } + }, + "query": "INSERT INTO sessions (id, actor, secret, created_at, expires_at) VALUES ($1, $2, $3, $4, $5) RETURNING id" + }, + "61fcf083e6abcfed9480164f3ed4ff2f396c8041a30df002f6e4af920b41f4e4": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Text", + "Uuid" + ] + } + }, + "query": "UPDATE sites SET deleted_at = NOW() WHERE name = $1 AND owner = $2 AND deleted_at IS NULL" + }, + "6690726421106e317b58513f5d0a2eb0a38f15e1e89eb10695eb00e8bb3076d9": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Text", + "Uuid", + "Text", + "Uuid" + ] + } + }, + "query": "UPDATE sites SET\n name = COALESCE($1, name),\n title = COALESCE($2, title),\n description = COALESCE($3, description),\n default_collection = COALESCE($4, default_collection),\n modified_at = NOW()\n WHERE\n name = $5\n AND owner = $6\n AND deleted_at IS NULL" + }, + "742b9eef92ad404b3b1ec285ce8b99e945b1b910f25bff376466c704a79fc9c8": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Timestamp" + ] + } + }, + "query": "DELETE FROM sessions WHERE expires_at < $1" }, "7bdb55b060b5e81b50c1bf86cb117215cba7010c91f8384a397efac06a88507d": { "describe": { @@ -382,6 +390,19 @@ }, "query": "UPDATE sessions SET expires_at = $1 WHERE id = $2 RETURNING id" }, + "b85aa5820a73028398878a86ed550ea42daa362a8b0124f9ed626769a22bc047": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Uuid", + "Uuid" + ] + } + }, + "query": "UPDATE sites\n SET default_collection = $1\n WHERE id = $2" + }, "c567d96c92638902bc90f5fc0aa5bbbe0b6d26607767720a87fdbe67335a0180": { "describe": { "columns": [ @@ -574,22 +595,6 @@ }, "query": "DELETE FROM sites WHERE name = $1 AND owner = $2" }, - "d0005084c0ae3939460c3c727b29899c65a05e53fbc477cdda9665e7f243d5e9": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Varchar", - "Varchar", - "Text", - "Text", - "Uuid" - ] - } - }, - "query": "UPDATE sites SET\n name = COALESCE($1, name),\n title = COALESCE($2, title),\n description = COALESCE($3, description),\n modified_at = NOW()\n WHERE\n name = $4\n AND owner = $5\n AND deleted_at IS NULL" - }, "d9649c375db5825adda3b91c52468235fd9c361b827df64350c7e65bca0f00b4": { "describe": { "columns": [], @@ -628,6 +633,29 @@ }, "query": "INSERT INTO sites (id, name, owner, title, description)\n VALUES ($1, $2, $3, $4, $5) RETURNING id" }, + "eaf5344fe77b96ba3697e0454989d0e4d2fd074a850e7c8bafeaa9a19c50b71a": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Uuid" + } + ], + "nullable": [ + false + ], + "parameters": { + "Left": [ + "Uuid", + "Uuid", + "Varchar", + "Varchar" + ] + } + }, + "query": "INSERT INTO collections (id, site, slug, name)\n VALUES ($1, $2, $3, $4)\n RETURNING id" + }, "ecbbf6f400e058cd6e8695354d65a1883f974a39e11ac5ba0f7fba408fcf6b2a": { "describe": { "columns": [], diff --git a/src/content/collection.rs b/src/content/collection.rs index 884f0ce..ba2b779 100644 --- a/src/content/collection.rs +++ b/src/content/collection.rs @@ -7,7 +7,7 @@ use super::Error; pub const DEFAULT_COLLECTIONS: [(&str, &str); 2] = [("blog", "Blog"), ("pages", "Pages")]; -#[derive(Deserialize, Serialize, FromRow)] +#[derive(Debug, Deserialize, Serialize, FromRow)] pub struct Collection { /// Collection ID pub id: Uuid, @@ -25,7 +25,7 @@ pub struct Collection { pub parent: Option, } -#[derive(Serialize)] +#[derive(Debug, Serialize)] pub struct CollectionData { pub id: Uuid, pub slug: String, diff --git a/src/content/post.rs b/src/content/post.rs index 19c3d5a..72f69ad 100644 --- a/src/content/post.rs +++ b/src/content/post.rs @@ -135,8 +135,8 @@ pub trait PostRepository { /// List posts in a collection async fn list_posts_in_collection( &self, - site: &str, - collection: &str, + site: &Uuid, + collection: &Uuid, cursor: Option, limit: i64, ) -> Result, Error>; diff --git a/src/content/site.rs b/src/content/site.rs index 741be25..acb30be 100644 --- a/src/content/site.rs +++ b/src/content/site.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use sqlx::FromRow; use uuid::Uuid; -use super::Error; +use super::{collection::CollectionData, Error}; #[derive(Deserialize, Serialize, FromRow)] pub struct Site { @@ -23,6 +23,9 @@ pub struct Site { /// Site description (like a user's bio) pub description: Option, + /// Default collection (for homepage) + pub default_collection: Option, + /// Times pub created_at: NaiveDateTime, pub modified_at: Option, @@ -38,13 +41,8 @@ pub struct SiteData { pub title: String, pub description: Option, pub created_at: NaiveDateTime, - pub collections: Vec, -} - -#[derive(Debug, Serialize)] -pub struct CollectionNameAndSlug { - pub name: String, - pub slug: String, + pub collections: Vec, + pub default_collection: Option, } /// Data required to create a new site @@ -61,6 +59,7 @@ pub struct UpdateSiteData { pub name: Option, pub title: Option, pub description: Option, + pub default_collection: Option, } #[async_trait] diff --git a/src/database/collection.rs b/src/database/collection.rs index fc1980a..4d40dee 100644 --- a/src/database/collection.rs +++ b/src/database/collection.rs @@ -1,4 +1,5 @@ use async_trait::async_trait; +use std::vec; use uuid::Uuid; use crate::content::{ @@ -23,19 +24,32 @@ impl CollectionRepository for Database { async fn create_default_collections(&self, site: &Uuid) -> Result<(), Error> { let mut tx = self.pool.begin().await?; + let mut uuids = vec![]; for (slug, name) in DEFAULT_COLLECTIONS { - sqlx::query!( + let record = sqlx::query!( r#"INSERT INTO collections (id, site, slug, name) - VALUES ($1, $2, $3, $4)"#, + VALUES ($1, $2, $3, $4) + RETURNING id"#, Uuid::now_v7(), site, slug, name ) - .execute(&mut tx) + .fetch_one(&mut tx) .await?; + uuids.push(record.id); } + sqlx::query!( + r#"UPDATE sites + SET default_collection = $1 + WHERE id = $2"#, + uuids[0], + site + ) + .execute(&mut tx) + .await?; + tx.commit().await?; Ok(()) diff --git a/src/database/post.rs b/src/database/post.rs index fa02925..b2aedde 100644 --- a/src/database/post.rs +++ b/src/database/post.rs @@ -20,8 +20,8 @@ use super::Database; impl PostRepository for Database { async fn list_posts_in_collection( &self, - site: &str, - collection: &str, + site: &Uuid, + collection: &Uuid, from: Option, limit: i64, ) -> Result, Error> { @@ -42,13 +42,9 @@ impl PostRepository for Database { FROM pages JOIN users ON author = users.id - JOIN - sites ON site = sites.id - JOIN - collections ON collections.slug = $2 AND collections.site = sites.id WHERE - sites.name = $1 - AND collections.id = ANY(collections) + pages.site = $1 + AND $2 = ANY(collections) AND pages.deleted_at IS NULL AND published = true AND pages.created_at <= $3 diff --git a/src/database/site.rs b/src/database/site.rs index 01c0889..42b641c 100644 --- a/src/database/site.rs +++ b/src/database/site.rs @@ -3,33 +3,42 @@ use sqlx::Postgres; use uuid::Uuid; use crate::content::{ - site::{CollectionNameAndSlug, CreateSiteData, SiteData, SiteRepository, UpdateSiteData}, + collection::CollectionData, + site::{CreateSiteData, SiteData, SiteRepository, UpdateSiteData}, Error, }; use super::Database; -impl sqlx::Type for CollectionNameAndSlug { +impl sqlx::Type for CollectionData { fn type_info() -> sqlx::postgres::PgTypeInfo { - sqlx::postgres::PgTypeInfo::with_name("collection_name_slug") + sqlx::postgres::PgTypeInfo::with_name("collection_data") } } -impl<'r> sqlx::Decode<'r, Postgres> for CollectionNameAndSlug { +impl<'r> sqlx::Decode<'r, Postgres> for CollectionData { fn decode( value: sqlx::postgres::PgValueRef<'r>, ) -> Result> { let mut decoder = sqlx::postgres::types::PgRecordDecoder::new(value)?; - let slug = decoder.try_decode::()?; + let id = decoder.try_decode::()?; let name = decoder.try_decode::()?; - Ok(Self { name, slug }) + let slug = decoder.try_decode::()?; + let parent = decoder.try_decode::>()?; + Ok(Self { + id, + name, + slug, + parent, + }) } } #[async_trait] impl SiteRepository for Database { async fn get_site_by_name(&self, name: &str) -> Result { - let site = sqlx::query!( + let site = sqlx::query_as!( + SiteData, r#"SELECT sites.name, users.name as owner, @@ -37,7 +46,8 @@ impl SiteRepository for Database { title, description, sites.created_at, - array_agg(row(collections.slug, collections.name)) as "collections!: Vec" + default_collection, + array_agg(row(collections.id, collections.slug, collections.name, parent)) as "collections!: Vec" FROM sites JOIN users ON sites.owner = users.id @@ -47,7 +57,7 @@ impl SiteRepository for Database { sites.name = $1 AND sites.deleted_at IS NULL GROUP BY - sites.name, users.name, users.display_name, title, description, sites.created_at"#, + sites.name, users.name, users.display_name, title, description, sites.created_at, default_collection"#, name ) .fetch_one(&self.pool) @@ -56,15 +66,7 @@ impl SiteRepository for Database { sqlx::Error::RowNotFound => Error::NotFound, _ => e.into(), })?; - Ok(SiteData { - name: site.name, - owner: site.owner, - owner_display_name: site.owner_display_name, - title: site.title, - description: site.description, - created_at: site.created_at, - collections: site.collections, - }) + Ok(site) } async fn create_site(&self, owner: &Uuid, options: CreateSiteData) -> Result { @@ -99,14 +101,16 @@ impl SiteRepository for Database { name = COALESCE($1, name), title = COALESCE($2, title), description = COALESCE($3, description), + default_collection = COALESCE($4, default_collection), modified_at = NOW() WHERE - name = $4 - AND owner = $5 + name = $5 + AND owner = $6 AND deleted_at IS NULL"#, options.name, options.title, options.description, + options.default_collection, name, owner, ) diff --git a/src/routes/collections.rs b/src/routes/collections.rs index d32aba7..71a9687 100644 --- a/src/routes/collections.rs +++ b/src/routes/collections.rs @@ -7,6 +7,7 @@ use axum::{ use chrono::NaiveDateTime; use serde::Deserialize; use std::sync::Arc; +use uuid::Uuid; use crate::{ content::{collection::CollectionRepository, post::PostRepository, site::SiteRepository}, @@ -15,6 +16,8 @@ use crate::{ state::AppState, }; +const DEFAULT_PAGE_SIZE: i64 = 10; + async fn create_collection(repository: Repo) { todo!() } @@ -40,19 +43,27 @@ async fn delete_collection(repository: Repo) { todo!() } -#[derive(Deserialize)] +#[derive(Deserialize, Default)] struct PaginationQuery { before: Option, - limit: i64, + limit: Option, } -async fn list_posts_in_collection( +async fn list_posts_in_collection( repository: Repo, - Path((site, slug)): Path<(String, String)>, + Path((site, id)): Path<(String, Uuid)>, query: Query, ) -> Result> { + // Resolve site + let (site_id, _) = repository.get_site_id_and_owner(&site).await?; + let results = repository - .list_posts_in_collection(&site, &slug, query.before, query.limit) + .list_posts_in_collection( + &site_id, + &id, + query.before, + query.limit.unwrap_or(DEFAULT_PAGE_SIZE), + ) .await?; Ok(Json(results)) } @@ -64,13 +75,13 @@ pub fn router() -> Router> { get(list_collections_for_site::).post(create_collection::), ) .route( - "/:site/:slug", + "/:site/:id", get(get_collection::) .put(update_collection::) .delete(delete_collection::), ) .route( - "/:site/:slug/posts", + "/:site/:id/posts", get(list_posts_in_collection::), ) }