use std::collections::BTreeMap;
use serde_json::Value;
use crate::auth::AuthClaims;
use crate::error::AppError;
use vta_sdk::provision_integration::{BootstrapAsk, DidTemplateRef, VerifiedBootstrapRequest};
use super::ProvisionIntegrationDeps;
pub(super) async fn preconditions(
state: &ProvisionIntegrationDeps,
auth: &AuthClaims,
context: &str,
request: &VerifiedBootstrapRequest,
) -> Result<(), AppError> {
auth.require_admin()?;
auth.require_context(context)?;
if crate::contexts::get_context(&state.contexts_ks, context)
.await?
.is_none()
{
return Err(AppError::NotFound(format!(
"context '{context}' is not registered on this VTA — create it first via \
'vta context create --id {context}' (offline) or 'pnm contexts create' (online), \
or pass '--create-context' to provision it inline"
)));
}
let hint = match request.ask() {
BootstrapAsk::TemplateBootstrap(ask) => ask.context_hint.as_deref(),
BootstrapAsk::AdminRotation(ask) => ask.context_hint.as_deref(),
};
if let Some(hint) = hint
&& hint != context
{
return Err(AppError::Validation(format!(
"request contextHint '{hint}' does not match provisioning context '{context}'"
)));
}
let (integration_template_name, admin_template_name): (Option<String>, Option<String>) =
match request.ask() {
BootstrapAsk::TemplateBootstrap(ask) => (
Some(ask.template.name.clone()),
ask.admin_template.as_ref().map(|t| t.name.clone()),
),
BootstrapAsk::AdminRotation(ask) => (None, Some(ask.admin_template.name.clone())),
};
if let Some(template_name) = integration_template_name.as_deref() {
let template_registered = crate::did_templates::get_context_template(
&state.did_templates_ks,
context,
template_name,
)
.await?
.is_some()
|| crate::did_templates::get_global_template(&state.did_templates_ks, template_name)
.await?
.is_some()
|| vta_sdk::did_templates::load_embedded(template_name).is_ok();
if !template_registered {
return Err(AppError::Validation(format!(
"template '{template_name}' is not registered on this VTA. Register it via \
'pnm did-templates upload {template_name} --file <path>' then retry"
)));
}
}
if let Some(name) = admin_template_name {
let registered =
crate::did_templates::get_context_template(&state.did_templates_ks, context, &name)
.await?
.is_some()
|| crate::did_templates::get_global_template(&state.did_templates_ks, &name)
.await?
.is_some()
|| vta_sdk::did_templates::load_embedded(&name).is_ok();
if !registered {
return Err(AppError::Validation(format!(
"admin template '{name}' is not registered on this VTA. Register it via \
'pnm did-templates upload {name} --file <path>' then retry, or use the \
built-in 'vta-admin' template."
)));
}
}
Ok(())
}
pub(super) fn extract_template(
ask: &BootstrapAsk,
) -> Result<Option<(String, BTreeMap<String, Value>)>, AppError> {
match ask {
BootstrapAsk::TemplateBootstrap(ask) => {
Ok(Some((ask.template.name.clone(), ask.template.vars.clone())))
}
BootstrapAsk::AdminRotation(_) => Ok(None),
}
}
pub(super) fn extract_admin_template(ask: &BootstrapAsk) -> Option<DidTemplateRef> {
match ask {
BootstrapAsk::TemplateBootstrap(ask) => ask.admin_template.clone(),
BootstrapAsk::AdminRotation(ask) => Some(ask.admin_template.clone()),
}
}