use crate::errors::OrionError;
pub(crate) async fn ensure_workflow_is_active(
workflows: &dyn crate::storage::repositories::workflows::WorkflowRepository,
draft: &crate::storage::models::Channel,
) -> Result<(), OrionError> {
let Some(workflow_id) = draft
.workflow_id
.as_deref()
.filter(|w| !w.trim().is_empty())
else {
return Err(OrionError::validation(format!(
"Cannot activate channel '{}': it names no workflow_id, so it would be \
quarantined at load and never serve. Set workflow_id first.",
draft.name
)));
};
let has_active = workflows
.list_active()
.await?
.iter()
.any(|w| w.workflow_id == workflow_id);
if !has_active {
let detail = match workflows.get_by_id(workflow_id).await {
Ok(_) => "has no active version",
Err(OrionError::NotFound(_)) => "does not exist",
Err(e) => return Err(e),
};
return Err(OrionError::validation(format!(
"Cannot activate channel '{}': workflow '{workflow_id}' {detail} — \
activate the workflow first",
draft.name
)));
}
Ok(())
}
pub(crate) async fn ensure_name_is_unclaimed(
channels: &dyn crate::storage::repositories::channels::ChannelRepository,
draft: &crate::storage::models::Channel,
) -> Result<(), OrionError> {
if let Some(holder) = channels
.active_name_holder(&draft.name, &draft.channel_id)
.await?
{
return Err(OrionError::Conflict(format!(
"Cannot activate channel '{}': active channel id '{holder}' already uses \
that name, and the data plane addresses channels by name. Rename one \
of the two first.",
draft.name
)));
}
Ok(())
}
pub(crate) async fn ensure_route_is_unclaimed(
channels: &dyn crate::storage::repositories::channels::ChannelRepository,
data_mounts: &[String],
draft: &crate::storage::models::Channel,
) -> Result<(), OrionError> {
use crate::channel::routing::declared_route;
let Some((route, methods)) = declared_route(draft) else {
return Ok(());
};
for mount in data_mounts {
let served = if mount == "/" {
route.clone()
} else {
format!("{mount}{route}")
};
if let Some(platform) = crate::server::routes::shadowed_platform_route(&served) {
return Err(OrionError::validation(format!(
"Cannot activate channel '{}': with server.data_mounts entry '{mount}' \
its route would be served at {served}, which collides with the \
platform route '{platform}'. The platform route wins, so the channel \
would never run. Change the route_pattern or the mount.",
draft.name,
)));
}
}
for other in channels.list_active().await? {
if other.channel_id == draft.channel_id {
continue; }
let Some((other_route, other_methods)) = declared_route(&other) else {
continue;
};
if other_route == route
&& other.priority == draft.priority
&& crate::channel::routing::methods_overlap(&methods, &other_methods)
{
return Err(OrionError::validation(format!(
"Cannot activate channel '{}': active channel '{}' (id {}) already \
claims {} {route} at priority {}. Requests to that path would run one \
of the two arbitrarily. Change the route_pattern, narrow the methods, \
or give one a higher priority.",
draft.name,
other.name,
other.channel_id,
if methods.is_empty() {
"every method on".to_string()
} else {
methods.join("/")
},
draft.priority,
)));
}
}
Ok(())
}