use super::*;
#[derive(Debug, Default, Clone, Copy)]
pub struct LogFilter<'a> {
pub stage: Option<&'a str>,
pub trace: Option<&'a str>,
pub stream: Option<&'a str>,
pub tail: Option<usize>,
}
impl ApiClient {
pub async fn deployments(&self, service_environment_id: &str) -> Result<Vec<Deployment>> {
Ok(self
.get::<DeploymentsPage>(&format!(
"/api/infra/services/{service_environment_id}/deployments"
))
.await?
.deployments)
}
pub async fn deployments_of_commit(
&self,
service_environment_id: &str,
commit: &str,
) -> Result<Vec<Deployment>> {
Ok(self
.get::<DeploymentsPage>(&format!(
"/api/infra/services/{service_environment_id}/deployments?commit={commit}"
))
.await?
.deployments)
}
pub async fn commit_change(
&self,
service_environment_id: &str,
sha: &str,
) -> Result<CommitChangeResponse> {
self.get(&format!(
"/api/infra/services/{service_environment_id}/commits/{sha}"
))
.await
}
pub async fn deployment(&self, deployment_id: &str) -> Result<DeploymentDetail> {
self.get(&format!("/api/infra/deployments/{deployment_id}"))
.await
}
pub async fn incidents(
&self,
service_environment_id: &str,
decided: bool,
) -> Result<Vec<Incident>> {
Ok(self
.get::<IncidentsPage>(&format!(
"/api/infra/services/{service_environment_id}/incidents?decided={decided}"
))
.await?
.incidents)
}
pub async fn decide_incident(&self, incident_id: &str, status: &str) -> Result<Incident> {
self.send(
Method::PATCH,
&format!("/api/infra/incidents/{incident_id}"),
Some(&serde_json::json!({ "status": status })),
)
.await
}
pub async fn project_approvals(&self, project_id: &str) -> Result<Vec<Approval>> {
Ok(self
.get::<ApprovalsPage>(&format!("/api/infra/projects/{project_id}/approvals"))
.await?
.approvals)
}
pub async fn service_approvals(&self, service_environment_id: &str) -> Result<Vec<Approval>> {
Ok(self
.get::<ApprovalsPage>(&format!(
"/api/infra/services/{service_environment_id}/approvals"
))
.await?
.approvals)
}
pub async fn approve_deployment(&self, approval_id: &str) -> Result<Deployment> {
Ok(self
.post_empty::<DeploymentAccepted>(&format!(
"/api/infra/approvals/{approval_id}/approve"
))
.await?
.deployment)
}
pub async fn reject_deployment(&self, approval_id: &str) -> Result<serde_json::Value> {
self.post_empty(&format!("/api/infra/approvals/{approval_id}/reject"))
.await
}
pub async fn autofix_runs(&self, service_environment_id: &str) -> Result<Vec<AutofixRun>> {
Ok(self
.get::<AutofixPage>(&format!(
"/api/infra/services/{service_environment_id}/autofix"
))
.await?
.runs)
}
pub async fn set_autofix(
&self,
service_environment_id: &str,
changes: &AutofixPatch,
) -> Result<serde_json::Value> {
self.put(
&format!("/api/infra/services/{service_environment_id}/autofix"),
changes,
)
.await
}
pub async fn fix_incident(&self, incident_id: &str) -> Result<AutofixRun> {
self.post_empty(&format!("/api/infra/incidents/{incident_id}/fix"))
.await
}
pub async fn deploy(&self, service_environment_id: &str) -> Result<Deployment> {
Ok(self
.post_empty::<DeploymentAccepted>(&self.released(&format!(
"/api/infra/services/{service_environment_id}/deploy"
)))
.await?
.deployment)
}
pub async fn upload(
&self,
service_environment_id: &str,
archive: Vec<u8>,
file_name: &str,
deploy: bool,
) -> Result<UploadAccepted> {
let path = self.released(&format!(
"/api/infra/services/{service_environment_id}/uploads{}",
if deploy { "" } else { "?deploy=false" }
));
let response = self
.unbounded_request(Method::POST, &path, None::<&()>)
.header("content-type", "application/gzip")
.header("x-fundaia-upload-name", file_name)
.body(archive)
.send()
.await?;
let text = Self::check(response).await?;
serde_json::from_str(&text)
.with_context(|| format!("could not read the answer from {path}"))
}
pub async fn uploads(&self, service_environment_id: &str) -> Result<UploadsPage> {
self.get(&format!(
"/api/infra/services/{service_environment_id}/uploads"
))
.await
}
pub async fn restart(&self, service_environment_id: &str) -> Result<Deployment> {
Ok(self
.post_empty::<DeploymentAccepted>(&self.released(&format!(
"/api/infra/services/{service_environment_id}/restart"
)))
.await?
.deployment)
}
pub async fn stop(&self, service_environment_id: &str) -> Result<()> {
let response = self
.request(
Method::POST,
&self.released(&format!(
"/api/infra/services/{service_environment_id}/stop"
)),
Some(&serde_json::json!({})),
)
.send()
.await?;
Self::check(response).await?;
Ok(())
}
pub async fn redeploy(&self, deployment_id: &str) -> Result<Deployment> {
Ok(self
.post_empty::<DeploymentAccepted>(
&self.released(&format!("/api/infra/deployments/{deployment_id}/redeploy")),
)
.await?
.deployment)
}
pub async fn rollback(&self, deployment_id: &str) -> Result<Deployment> {
Ok(self
.post_empty::<DeploymentAccepted>(
&self.released(&format!("/api/infra/deployments/{deployment_id}/rollback")),
)
.await?
.deployment)
}
pub async fn cancel(&self, deployment_id: &str) -> Result<DeploymentDetail> {
self.post_empty(&format!("/api/infra/deployments/{deployment_id}/cancel"))
.await
}
pub async fn stream_log<F>(
&self,
deployment_id: &str,
after: u64,
stage: Option<&str>,
mut on_line: F,
) -> Result<()>
where
F: FnMut(LogLine),
{
let path = format!(
"/api/infra/deployments/{deployment_id}/log/stream?after={after}{}",
stage_query(stage)
);
self.read_events(&path, |event| {
if let Some(line) = parse_log_event(event) {
on_line(line);
}
if event.contains("event: end") {
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
})
.await
}
pub async fn read_log(
&self,
deployment_id: &str,
after: u64,
filter: LogFilter<'_>,
) -> Result<Vec<LogLine>> {
Ok(self
.get::<LogPage>(&format!(
"/api/infra/deployments/{deployment_id}/log?after={after}{}{}{}{}",
stage_query(filter.stage),
trace_query(filter.trace),
stream_query(filter.stream),
tail_query(filter.tail)
))
.await?
.lines)
}
}