Skip to content

Commit b72d0ae

Browse files
committed
Merge branch 'release/v1.0.5'
2 parents 84f9f03 + fb0e909 commit b72d0ae

12 files changed

Lines changed: 320 additions & 31 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
## [[1.0.5]](https://github.com/thoth-pub/thoth/releases/tag/v1.0.5) - 2026-04-15
10+
### Added
11+
- Add `workStatuses` filtering to `subjects` and `subjectCount` queries
12+
913
## [[1.0.4]](https://github.com/thoth-pub/thoth/releases/tag/v1.0.4) - 2026-04-13
1014
### Fixed
1115
- Aggregate Crossref Crossmark updates into a single `<updates>` block

Cargo.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "thoth"
3-
version = "1.0.4"
3+
version = "1.0.5"
44
authors = ["Javier Arias <javi@thoth.pub>", "Ross Higman <ross@thoth.pub>"]
55
edition = "2021"
66
license = "Apache-2.0"
@@ -15,10 +15,10 @@ maintenance = { status = "actively-developed" }
1515
members = ["thoth-api", "thoth-api-server", "thoth-client", "thoth-errors", "thoth-export-server"]
1616

1717
[dependencies]
18-
thoth-api = { version = "=1.0.4", path = "thoth-api", features = ["backend"] }
19-
thoth-api-server = { version = "=1.0.4", path = "thoth-api-server" }
20-
thoth-errors = { version = "=1.0.4", path = "thoth-errors" }
21-
thoth-export-server = { version = "=1.0.4", path = "thoth-export-server" }
18+
thoth-api = { version = "=1.0.5", path = "thoth-api", features = ["backend"] }
19+
thoth-api-server = { version = "=1.0.5", path = "thoth-api-server" }
20+
thoth-errors = { version = "=1.0.5", path = "thoth-errors" }
21+
thoth-export-server = { version = "=1.0.5", path = "thoth-export-server" }
2222
base64 = "0.22.1"
2323
clap = { version = "4.5.32", features = ["cargo", "env"] }
2424
dialoguer = { version = "0.11.0", features = ["password"] }

thoth-api-server/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "thoth-api-server"
3-
version = "1.0.4"
3+
version = "1.0.5"
44
authors = ["Javier Arias <javi@thoth.pub>", "Ross Higman <ross@thoth.pub>"]
55
edition = "2021"
66
license = "Apache-2.0"
@@ -9,8 +9,8 @@ repository = "https://github.com/thoth-pub/thoth"
99
readme = "README.md"
1010

1111
[dependencies]
12-
thoth-api = { version = "=1.0.4", path = "../thoth-api", features = ["backend"] }
13-
thoth-errors = { version = "=1.0.4", path = "../thoth-errors" }
12+
thoth-api = { version = "=1.0.5", path = "../thoth-api", features = ["backend"] }
13+
thoth-errors = { version = "=1.0.5", path = "../thoth-errors" }
1414
actix-web = "4.10"
1515
actix-cors = "0.7.1"
1616
actix-http = "3.10.0"

thoth-api/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "thoth-api"
3-
version = "1.0.4"
3+
version = "1.0.5"
44
authors = ["Javier Arias <javi@thoth.pub>", "Ross Higman <ross@thoth.pub>"]
55
edition = "2021"
66
license = "Apache-2.0"
@@ -31,7 +31,7 @@ backend = [
3131
]
3232

3333
[dependencies]
34-
thoth-errors = { version = "=1.0.4", path = "../thoth-errors" }
34+
thoth-errors = { version = "=1.0.5", path = "../thoth-errors" }
3535
actix-web = { version = "4.10", optional = true }
3636
isbn = "0.6.0"
3737
chrono = { version = "0.4.40", features = ["serde"] }

thoth-api/src/graphql/query.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,6 +1138,7 @@ impl QueryRoot {
11381138
.map_err(Into::into)
11391139
}
11401140

1141+
#[allow(clippy::too_many_arguments)]
11411142
#[graphql(description = "Query the full list of subjects")]
11421143
fn subjects(
11431144
context: &Context,
@@ -1163,6 +1164,11 @@ impl QueryRoot {
11631164
description = "Specific types to filter by",
11641165
)]
11651166
subject_types: Option<Vec<SubjectType>>,
1167+
#[graphql(
1168+
default = vec![],
1169+
description = "Specific statuses to filter by"
1170+
)]
1171+
work_statuses: Option<Vec<WorkStatus>>,
11661172
) -> FieldResult<Vec<Subject>> {
11671173
Subject::all(
11681174
&context.db,
@@ -1174,7 +1180,7 @@ impl QueryRoot {
11741180
None,
11751181
None,
11761182
subject_types.unwrap_or_default(),
1177-
vec![],
1183+
work_statuses.unwrap_or_default(),
11781184
None,
11791185
None,
11801186
)
@@ -1202,13 +1208,18 @@ impl QueryRoot {
12021208
description = "Specific types to filter by",
12031209
)]
12041210
subject_types: Option<Vec<SubjectType>>,
1211+
#[graphql(
1212+
default = vec![],
1213+
description = "Specific statuses to filter by"
1214+
)]
1215+
work_statuses: Option<Vec<WorkStatus>>,
12051216
) -> FieldResult<i32> {
12061217
Subject::count(
12071218
&context.db,
12081219
filter,
12091220
vec![],
12101221
subject_types.unwrap_or_default(),
1211-
vec![],
1222+
work_statuses.unwrap_or_default(),
12121223
None,
12131224
None,
12141225
)

thoth-api/src/graphql/tests.rs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2007,6 +2007,136 @@ query Root(
20072007
assert_title_resolvers(&title, &context);
20082008
}
20092009

2010+
#[test]
2011+
fn graphql_subjects_and_subject_count_support_work_status_filters() {
2012+
let (_guard, pool) = test_db::setup_test_db();
2013+
let schema = create_schema();
2014+
let superuser = test_db::test_superuser("user-subject-status-filters");
2015+
let context = test_db::test_context_with_user(pool.clone(), superuser);
2016+
2017+
let publisher = create_with_data(
2018+
&schema,
2019+
&context,
2020+
"createPublisher",
2021+
"NewPublisher",
2022+
"publisherId",
2023+
make_new_publisher("org-subject-status-filters"),
2024+
);
2025+
let publisher_id = json_uuid(&publisher["publisherId"]);
2026+
2027+
let imprint = create_with_data(
2028+
&schema,
2029+
&context,
2030+
"createImprint",
2031+
"NewImprint",
2032+
"imprintId",
2033+
make_new_imprint(publisher_id),
2034+
);
2035+
let imprint_id = json_uuid(&imprint["imprintId"]);
2036+
2037+
let active_work = create_with_data(
2038+
&schema,
2039+
&context,
2040+
"createWork",
2041+
"NewWork",
2042+
"workId",
2043+
make_new_work(
2044+
imprint_id,
2045+
WorkType::Monograph,
2046+
Doi::from_str("10.1234/subjects-status-active").unwrap(),
2047+
),
2048+
);
2049+
let active_work_id = json_uuid(&active_work["workId"]);
2050+
2051+
let mut forthcoming_work_input = make_new_work(
2052+
imprint_id,
2053+
WorkType::Monograph,
2054+
Doi::from_str("10.1234/subjects-status-forthcoming").unwrap(),
2055+
);
2056+
forthcoming_work_input.work_status = WorkStatus::Forthcoming;
2057+
forthcoming_work_input.publication_date = None;
2058+
let forthcoming_work = create_with_data(
2059+
&schema,
2060+
&context,
2061+
"createWork",
2062+
"NewWork",
2063+
"workId",
2064+
forthcoming_work_input,
2065+
);
2066+
let forthcoming_work_id = json_uuid(&forthcoming_work["workId"]);
2067+
2068+
let active_subject = create_with_data(
2069+
&schema,
2070+
&context,
2071+
"createSubject",
2072+
"NewSubject",
2073+
"subjectId",
2074+
make_new_subject(active_work_id, 1),
2075+
);
2076+
let active_subject_id = json_uuid(&active_subject["subjectId"]);
2077+
2078+
let forthcoming_subject = create_with_data(
2079+
&schema,
2080+
&context,
2081+
"createSubject",
2082+
"NewSubject",
2083+
"subjectId",
2084+
make_new_subject(forthcoming_work_id, 1),
2085+
);
2086+
let forthcoming_subject_id = json_uuid(&forthcoming_subject["subjectId"]);
2087+
2088+
let query_plural = r#"
2089+
query SubjectsByStatuses($statuses: [WorkStatus!]) {
2090+
subjects(limit: 10, workStatuses: $statuses) { subjectId }
2091+
subjectCount(workStatuses: $statuses)
2092+
}
2093+
"#;
2094+
let mut plural_vars = Variables::new();
2095+
insert_var(&mut plural_vars, "statuses", vec![WorkStatus::Forthcoming]);
2096+
let plural_data = execute_graphql(&schema, &context, query_plural, Some(plural_vars));
2097+
2098+
let plural_subjects = plural_data["subjects"]
2099+
.as_array()
2100+
.expect("Expected subjects array");
2101+
assert_eq!(plural_subjects.len(), 1);
2102+
assert_eq!(
2103+
json_uuid(&plural_subjects[0]["subjectId"]),
2104+
forthcoming_subject_id
2105+
);
2106+
assert_eq!(plural_data["subjectCount"].as_i64(), Some(1));
2107+
2108+
let query_multiple_statuses = r#"
2109+
query SubjectsByMultipleStatuses($statuses: [WorkStatus!]) {
2110+
subjects(limit: 10, workStatuses: $statuses) { subjectId }
2111+
subjectCount(workStatuses: $statuses)
2112+
}
2113+
"#;
2114+
let mut multiple_statuses_vars = Variables::new();
2115+
insert_var(
2116+
&mut multiple_statuses_vars,
2117+
"statuses",
2118+
vec![WorkStatus::Active, WorkStatus::Forthcoming],
2119+
);
2120+
let multiple_statuses_data = execute_graphql(
2121+
&schema,
2122+
&context,
2123+
query_multiple_statuses,
2124+
Some(multiple_statuses_vars),
2125+
);
2126+
2127+
let multiple_statuses_subjects = multiple_statuses_data["subjects"]
2128+
.as_array()
2129+
.expect("Expected subjects array");
2130+
let multiple_statuses_ids: Vec<Uuid> = multiple_statuses_subjects
2131+
.iter()
2132+
.map(|subject| json_uuid(&subject["subjectId"]))
2133+
.collect();
2134+
assert_eq!(multiple_statuses_ids.len(), 2);
2135+
assert!(multiple_statuses_ids.contains(&active_subject_id));
2136+
assert!(multiple_statuses_ids.contains(&forthcoming_subject_id));
2137+
assert_eq!(multiple_statuses_data["subjectCount"].as_i64(), Some(2));
2138+
}
2139+
20102140
#[test]
20112141
fn graphql_books_order_respects_field_and_direction() {
20122142
let (_guard, pool) = test_db::setup_test_db();

thoth-api/src/model/subject/crud.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use super::{
22
NewSubject, NewSubjectHistory, PatchSubject, Subject, SubjectField, SubjectHistory, SubjectType,
33
};
44
use crate::graphql::types::inputs::SubjectOrderBy;
5+
use crate::model::work::WorkStatus;
56
use crate::model::{Crud, DbInsert, HistoryEntry, Reorder};
67
use crate::schema::{subject, subject_history};
78
use diesel::{
@@ -16,7 +17,7 @@ impl Crud for Subject {
1617
type PatchEntity = PatchSubject;
1718
type OrderByEntity = SubjectOrderBy;
1819
type FilterParameter1 = SubjectType;
19-
type FilterParameter2 = ();
20+
type FilterParameter2 = WorkStatus;
2021
type FilterParameter3 = ();
2122
type FilterParameter4 = ();
2223

@@ -34,7 +35,7 @@ impl Crud for Subject {
3435
parent_id_1: Option<Uuid>,
3536
_: Option<Uuid>,
3637
subject_types: Vec<Self::FilterParameter1>,
37-
_: Vec<Self::FilterParameter2>,
38+
work_statuses: Vec<Self::FilterParameter2>,
3839
_: Option<Self::FilterParameter3>,
3940
_: Option<Self::FilterParameter4>,
4041
) -> ThothResult<Vec<Subject>> {
@@ -77,6 +78,9 @@ impl Crud for Subject {
7778
if !subject_types.is_empty() {
7879
query = query.filter(subject_type.eq_any(subject_types));
7980
}
81+
if !work_statuses.is_empty() {
82+
query = query.filter(crate::schema::work::work_status.eq_any(work_statuses));
83+
}
8084
if let Some(filter) = filter {
8185
query = query.filter(subject_code.ilike(format!("%{filter}%")));
8286
}
@@ -93,16 +97,19 @@ impl Crud for Subject {
9397
filter: Option<String>,
9498
_: Vec<Uuid>,
9599
subject_types: Vec<Self::FilterParameter1>,
96-
_: Vec<Self::FilterParameter2>,
100+
work_statuses: Vec<Self::FilterParameter2>,
97101
_: Option<Self::FilterParameter3>,
98102
_: Option<Self::FilterParameter4>,
99103
) -> ThothResult<i32> {
100104
use crate::schema::subject::dsl::*;
101105
let mut connection = db.get()?;
102-
let mut query = subject.into_boxed();
106+
let mut query = subject.inner_join(crate::schema::work::table).into_boxed();
103107
if !subject_types.is_empty() {
104108
query = query.filter(subject_type.eq_any(subject_types));
105109
}
110+
if !work_statuses.is_empty() {
111+
query = query.filter(crate::schema::work::work_status.eq_any(work_statuses));
112+
}
106113
if let Some(filter) = filter {
107114
query = query.filter(subject_code.ilike(format!("%{filter}%")));
108115
}

0 commit comments

Comments
 (0)