more CRUDdy bits
This commit is contained in:
parent
0f79975ad6
commit
7a4f3b15b7
4 changed files with 174 additions and 28 deletions
|
@ -31,11 +31,13 @@ async fn setup_inmem_store() -> outbound::repository::adapters::memory::InMemory
|
|||
|
||||
let store = outbound::repository::adapters::memory::InMemoryStore::new();
|
||||
store
|
||||
.add_site(outbound::repository::site::SiteMetadata {
|
||||
.create_site(outbound::repository::site::SiteMetadata {
|
||||
domain: "localhost".to_string(),
|
||||
title: "Test site".to_string(),
|
||||
})
|
||||
.await;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
store
|
||||
.set_page(
|
||||
"localhost",
|
||||
|
@ -56,6 +58,7 @@ async fn setup_inmem_store() -> outbound::repository::adapters::memory::InMemory
|
|||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
store
|
||||
.set_page(
|
||||
"localhost",
|
||||
|
@ -73,6 +76,7 @@ async fn setup_inmem_store() -> outbound::repository::adapters::memory::InMemory
|
|||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
store
|
||||
.add_post(
|
||||
"localhost",
|
||||
|
@ -85,6 +89,7 @@ async fn setup_inmem_store() -> outbound::repository::adapters::memory::InMemory
|
|||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
store
|
||||
.add_post(
|
||||
"localhost",
|
||||
|
|
|
@ -36,14 +36,6 @@ impl InMemoryStore {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn add_site(&self, site: SiteMetadata) {
|
||||
self.data
|
||||
.lock()
|
||||
.await
|
||||
.sites
|
||||
.insert(site.domain.clone(), site);
|
||||
}
|
||||
|
||||
pub async fn add_post(&self, site: &str, collection_id: &str, post_id: &str, post: Post) {
|
||||
self.data
|
||||
.lock()
|
||||
|
@ -61,11 +53,12 @@ impl InMemoryStore {
|
|||
let store = InMemoryStore::new();
|
||||
|
||||
store
|
||||
.add_site(SiteMetadata {
|
||||
.create_site(SiteMetadata {
|
||||
domain: "example.com".to_string(),
|
||||
title: "Test site".to_string(),
|
||||
})
|
||||
.await;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
store
|
||||
.set_page(
|
||||
|
@ -167,6 +160,21 @@ impl SiteRepository for InMemoryStore {
|
|||
Ok(pages)
|
||||
}
|
||||
|
||||
async fn create_site(&self, site: SiteMetadata) -> Result<()> {
|
||||
// Check for existing site
|
||||
if self.data.lock().await.sites.contains_key(&site.domain) {
|
||||
return Err(repository::site::Error::Conflict);
|
||||
}
|
||||
|
||||
self.data
|
||||
.lock()
|
||||
.await
|
||||
.sites
|
||||
.insert(site.domain.clone(), site);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_page(&self, domain: &str, page: &str) -> Result<Page> {
|
||||
self.data
|
||||
.lock()
|
||||
|
@ -187,6 +195,21 @@ impl SiteRepository for InMemoryStore {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn delete_page(&self, domain: &str, page: &str) -> Result<()> {
|
||||
let deleted = self
|
||||
.data
|
||||
.lock()
|
||||
.await
|
||||
.pages
|
||||
.remove(&(domain.to_string(), page.to_string()));
|
||||
|
||||
if deleted.is_none() {
|
||||
return Err(repository::site::Error::NotFound);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_post(&self, domain: &str, collection_id: &str, post_id: &str) -> Result<Post> {
|
||||
self.data
|
||||
.lock()
|
||||
|
@ -259,7 +282,10 @@ mod tests {
|
|||
cursor::CursorOptions,
|
||||
site::{Block, Page, PageContent, PageInfo, Post},
|
||||
},
|
||||
outbound::repository::{self, site::SiteRepository},
|
||||
outbound::repository::{
|
||||
self,
|
||||
site::{SiteMetadata, SiteRepository},
|
||||
},
|
||||
};
|
||||
|
||||
use super::InMemoryStore;
|
||||
|
@ -445,4 +471,57 @@ mod tests {
|
|||
assert_eq!(page.info.name, "new-page");
|
||||
assert_eq!(page.content, content);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn delete_page_works() {
|
||||
let store = InMemoryStore::with_test_data().await;
|
||||
let _ = store.delete_page("example.com", "/").await.unwrap();
|
||||
let result = store.get_page("example.com", "/").await;
|
||||
assert!(result.is_err());
|
||||
assert!(matches!(
|
||||
result.err().unwrap(),
|
||||
repository::site::Error::NotFound
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn delete_page_for_nonexistent_fails() {
|
||||
let store = InMemoryStore::new();
|
||||
let result = store.delete_page("example.com", "/").await;
|
||||
assert!(result.is_err());
|
||||
assert!(matches!(
|
||||
result.err().unwrap(),
|
||||
repository::site::Error::NotFound
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn creates_a_site() {
|
||||
let store = InMemoryStore::new();
|
||||
let _ = store
|
||||
.create_site(SiteMetadata {
|
||||
domain: "example.com".to_string(),
|
||||
title: "Test site".to_string(),
|
||||
})
|
||||
.await;
|
||||
let info = store.get_site_by_domain("example.com").await.unwrap();
|
||||
assert_eq!(info.domain, "example.com");
|
||||
assert_eq!(info.title, "Test site");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn doesnt_overwrite_existing_site() {
|
||||
let store = InMemoryStore::with_test_data().await;
|
||||
let result = store
|
||||
.create_site(SiteMetadata {
|
||||
domain: "example.com".to_string(),
|
||||
title: "Test site".to_string(),
|
||||
})
|
||||
.await;
|
||||
assert!(result.is_err());
|
||||
assert!(matches!(
|
||||
result.err().unwrap(),
|
||||
repository::site::Error::Conflict
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,11 @@ pub trait SiteRepository: Send + Sync + 'static {
|
|||
async fn get_site_by_domain(&self, domain: &str) -> Result<SiteMetadata>;
|
||||
async fn get_pages_for_site(&self, domain: &str) -> Result<Vec<PageInfo>>;
|
||||
|
||||
async fn create_site(&self, site: SiteMetadata) -> Result<()>;
|
||||
|
||||
async fn get_page(&self, domain: &str, name: &str) -> Result<Page>;
|
||||
async fn set_page(&self, domain: &str, page: Page) -> Result<()>;
|
||||
async fn delete_page(&self, domain: &str, page: &str) -> Result<()>;
|
||||
|
||||
async fn get_post(&self, domain: &str, collection_id: &str, id: &str) -> Result<Post>;
|
||||
async fn get_posts_for_collection(
|
||||
|
@ -36,6 +39,9 @@ pub enum Error {
|
|||
#[error("resource not found")]
|
||||
NotFound,
|
||||
|
||||
#[error("a resource with the same identifier already exists")]
|
||||
Conflict,
|
||||
|
||||
#[error("the server encountered an error: {0}")]
|
||||
ServerError(String),
|
||||
}
|
||||
|
|
|
@ -7,7 +7,10 @@ use crate::{
|
|||
cursor::{CursorOptions, Paginated},
|
||||
site::{Page, Post, SiteInfo},
|
||||
},
|
||||
outbound::repository::{self, site::SiteRepository},
|
||||
outbound::repository::{
|
||||
self,
|
||||
site::{SiteMetadata, SiteRepository},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -36,18 +39,34 @@ impl SiteService {
|
|||
})
|
||||
}
|
||||
|
||||
pub async fn create_site(&self, site: SiteMetadata) -> Result<()> {
|
||||
self.site_repository.create_site(site).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_page(&self, domain: &str, name: &str) -> Result<Page> {
|
||||
let info = self.site_repository.get_site_by_domain(domain).await?;
|
||||
let page = self.site_repository.get_page(&info.domain, name).await?;
|
||||
let page = self.site_repository.get_page(domain, name).await?;
|
||||
|
||||
Ok(page)
|
||||
}
|
||||
|
||||
pub async fn set_page(&self, domain: &str, page: Page) -> Result<()> {
|
||||
self.site_repository.set_page(domain, page).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_page(&self, domain: &str, page: &str) -> Result<()> {
|
||||
self.site_repository.delete_page(domain, page).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_post(&self, domain: &str, collection_id: &str, id: &str) -> Result<Post> {
|
||||
let info = self.site_repository.get_site_by_domain(domain).await?;
|
||||
let post = self
|
||||
.site_repository
|
||||
.get_post(&info.domain, collection_id, id)
|
||||
.get_post(domain, collection_id, id)
|
||||
.await?;
|
||||
|
||||
Ok(post)
|
||||
|
@ -59,11 +78,10 @@ impl SiteService {
|
|||
collection_id: &str,
|
||||
cursor: Option<String>,
|
||||
) -> Result<Paginated<Post, String>> {
|
||||
let info = self.site_repository.get_site_by_domain(domain).await?;
|
||||
let posts = self
|
||||
.site_repository
|
||||
.get_posts_for_collection(
|
||||
&info.domain,
|
||||
domain,
|
||||
collection_id,
|
||||
CursorOptions {
|
||||
after: cursor,
|
||||
|
@ -74,13 +92,6 @@ impl SiteService {
|
|||
|
||||
Ok(posts)
|
||||
}
|
||||
|
||||
pub async fn set_page(&self, domain: &str, page: Page) -> Result<()> {
|
||||
let info = self.site_repository.get_site_by_domain(domain).await?;
|
||||
self.site_repository.set_page(&info.domain, page).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
@ -108,7 +119,7 @@ mod tests {
|
|||
use super::{Error, SiteService};
|
||||
use crate::{
|
||||
domain::entities::site::{Block, Page, PageContent, PageInfo, Post},
|
||||
outbound::repository::adapters::memory::InMemoryStore,
|
||||
outbound::repository::{adapters::memory::InMemoryStore, site::SiteMetadata},
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
|
@ -244,4 +255,49 @@ mod tests {
|
|||
assert_eq!(page.info.name, "new_page");
|
||||
assert_eq!(page.content, content);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn deletes_a_page() {
|
||||
let service = SiteService::new(InMemoryStore::with_test_data().await);
|
||||
service.delete_page("example.com", "about").await.unwrap();
|
||||
let result = service.get_page("example.com", "about").await;
|
||||
assert!(result.is_err());
|
||||
assert!(matches!(result.err().unwrap(), Error::NotFound));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn deletes_a_nonexistent_page() {
|
||||
let service = SiteService::new(InMemoryStore::with_test_data().await);
|
||||
let result = service.delete_page("example.com", "nonexistent").await;
|
||||
assert!(result.is_err());
|
||||
assert!(matches!(result.err().unwrap(), Error::NotFound));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn creates_a_site() {
|
||||
let service = SiteService::new(InMemoryStore::with_test_data().await);
|
||||
service
|
||||
.create_site(SiteMetadata {
|
||||
domain: "new.com".to_string(),
|
||||
title: "New site".to_string(),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn doesnt_create_a_site_that_already_exists() {
|
||||
let service = SiteService::new(InMemoryStore::with_test_data().await);
|
||||
let result = service
|
||||
.create_site(SiteMetadata {
|
||||
domain: "example.com".to_string(),
|
||||
title: "Example site".to_string(),
|
||||
})
|
||||
.await;
|
||||
assert!(result.is_err());
|
||||
assert!(matches!(
|
||||
result.err().unwrap(),
|
||||
Error::RepositoryError(crate::outbound::repository::site::Error::Conflict)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue