Skip to content

Commit 61e7b51

Browse files
authored
mock search API (#117)
adds expanded search capabilities to the mock
2 parents 3e7b032 + 5c0e735 commit 61e7b51

File tree

1 file changed

+147
-158
lines changed
  • src/vit-testing/vitup/src/mode/mock/rest

1 file changed

+147
-158
lines changed

src/vit-testing/vitup/src/mode/mock/rest/search.rs

Lines changed: 147 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use rand::seq::SliceRandom;
2+
use rand::thread_rng;
13
use vit_servicing_station_lib::db::models::proposals::FullProposalInfo;
24
use vit_servicing_station_lib::v0::endpoints::search::requests::*;
35
use vit_servicing_station_lib::{db::models::challenges::Challenge, v0::result::HandlerResult};
@@ -13,14 +15,6 @@ fn make_error(s: &str, code: u16) -> Rejection {
1315
})
1416
}
1517

16-
fn challenge_field_error() -> Rejection {
17-
make_error("`challenge` doesn't support `funds` or `author`", 400)
18-
}
19-
20-
fn proposal_field_error() -> Rejection {
21-
make_error("`proposal` doesn't support `type`", 400)
22-
}
23-
2418
fn mock_order_by_error() -> Rejection {
2519
make_error("Mock implementation only supports 0 or 1 `order_by`s", 400)
2620
}
@@ -73,181 +67,176 @@ async fn search_impl(
7367
};
7468
match table {
7569
Table::Challenges => {
76-
if filter.iter().any(|c| {
77-
matches!(
78-
c,
79-
Constraint::Text {
80-
column: Column::Funds | Column::Author,
81-
..
82-
}
83-
)
84-
}) {
85-
return Err(challenge_field_error());
86-
}
87-
88-
if matches!(
89-
order_by,
90-
Some(OrderBy::Column {
91-
column: Column::Funds | Column::Author,
92-
..
93-
})
94-
) {
95-
return Err(challenge_field_error());
96-
}
97-
98-
let ctx = context.read().unwrap();
99-
let challenges = ctx.state().vit().challenges();
100-
let result = search_challenges(challenges, &filter, order_by);
101-
let result = limit_and_offset(result, limit, offset);
102-
Ok(SearchResponse::Challenge(result))
70+
let results = context.read().unwrap().state().vit().challenges();
71+
let results = filter_challenges(results, &filter)?;
72+
let results = sort_challenges(results, order_by);
73+
let results = limit_and_offset(results, limit, offset);
74+
Ok(SearchResponse::Challenge(results))
10375
}
10476
Table::Proposals => {
105-
if filter.iter().any(|c| {
106-
matches!(
107-
c,
108-
Constraint::Text {
109-
column: Column::Type,
110-
..
111-
}
112-
)
113-
}) {
114-
return Err(proposal_field_error());
115-
}
77+
let results = context.read().unwrap().state().vit().proposals();
78+
let results = filter_proposals(results, &filter)?;
79+
let results = sort_proposals(results, order_by);
80+
let results = limit_and_offset(results, limit, offset);
81+
Ok(SearchResponse::Proposal(results))
82+
}
83+
}
84+
}
11685

117-
if filter.iter().any(|c| {
118-
matches!(
119-
c,
120-
Constraint::Text {
121-
column: Column::Funds,
122-
..
123-
}
124-
)
125-
}) {
126-
return Err(proposal_field_error());
86+
fn filter_challenges(
87+
mut challenges: Vec<Challenge>,
88+
filter: &[Constraint],
89+
) -> Result<Vec<Challenge>, Rejection> {
90+
for f in filter {
91+
use Column::*;
92+
match f {
93+
Constraint::Text { search, column } => {
94+
let search = search.to_lowercase();
95+
let string_function = match column {
96+
Type => |c: &Challenge| c.challenge_type.to_string(),
97+
Title => |c: &Challenge| c.title.clone(),
98+
Desc => |c: &Challenge| c.description.clone(),
99+
Author | Funds | ImpactScore => return Err(make_error("invalid column", 400)),
100+
};
101+
102+
challenges.retain(|c| string_function(c).to_lowercase().contains(&search))
127103
}
128-
129-
if matches!(
130-
order_by,
131-
Some(OrderBy::Column {
132-
column: Column::Type,
133-
..
134-
})
135-
) {
136-
return Err(make_error("can't search proposal by `fund`", 400));
104+
Constraint::Range {
105+
lower,
106+
upper,
107+
column,
108+
} => {
109+
let lower = lower.unwrap_or(i64::MIN);
110+
let upper = upper.unwrap_or(i64::MAX);
111+
112+
let num_function = match column {
113+
Funds => |c: &Challenge| c.rewards_total,
114+
_ => return Err(make_error("invalid column", 400)),
115+
};
116+
117+
challenges.retain(|c| {
118+
let num = num_function(c);
119+
lower <= num && num <= upper
120+
});
137121
}
138-
139-
let ctx = context.read().unwrap();
140-
let proposals = ctx.state().vit().proposals();
141-
let result = search_proposals(proposals, &filter, order_by);
142-
let result = limit_and_offset(result, limit, offset);
143-
Ok(SearchResponse::Proposal(result))
144122
}
145123
}
124+
125+
Ok(challenges)
146126
}
147127

148-
fn limit_and_offset<T>(vec: Vec<T>, limit: Option<u64>, offset: Option<u64>) -> Vec<T> {
149-
let offset = offset.unwrap_or(0);
150-
let limit = limit.unwrap_or(u64::MAX);
151-
vec.into_iter()
152-
.skip(offset as usize)
153-
.take(limit as usize)
154-
.collect()
128+
fn sort_challenges(mut challenges: Vec<Challenge>, order_by: Option<OrderBy>) -> Vec<Challenge> {
129+
match order_by {
130+
None => {}
131+
Some(OrderBy::Random) => challenges.shuffle(&mut thread_rng()),
132+
Some(OrderBy::Column { column, descending }) => {
133+
match column {
134+
Column::Type => challenges.sort_by(|a, b| {
135+
a.challenge_type
136+
.to_string()
137+
.cmp(&b.challenge_type.to_string())
138+
}),
139+
Column::Desc => challenges.sort_by(|a, b| a.description.cmp(&b.description)),
140+
Column::Title => challenges.sort_by(|a, b| a.title.cmp(&b.title)),
141+
_ => {}
142+
}
143+
144+
if descending {
145+
challenges.reverse();
146+
}
147+
}
148+
};
149+
150+
challenges
155151
}
156152

157-
fn search_proposals(
153+
fn filter_proposals(
158154
mut proposals: Vec<FullProposalInfo>,
159155
filter: &[Constraint],
160-
order_by: Option<OrderBy>,
161-
) -> Vec<FullProposalInfo> {
162-
fn is_match(proposal: &FullProposalInfo, constraint: &Constraint) -> bool {
163-
let Constraint::Text { column, search } = constraint else { todo!() };
164-
let string = match column {
165-
Column::Desc => &proposal.proposal.proposal_summary,
166-
Column::Title => &proposal.proposal.proposal_title,
167-
Column::Author => &proposal.proposal.proposer.proposer_name,
168-
_ => return false,
169-
};
170-
171-
string.to_lowercase().contains(&search.to_lowercase())
172-
}
173-
174-
let should_retain = |p: &FullProposalInfo| filter.iter().all(|cons| is_match(p, cons));
175-
proposals.retain(should_retain);
176-
177-
if let Some(OrderBy::Column { column, descending }) = order_by {
178-
match column {
179-
Column::Desc => proposals.sort_by(|a, b| {
180-
a.proposal
181-
.proposal_summary
182-
.cmp(&b.proposal.proposal_summary)
183-
}),
184-
Column::Title => {
185-
proposals.sort_by(|a, b| a.proposal.proposal_title.cmp(&b.proposal.proposal_title))
156+
) -> Result<Vec<FullProposalInfo>, Rejection> {
157+
for f in filter {
158+
use Column::*;
159+
match f {
160+
Constraint::Text { search, column } => {
161+
let search = search.to_lowercase();
162+
let string_function = match column {
163+
Type => |p: &FullProposalInfo| p.challenge_type.to_string(),
164+
Title => |p: &FullProposalInfo| p.proposal.proposal_title.clone(),
165+
Desc => |p: &FullProposalInfo| p.proposal.proposal_summary.clone(),
166+
Author => |p: &FullProposalInfo| p.proposal.proposer.proposer_name.clone(),
167+
Funds | ImpactScore => return Err(make_error("invalid column", 400)),
168+
};
169+
170+
proposals.retain(|p| string_function(p).to_lowercase().contains(&search))
186171
}
187-
Column::Funds => {
188-
proposals.sort_by(|a, b| a.proposal.proposal_funds.cmp(&b.proposal.proposal_funds))
172+
Constraint::Range {
173+
lower,
174+
upper,
175+
column,
176+
} => {
177+
let lower = lower.unwrap_or(i64::MIN);
178+
let upper = upper.unwrap_or(i64::MAX);
179+
180+
let num_function = match column {
181+
Funds => |p: &FullProposalInfo| p.proposal.proposal_funds,
182+
ImpactScore => |p: &FullProposalInfo| p.proposal.proposal_impact_score,
183+
_ => return Err(make_error("invalid column", 400)),
184+
};
185+
186+
proposals.retain(|p| {
187+
let num = num_function(p);
188+
lower <= num && num <= upper
189+
});
189190
}
190-
Column::Author => proposals.sort_by(|a, b| {
191-
a.proposal
192-
.proposer
193-
.proposer_name
194-
.cmp(&b.proposal.proposer.proposer_name)
195-
}),
196-
_ => {}
197-
};
198-
199-
if descending {
200-
proposals.reverse();
201191
}
202192
}
203193

204-
proposals
194+
Ok(proposals)
205195
}
206196

207-
fn search_challenges(
208-
mut challenges: Vec<Challenge>,
209-
filter: &[Constraint],
197+
fn sort_proposals(
198+
mut proposals: Vec<FullProposalInfo>,
210199
order_by: Option<OrderBy>,
211-
) -> Vec<Challenge> {
212-
fn is_match(challenge: &Challenge, constraint: &Constraint) -> bool {
213-
let Constraint::Text { search, column } = constraint else { todo!() };
214-
let string = match column {
215-
Column::Type => {
216-
return challenge
217-
.challenge_type
218-
.to_string()
219-
.to_lowercase()
220-
.contains(&challenge.challenge_type.to_string())
200+
) -> Vec<FullProposalInfo> {
201+
match order_by {
202+
None => {}
203+
Some(OrderBy::Random) => proposals.shuffle(&mut thread_rng()),
204+
Some(OrderBy::Column { column, descending }) => {
205+
match column {
206+
Column::Desc => proposals.sort_by(|a, b| {
207+
a.proposal
208+
.proposal_summary
209+
.cmp(&b.proposal.proposal_summary)
210+
}),
211+
Column::Title => proposals
212+
.sort_by(|a, b| a.proposal.proposal_title.cmp(&b.proposal.proposal_title)),
213+
Column::Funds => proposals
214+
.sort_by(|a, b| a.proposal.proposal_funds.cmp(&b.proposal.proposal_funds)),
215+
Column::Author => proposals.sort_by(|a, b| {
216+
a.proposal
217+
.proposer
218+
.proposer_name
219+
.cmp(&b.proposal.proposer.proposer_name)
220+
}),
221+
_ => {}
222+
};
223+
224+
if descending {
225+
proposals.reverse();
221226
}
222-
Column::Desc => &challenge.description,
223-
Column::Title => &challenge.title,
224-
_ => return false,
225-
};
226-
227-
string.to_lowercase().contains(&search.to_lowercase())
228-
}
229-
230-
let should_retain = |c: &Challenge| filter.iter().all(|cons| is_match(c, cons));
231-
challenges.retain(should_retain);
232-
233-
if let Some(OrderBy::Column { column, descending }) = order_by {
234-
match column {
235-
Column::Type => challenges.sort_by(|a, b| {
236-
a.challenge_type
237-
.to_string()
238-
.cmp(&b.challenge_type.to_string())
239-
}),
240-
Column::Desc => challenges.sort_by(|a, b| a.description.cmp(&b.description)),
241-
Column::Title => challenges.sort_by(|a, b| a.title.cmp(&b.title)),
242-
_ => {}
243-
}
244-
245-
if descending {
246-
challenges.reverse();
247227
}
248228
}
249229

250-
challenges
230+
proposals
231+
}
232+
233+
fn limit_and_offset<T>(vec: Vec<T>, limit: Option<u64>, offset: Option<u64>) -> Vec<T> {
234+
let offset = offset.unwrap_or(0);
235+
let limit = limit.unwrap_or(u64::MAX);
236+
vec.into_iter()
237+
.skip(offset as usize)
238+
.take(limit as usize)
239+
.collect()
251240
}
252241

253242
#[cfg(test)]

0 commit comments

Comments
 (0)