ticket: use list state tokens in query
This commit is contained in:
parent
39be7a5a0a
commit
556adb429c
|
|
@ -510,6 +510,53 @@ impl NewTicket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum TicketListState {
|
||||||
|
Planning,
|
||||||
|
Ready,
|
||||||
|
Queued,
|
||||||
|
InProgress,
|
||||||
|
Done,
|
||||||
|
Closed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TicketListState {
|
||||||
|
pub fn parse(value: &str) -> Option<Self> {
|
||||||
|
match value {
|
||||||
|
"planning" => Some(Self::Planning),
|
||||||
|
"ready" => Some(Self::Ready),
|
||||||
|
"queued" => Some(Self::Queued),
|
||||||
|
"inprogress" => Some(Self::InProgress),
|
||||||
|
"done" => Some(Self::Done),
|
||||||
|
"closed" => Some(Self::Closed),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_str(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Planning => "planning",
|
||||||
|
Self::Ready => "ready",
|
||||||
|
Self::Queued => "queued",
|
||||||
|
Self::InProgress => "inprogress",
|
||||||
|
Self::Done => "done",
|
||||||
|
Self::Closed => "closed",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn matches_workflow_state(self, state: TicketWorkflowState) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Planning => state == TicketWorkflowState::Planning,
|
||||||
|
Self::Ready => state == TicketWorkflowState::Ready,
|
||||||
|
Self::Queued => state == TicketWorkflowState::Queued,
|
||||||
|
Self::InProgress => state == TicketWorkflowState::InProgress,
|
||||||
|
Self::Done => state == TicketWorkflowState::Done,
|
||||||
|
Self::Closed => state == TicketWorkflowState::Closed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum TicketStateSelector {
|
pub enum TicketStateSelector {
|
||||||
|
|
@ -517,8 +564,8 @@ pub enum TicketStateSelector {
|
||||||
Active,
|
Active,
|
||||||
/// Every workflow state, including closed.
|
/// Every workflow state, including closed.
|
||||||
All,
|
All,
|
||||||
/// An explicit set of workflow states.
|
/// An explicit set of list-query state tokens.
|
||||||
States(BTreeSet<TicketWorkflowState>),
|
States(BTreeSet<TicketListState>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TicketStateSelector {
|
impl Default for TicketStateSelector {
|
||||||
|
|
@ -551,11 +598,11 @@ impl TicketListQuery {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn state(state: TicketWorkflowState) -> Self {
|
pub fn state(state: TicketListState) -> Self {
|
||||||
Self::states([state])
|
Self::states([state])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn states(states: impl IntoIterator<Item = TicketWorkflowState>) -> Self {
|
pub fn states(states: impl IntoIterator<Item = TicketListState>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: TicketStateSelector::States(states.into_iter().collect()),
|
state: TicketStateSelector::States(states.into_iter().collect()),
|
||||||
}
|
}
|
||||||
|
|
@ -565,7 +612,9 @@ impl TicketListQuery {
|
||||||
match &self.state {
|
match &self.state {
|
||||||
TicketStateSelector::Active => state != TicketWorkflowState::Closed,
|
TicketStateSelector::Active => state != TicketWorkflowState::Closed,
|
||||||
TicketStateSelector::All => true,
|
TicketStateSelector::All => true,
|
||||||
TicketStateSelector::States(states) => states.contains(&state),
|
TicketStateSelector::States(states) => states
|
||||||
|
.iter()
|
||||||
|
.any(|query_state| query_state.matches_workflow_state(state)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4060,15 +4109,15 @@ state: planning
|
||||||
assert!(all_ids.contains(&closed.id.as_str()));
|
assert!(all_ids.contains(&closed.id.as_str()));
|
||||||
|
|
||||||
let ready_only = backend
|
let ready_only = backend
|
||||||
.list(TicketListQuery::state(TicketWorkflowState::Ready))
|
.list(TicketListQuery::state(TicketListState::Ready))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(ready_only.len(), 1);
|
assert_eq!(ready_only.len(), 1);
|
||||||
assert_eq!(ready_only[0].id, ready.id);
|
assert_eq!(ready_only[0].id, ready.id);
|
||||||
|
|
||||||
let planning_or_closed = backend
|
let planning_or_closed = backend
|
||||||
.list(TicketListQuery::states([
|
.list(TicketListQuery::states([
|
||||||
TicketWorkflowState::Planning,
|
TicketListState::Planning,
|
||||||
TicketWorkflowState::Closed,
|
TicketListState::Closed,
|
||||||
]))
|
]))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let explicit_ids = planning_or_closed
|
let explicit_ids = planning_or_closed
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ use crate::{
|
||||||
NewTicket, NewTicketEvent, NewTicketRelation, OrchestrationPlanKind, OrchestrationPlanRecord,
|
NewTicket, NewTicketEvent, NewTicketRelation, OrchestrationPlanKind, OrchestrationPlanRecord,
|
||||||
Result as TicketResult, Ticket, TicketBackend, TicketDoctorDiagnostic, TicketDoctorReport,
|
Result as TicketResult, Ticket, TicketBackend, TicketDoctorDiagnostic, TicketDoctorReport,
|
||||||
TicketDoctorSeverity, TicketError, TicketEventKind, TicketIdOrSlug, TicketIntakeSummary,
|
TicketDoctorSeverity, TicketError, TicketEventKind, TicketIdOrSlug, TicketIntakeSummary,
|
||||||
TicketRef, TicketRelation, TicketRelationKind, TicketRelationView, TicketReview,
|
TicketListState, TicketRef, TicketRelation, TicketRelationKind, TicketRelationView,
|
||||||
TicketReviewResult, TicketStateChange, TicketSummary, TicketWorkflowState,
|
TicketReview, TicketReviewResult, TicketStateChange, TicketSummary, TicketWorkflowState,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_LIST_LIMIT: usize = 50;
|
const DEFAULT_LIST_LIMIT: usize = 50;
|
||||||
|
|
@ -373,6 +373,17 @@ impl TicketWorkflowStateParam {
|
||||||
Self::Closed => TicketWorkflowState::Closed,
|
Self::Closed => TicketWorkflowState::Closed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn into_list_state(self) -> TicketListState {
|
||||||
|
match self {
|
||||||
|
Self::Planning => TicketListState::Planning,
|
||||||
|
Self::Ready => TicketListState::Ready,
|
||||||
|
Self::Queued => TicketListState::Queued,
|
||||||
|
Self::Inprogress => TicketListState::InProgress,
|
||||||
|
Self::Done => TicketListState::Done,
|
||||||
|
Self::Closed => TicketListState::Closed,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Deserialize, schemars::JsonSchema)]
|
#[derive(Debug, Clone, Copy, Deserialize, schemars::JsonSchema)]
|
||||||
|
|
@ -389,14 +400,14 @@ enum TicketListStateParam {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TicketListStateParam {
|
impl TicketListStateParam {
|
||||||
fn as_state(self) -> Option<TicketWorkflowState> {
|
fn as_list_state(self) -> Option<TicketListState> {
|
||||||
match self {
|
match self {
|
||||||
Self::Planning => Some(TicketWorkflowState::Planning),
|
Self::Planning => Some(TicketListState::Planning),
|
||||||
Self::Ready => Some(TicketWorkflowState::Ready),
|
Self::Ready => Some(TicketListState::Ready),
|
||||||
Self::Queued => Some(TicketWorkflowState::Queued),
|
Self::Queued => Some(TicketListState::Queued),
|
||||||
Self::Inprogress => Some(TicketWorkflowState::InProgress),
|
Self::Inprogress => Some(TicketListState::InProgress),
|
||||||
Self::Done => Some(TicketWorkflowState::Done),
|
Self::Done => Some(TicketListState::Done),
|
||||||
Self::Closed => Some(TicketWorkflowState::Closed),
|
Self::Closed => Some(TicketListState::Closed),
|
||||||
Self::Active | Self::All => None,
|
Self::Active | Self::All => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -428,15 +439,15 @@ impl TicketListParams {
|
||||||
"TicketList `states` must include at least one workflow state".to_string(),
|
"TicketList `states` must include at least one workflow state".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
crate::TicketListQuery::states(states.into_iter().map(|state| state.into_state()))
|
crate::TicketListQuery::states(states.into_iter().map(|state| state.into_list_state()))
|
||||||
} else {
|
} else {
|
||||||
match self.state.unwrap_or(TicketListStateParam::Active) {
|
match self.state.unwrap_or(TicketListStateParam::Active) {
|
||||||
TicketListStateParam::Active => crate::TicketListQuery::active(),
|
TicketListStateParam::Active => crate::TicketListQuery::active(),
|
||||||
TicketListStateParam::All => crate::TicketListQuery::all(),
|
TicketListStateParam::All => crate::TicketListQuery::all(),
|
||||||
state => crate::TicketListQuery::state(
|
state => crate::TicketListQuery::state(
|
||||||
state
|
state
|
||||||
.as_state()
|
.as_list_state()
|
||||||
.expect("workflow state list param maps to TicketWorkflowState"),
|
.expect("workflow state list param maps to TicketListState"),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ use ticket::config::{
|
||||||
use ticket::{
|
use ticket::{
|
||||||
LocalTicketBackend, MarkdownText, NewTicket, NewTicketEvent, NewTicketRelation, TicketBackend,
|
LocalTicketBackend, MarkdownText, NewTicket, NewTicketEvent, NewTicketRelation, TicketBackend,
|
||||||
TicketDoctorSeverity, TicketEventKind, TicketIdOrSlug, TicketIntakeSummary, TicketListQuery,
|
TicketDoctorSeverity, TicketEventKind, TicketIdOrSlug, TicketIntakeSummary, TicketListQuery,
|
||||||
TicketRelationKind, TicketReview, TicketReviewResult, TicketSummary, TicketWorkflowState,
|
TicketListState, TicketRelationKind, TicketReview, TicketReviewResult, TicketSummary,
|
||||||
|
TicketWorkflowState,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_LIST_LIMIT: usize = 50;
|
const DEFAULT_LIST_LIMIT: usize = 50;
|
||||||
|
|
@ -49,7 +50,7 @@ pub struct CreateOptions {
|
||||||
pub enum ListState {
|
pub enum ListState {
|
||||||
Active,
|
Active,
|
||||||
All,
|
All,
|
||||||
States(Vec<TicketWorkflowState>),
|
States(Vec<TicketListState>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
|
@ -1060,7 +1061,7 @@ fn parse_list_state(raw: &str) -> Result<ListState, TicketCliError> {
|
||||||
|
|
||||||
let mut states = Vec::new();
|
let mut states = Vec::new();
|
||||||
for token in tokens {
|
for token in tokens {
|
||||||
let state = TicketWorkflowState::parse(token).ok_or_else(|| {
|
let state = TicketListState::parse(token).ok_or_else(|| {
|
||||||
TicketCliError::new(format!(
|
TicketCliError::new(format!(
|
||||||
"invalid state: {token}; expected active, all, planning, ready, queued, inprogress, done, closed"
|
"invalid state: {token}; expected active, all, planning, ready, queued, inprogress, done, closed"
|
||||||
))
|
))
|
||||||
|
|
@ -1554,18 +1555,17 @@ mod tests {
|
||||||
fn ticket_cli_list_defaults_to_active_and_accepts_multi_state_filter() {
|
fn ticket_cli_list_defaults_to_active_and_accepts_multi_state_filter() {
|
||||||
let default = parse_ticket_args(&args(&["list"])).unwrap();
|
let default = parse_ticket_args(&args(&["list"])).unwrap();
|
||||||
match default {
|
match default {
|
||||||
TicketCommand::List(options) => assert_eq!(options.state, ListState::Active),
|
TicketCli::Command(TicketCommand::List(options)) => {
|
||||||
|
assert_eq!(options.state, ListState::Active)
|
||||||
|
}
|
||||||
other => panic!("unexpected command: {other:?}"),
|
other => panic!("unexpected command: {other:?}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
let explicit = parse_ticket_args(&args(&["list", "--state", "planning,closed"])).unwrap();
|
let explicit = parse_ticket_args(&args(&["list", "--state", "planning,closed"])).unwrap();
|
||||||
match explicit {
|
match explicit {
|
||||||
TicketCommand::List(options) => assert_eq!(
|
TicketCli::Command(TicketCommand::List(options)) => assert_eq!(
|
||||||
options.state,
|
options.state,
|
||||||
ListState::States(vec![
|
ListState::States(vec![TicketListState::Planning, TicketListState::Closed])
|
||||||
TicketWorkflowState::Planning,
|
|
||||||
TicketWorkflowState::Closed
|
|
||||||
])
|
|
||||||
),
|
),
|
||||||
other => panic!("unexpected command: {other:?}"),
|
other => panic!("unexpected command: {other:?}"),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user