use anyhow::Context;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use super::{entity::EntityDescriptorConst, AnyEntity, WorkloadV2};
#[derive(Serialize, Deserialize, JsonSchema, PartialEq, Eq, Clone, Debug)]
pub struct AppVersionV1Spec {
pub app_id: String,
pub app_version_id: String,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub aliases: Vec<String>,
pub workload: WorkloadV2,
}
#[derive(Serialize, Deserialize, JsonSchema, PartialEq, Eq, Clone, Debug)]
pub struct AppVersionV1State {}
impl EntityDescriptorConst for AppVersionV1Spec {
const NAMESPACE: &'static str = "wasmer.io";
const NAME: &'static str = "AppVersion";
const VERSION: &'static str = "1";
const KIND: &'static str = "wasmer.io/AppVersion.v1";
type Spec = AppVersionV1Spec;
type State = AppVersionV1State;
}
pub type AppVersionV1 = super::Entity<AppVersionV1Spec, AnyEntity>;
pub fn parse_upcast_app_version_yaml(yaml: &str) -> Result<AppVersionV1, anyhow::Error> {
let value: serde_yaml::Value =
serde_yaml::from_str(yaml).context("could not parse app version: invalid yaml")?;
let kind = value.get("kind").and_then(|x| x.as_str());
if kind == Some(AppVersionV1Spec::KIND) {
serde_yaml::from_value::<AppVersionV1>(value)
.context("could not parse app version: invalid app v1 schema")
} else if kind == Some(super::AppV1Spec::KIND) {
let mut app: super::AppV1 = serde_yaml::from_value(value)
.context("could not parse app version: invalid app v1 schema")?;
let annotations = super::AppMeta::try_from_annotations(&app.meta.annotations)?;
app.meta
.annotations
.remove(super::AppMeta::ANNOTATION_BACKEND_APP_ID);
app.meta
.annotations
.remove(super::AppMeta::ANNOTATION_BACKEND_APP_VERSION_ID);
Ok(AppVersionV1 {
meta: app.meta,
spec: AppVersionV1Spec {
app_id: annotations.app_id,
app_version_id: annotations.app_version_id,
aliases: app.spec.aliases,
workload: app.spec.workload,
},
children: None,
})
} else if let Some(other) = kind {
anyhow::bail!("could not parse app version: unknown kind: {other}");
} else {
anyhow::bail!("could not parse app version: no kind field in yaml");
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use crate::schema::EntityMeta;
use super::*;
#[test]
fn test_deser_app_v1_sparse() {
let inp = r#"
kind: wasmer.io/App.v1
meta:
name: my-app
spec:
app_id: da_123
app_version_id: dav_123
workload:
source: theduke/amaze
"#;
let a1 = serde_yaml::from_str::<AppVersionV1>(inp).unwrap();
assert_eq!(
a1,
AppVersionV1 {
meta: EntityMeta::new("my-app"),
spec: AppVersionV1Spec {
app_id: "da_123".to_string(),
app_version_id: "dav_123".to_string(),
aliases: Vec::new(),
workload: crate::schema::WorkloadV2 {
source: "theduke/amaze".parse().unwrap(),
capabilities: Default::default(),
},
},
children: None,
},
);
}
#[test]
fn test_deser_app_v1_full() {
let inp = r#"
kind: wasmer.io/App.v1
meta:
name: my-app
description: hello
labels:
"my/label": "value"
annotations:
"my/annotation": {nested: [1, 2, 3]}
spec:
app_id: da_123
app_version_id: dav_123
aliases:
- a
- b
workload:
source: "theduke/my-app"
"#;
let a1 = serde_yaml::from_str::<AppVersionV1>(inp).unwrap();
let expected = AppVersionV1 {
meta: EntityMeta {
uid: None,
name: "my-app".to_string(),
description: Some("hello".to_string()),
labels: vec![("my/label".to_string(), "value".to_string())]
.into_iter()
.collect(),
annotations: vec![(
"my/annotation".to_string(),
serde_json::json!({
"nested": [1, 2, 3],
}),
)]
.into_iter()
.collect(),
parent: None,
},
spec: AppVersionV1Spec {
app_id: "da_123".to_string(),
app_version_id: "dav_123".to_string(),
aliases: vec!["a".to_string(), "b".to_string()],
workload: WorkloadV2 {
source: "theduke/my-app".parse().unwrap(),
capabilities: Default::default(),
},
},
children: None,
};
assert_eq!(a1, expected);
}
}