Skip to content

Commit

Permalink
Merge pull request #1532 from scpwiki/listpages
Browse files Browse the repository at this point in the history
[WJ-228] Initial version of PageQueryService
  • Loading branch information
emmiegit authored Jul 13, 2023
2 parents fa8bf79 + e2d8a86 commit 0dd7178
Show file tree
Hide file tree
Showing 9 changed files with 910 additions and 13 deletions.
5 changes: 5 additions & 0 deletions deepwell/migrations/20220906103252_deepwell.sql
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ CREATE TABLE page (
deleted_at TIMESTAMP WITH TIME ZONE,
from_wikidot BOOLEAN NOT NULL DEFAULT false,
site_id BIGINT NOT NULL REFERENCES site(site_id),
latest_revision_id BIGINT, -- nullable to avoid an initial page_revision dependency cycle
page_category_id BIGINT NOT NULL REFERENCES page_category(category_id),
slug TEXT NOT NULL,
discussion_thread_id BIGINT, -- TODO: add REFERENCES to forum threads
Expand Down Expand Up @@ -269,6 +270,10 @@ CREATE TABLE page_revision (
UNIQUE (page_id, site_id, revision_number)
);

-- Add foreign key constraint for latest_revision_id
ALTER TABLE page ADD CONSTRAINT page_revision_revision_id_fk
FOREIGN KEY (latest_revision_id) REFERENCES page_revision(revision_id);

--
-- Page metadata
--
Expand Down
4 changes: 2 additions & 2 deletions deepwell/scripts/generate-models.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ cd "${0%/*}/.."
# Generate models
sea-orm-cli generate entity \
--verbose \
-database-url postgres://wikijump:wikijump@localhost/wikijump \
-output-dir src/models \
--database-url postgres://wikijump:wikijump@localhost/wikijump \
--output-dir src/models \
--date-time-crate time \
--with-serde both
11 changes: 9 additions & 2 deletions deepwell/src/models/page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub struct Model {
pub deleted_at: Option<OffsetDateTime>,
pub from_wikidot: bool,
pub site_id: i64,
pub latest_revision_id: Option<i64>,
pub page_category_id: i64,
#[sea_orm(column_type = "Text")]
pub slug: String,
Expand All @@ -31,6 +32,14 @@ pub enum Relation {
on_delete = "NoAction"
)]
PageCategory,
#[sea_orm(
belongs_to = "super::page_revision::Entity",
from = "Column::LatestRevisionId",
to = "super::page_revision::Column::RevisionId",
on_update = "NoAction",
on_delete = "NoAction"
)]
PageRevision,
#[sea_orm(
belongs_to = "super::site::Entity",
from = "Column::SiteId",
Expand All @@ -39,8 +48,6 @@ pub enum Relation {
on_delete = "NoAction"
)]
Site,
#[sea_orm(has_many = "super::page_revision::Entity")]
PageRevision,
#[sea_orm(has_many = "super::page_attribution::Entity")]
PageAttribution,
#[sea_orm(has_many = "super::page_lock::Entity")]
Expand Down
2 changes: 2 additions & 0 deletions deepwell/src/services/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ pub mod link;
pub mod mfa;
pub mod outdate;
pub mod page;
pub mod page_query;
pub mod page_revision;
pub mod parent;
pub mod password;
Expand Down Expand Up @@ -98,6 +99,7 @@ pub use self::link::LinkService;
pub use self::mfa::MfaService;
pub use self::outdate::OutdateService;
pub use self::page::PageService;
pub use self::page_query::PageQueryService;
pub use self::page_revision::PageRevisionService;
pub use self::parent::ParentService;
pub use self::password::PasswordService;
Expand Down
96 changes: 89 additions & 7 deletions deepwell/src/services/page/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use crate::services::page_revision::{
use crate::services::{CategoryService, FilterService, PageRevisionService, TextService};
use crate::utils::{get_category_name, trim_default};
use crate::web::PageOrder;
use sea_orm::ActiveValue;
use wikidot_normalize::normalize;

#[derive(Debug)]
Expand Down Expand Up @@ -79,7 +80,7 @@ impl PageService {
slug: Set(slug.clone()),
..Default::default()
};
let page = model.insert(txn).await?;
let PageModel { page_id, .. } = model.insert(txn).await?;

// Commit first revision
let revision_input = CreateFirstPageRevision {
Expand All @@ -94,12 +95,20 @@ impl PageService {
let CreateFirstPageRevisionOutput {
revision_id,
parser_errors,
} = PageRevisionService::create_first(ctx, site_id, page.page_id, revision_input)
} = PageRevisionService::create_first(ctx, site_id, page_id, revision_input)
.await?;

// Update latest revision
let model = page::ActiveModel {
page_id: Set(page_id),
latest_revision_id: Set(Some(revision_id)),
..Default::default()
};
model.update(txn).await?;

// Build and return
Ok(CreatePageOutput {
page_id: page.page_id,
page_id,
slug,
revision_id,
parser_errors,
Expand Down Expand Up @@ -169,12 +178,18 @@ impl PageService {
)
.await?;

// Set page updated_at column.
let latest_revision_id = match revision_output {
Some(ref output) => ActiveValue::Set(Some(output.revision_id)),
None => ActiveValue::NotSet,
};

// Set page updated_at and latest_revision_id columns.
//
// Previously this was conditional on whether a revision was actually created.
// But since this rerenders regardless, we need to update the page row.
let model = page::ActiveModel {
page_id: Set(page_id),
latest_revision_id,
updated_at: Set(Some(now())),
..Default::default()
};
Expand Down Expand Up @@ -242,14 +257,21 @@ impl PageService {
)
.await?;

let latest_revision_id = match revision_output {
Some(ref output) => ActiveValue::Set(Some(output.revision_id)),
None => ActiveValue::NotSet,
};

// Update page after move. This changes:
// * slug -- New slug for the page
// * page_category_id -- In case the category also changed
// * updated_at -- This is updated every time a page is changed
// * slug -- New slug for the page
// * page_category_id -- In case the category also changed
// * latest_revision_id -- In case a new revision was created
// * updated_at -- This is updated every time a page is changed
let model = page::ActiveModel {
page_id: Set(page_id),
slug: Set(new_slug.clone()),
page_category_id: Set(category_id),
latest_revision_id,
updated_at: Set(Some(now())),
..Default::default()
};
Expand Down Expand Up @@ -310,6 +332,7 @@ impl PageService {
// Set deletion flag
let model = page::ActiveModel {
page_id: Set(page_id),
latest_revision_id: Set(Some(output.revision_id)),
deleted_at: Set(Some(now())),
..Default::default()
};
Expand Down Expand Up @@ -379,6 +402,7 @@ impl PageService {
let model = page::ActiveModel {
page_id: Set(page_id),
page_category_id: Set(category.category_id),
latest_revision_id: Set(Some(output.revision_id)),
deleted_at: Set(None),
..Default::default()
};
Expand Down Expand Up @@ -512,6 +536,21 @@ impl PageService {
.await?
};

// Even in production, we want to assert that this invariant holds.
//
// We cannot set the column itself to NOT NULL because of cyclic update
// requirements. However when using PageService, at no point should a method
// quit with this value being null.
if let Some(ref page) = page {
assert!(
page.latest_revision_id.is_some(),
"Page ID {} (slug '{}', site ID {}) has a NULL latest_revision_id column!",
page.page_id,
page.slug,
page.site_id,
);
}

Ok(page)
}

Expand Down Expand Up @@ -551,6 +590,49 @@ impl PageService {
Ok(page)
}

/// Gets all pages which match the given page references.
///
/// The result list is not in the same order as the input, it
/// is up to the caller to order it if they wish.
pub async fn get_pages(
ctx: &ServiceContext<'_>,
site_id: i64,
references: &[Reference<'_>],
) -> Result<Vec<PageModel>> {
tide::log::info!(
"Getting {} pages from references in site ID {}",
references.len(),
site_id,
);

let mut filter_ids = Vec::new();
let mut filter_slugs = Vec::new();

for reference in references {
match reference {
Reference::Id(id) => filter_ids.push(*id),
Reference::Slug(slug) => filter_slugs.push(slug.as_ref()),
}
}

let txn = ctx.transaction();
let models = Page::find()
.filter(
Condition::all()
.add(page::Column::SiteId.eq(site_id))
.add(page::Column::DeletedAt.is_null())
.add(
Condition::any()
.add(page::Column::PageId.is_in(filter_ids))
.add(page::Column::Slug.is_in(filter_slugs)),
),
)
.all(txn)
.await?;

Ok(models)
}

/// Get all pages in a site, with potential conditions.
///
/// The `category` argument:
Expand Down
30 changes: 30 additions & 0 deletions deepwell/src/services/page_query/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* services/page_query/mod.rs
*
* DEEPWELL - Wikijump API provider and database manager
* Copyright (C) 2019-2023 Wikijump Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

mod prelude {
pub use super::super::prelude::*;
pub use super::structs::*;
}

mod service;
mod structs;

pub use self::service::PageQueryService;
pub use self::structs::*;
Loading

0 comments on commit 0dd7178

Please sign in to comment.