use cinderblock_core::{
Context, resource,
serde::{Deserialize, Serialize},
};
use uuid::Uuid;
resource! {
name = Helpdesk.Support.Agent;
attributes {
agent_id Uuid {
primary_key true;
writable false;
default || uuid::Uuid::new_v4();
}
name String;
}
relations {
has_many tickets {
ty Ticket;
source_attribute agent_id;
};
}
actions {
read all_agents;
read all_agents_with_tickets {
load [tickets];
};
create add_agent;
}
extensions {
cinderblock_json_api {
route = { method = GET; path = "/"; action = all_agents; };
route = { method = GET; path = "/with-tickets"; action = all_agents_with_tickets; };
route = { method = POST; path = "/"; action = add_agent; };
};
}
}
resource! {
name = Helpdesk.Support.Ticket;
attributes {
ticket_id Uuid {
primary_key true;
writable false;
default || uuid::Uuid::new_v4();
}
subject String;
status TicketStatus;
agent_id Uuid;
}
relations {
belongs_to agent {
ty Agent;
source_attribute agent_id;
};
}
actions {
read all {
argument {
status: TicketStatus,
};
filter { status == arg(status) };
};
read all_with_agent {
load [agent];
};
read one {
get;
};
create open;
create assign {
accept [subject, agent_id];
};
update close {
accept [];
change_ref |ticket| {
ticket.status = TicketStatus::Closed;
};
};
destroy remove;
}
extensions {
cinderblock_json_api {
route = { method = GET; path = "/"; action = all; };
route = { method = GET; path = "/with-agent"; action = all_with_agent; };
route = { method = GET; path = "/{primary_key}"; action = one; };
route = { method = POST; path = "/"; action = open; };
route = { method = POST; path = "/assign"; action = assign; };
route = { method = PATCH; path = "/{primary_key}"; action = close; };
route = { method = DELETE; path = "/{primary_key}"; action = remove; };
};
}
}
#[derive(
Debug,
Clone,
Default,
PartialEq,
Eq,
Serialize,
Deserialize,
cinderblock_json_api::utoipa::ToSchema,
)]
enum TicketStatus {
#[default]
Open,
Closed,
}
cinderblock_json_api::impl_field_schema!(TicketStatus);
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
tracing_subscriber::fmt::init();
let ctx = Context::new();
let alice = cinderblock_core::create::<Agent, AddAgent>(
AddAgentInput {
name: "Alice".to_string(),
},
&ctx,
)
.await?;
let bob = cinderblock_core::create::<Agent, AddAgent>(
AddAgentInput {
name: "Bob".to_string(),
},
&ctx,
)
.await?;
cinderblock_core::create::<Ticket, Open>(
OpenInput {
subject: "The computer does not turn on".to_string(),
status: TicketStatus::Open,
agent_id: alice.agent_id,
},
&ctx,
)
.await?;
cinderblock_core::create::<Ticket, Assign>(
AssignInput {
subject: "My mouse does not work".to_string(),
agent_id: alice.agent_id,
},
&ctx,
)
.await?;
cinderblock_core::create::<Ticket, Open>(
OpenInput {
subject: "VPN keeps disconnecting".to_string(),
status: TicketStatus::Open,
agent_id: bob.agent_id,
},
&ctx,
)
.await?;
println!("Seeded 2 agents and 3 tickets");
let first_ticket = cinderblock_core::read::<Ticket, All>(
&ctx,
&AllArguments {
status: TicketStatus::Open,
},
)
.await
.expect("Failed to list tickets");
if let Some(ticket) = first_ticket.first() {
let fetched = cinderblock_core::read_one::<Ticket, One>(&ctx, &ticket.ticket_id)
.await
.expect("Failed to fetch single ticket");
println!(
"Fetched ticket by ID: {} — {}",
fetched.ticket_id, fetched.subject
);
}
let router = cinderblock_json_api::router(ctx);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
println!("Listening on http://0.0.0.0:3000");
println!();
println!("Try:");
println!();
println!(" # List agents (plain)");
println!(" curl http://localhost:3000/helpdesk/support/agent/");
println!();
println!(" # List agents with their tickets loaded (has_many)");
println!(" curl http://localhost:3000/helpdesk/support/agent/with-tickets");
println!();
println!(" # List tickets filtered by status");
println!(" curl http://localhost:3000/helpdesk/support/ticket/?status=Open");
println!();
println!(" # List tickets with their agent loaded (belongs_to)");
println!(" curl http://localhost:3000/helpdesk/support/ticket/with-agent");
println!();
println!(" # Fetch a single ticket by ID");
println!(" curl http://localhost:3000/helpdesk/support/ticket/{{ticket-uuid-here}}");
println!();
println!(" # Create a ticket");
println!(" curl -X POST http://localhost:3000/helpdesk/support/ticket/ \\");
println!(" -H 'Content-Type: application/json' \\");
println!(
" -d '{{\"subject\": \"New ticket\", \"status\": \"Open\", \"agent_id\": \"{}\"}}'",
alice.agent_id,
);
println!();
println!("OpenAPI spec:");
println!(" curl http://localhost:3000/openapi.json");
axum::serve(listener, router).await?;
Ok(())
}