use crate::api::tables::{StorageAccess, StorageBinding as IssuedBinding, VendedCredential};
use crate::scandal::{GcsSession, S3Session, ScandalError, StorageBinding, StorageCredential};
impl StorageBinding {
pub fn from_storage(root: String, binding: IssuedBinding) -> Result<Self, ScandalError> {
match binding.access {
StorageAccess::Signed(access) => Ok(Self {
root_alias: (root != binding.root_uri).then_some(binding.root_uri),
root_uri: root,
credential: Some(StorageCredential(access.token.0)),
}),
StorageAccess::Vended(access) => match access.credential {
VendedCredential::AwsSession(credential) => {
let session = S3Session {
access_key_id: credential.access_key_id,
secret_access_key: credential.secret_access_key.0,
session_token: credential.session_token.0,
region: credential.region,
endpoint: credential.endpoint,
expires_at_micros: credential.expires_at_micros.0,
};
Ok(vended(root, binding.root_uri, session_json(&session)?))
}
VendedCredential::GcsAccessToken(credential) => {
let session = GcsSession {
access_token: credential.access_token.0,
expires_at_micros: credential.expires_at_micros.0,
};
Ok(vended(root, binding.root_uri, session_json(&session)?))
}
_ => Err(unsupported(&root)),
},
StorageAccess::Unknown { .. } => Err(unsupported(&root)),
}
}
pub fn for_resource(binding: IssuedBinding) -> Result<Self, ScandalError> {
Self::from_storage(binding.root_uri.clone(), binding)
}
}
fn vended(root: String, provider_root: String, credential: String) -> StorageBinding {
let root_alias = (root != provider_root).then_some(root);
StorageBinding {
root_uri: provider_root,
root_alias,
credential: Some(StorageCredential(credential)),
}
}
fn session_json(session: &impl serde::Serialize) -> Result<String, ScandalError> {
serde_json::to_string(session).map_err(|error| ScandalError::Internal {
message: error.to_string(),
})
}
fn unsupported(root: &str) -> ScandalError {
ScandalError::InvalidArgument {
message: format!("storage at {root} is neither signed by SPFS nor in object storage"),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::api::tables::{
GcsAccessToken, HuggingFaceToken, SecretToken, StorageMode, VendedAccess,
};
fn vended_binding(credential: VendedCredential) -> IssuedBinding {
IssuedBinding {
root_uri: "gs://acme/lake/tables/t1/".to_owned(),
mode: StorageMode::ReadOnly,
expires_at: 0.into(),
access: StorageAccess::Vended(VendedAccess { credential }),
}
}
#[test]
fn a_vended_binding_carries_its_session_to_scandal() {
let root = "gs://acme/lake/tables/t1/".to_owned();
let gcs = || {
vended_binding(VendedCredential::GcsAccessToken(GcsAccessToken {
access_token: SecretToken("ya29.vended".into()),
expires_at_micros: 7.into(),
}))
};
let binding = StorageBinding::from_storage(root.clone(), gcs()).unwrap();
assert_eq!(binding.root_uri, root);
assert_eq!(binding.root_alias, None);
let session: GcsSession = serde_json::from_str(binding.credential().unwrap()).unwrap();
assert_eq!(session.access_token, "ya29.vended");
assert_eq!(session.expires_at_micros, 7);
let aliased =
StorageBinding::from_storage("spfsu://4b44-alias/".to_owned(), gcs()).unwrap();
assert_eq!(aliased.root_uri, root);
assert_eq!(aliased.root_alias.as_deref(), Some("spfsu://4b44-alias/"));
let refused = StorageBinding::from_storage(
root,
vended_binding(VendedCredential::HuggingFaceToken(HuggingFaceToken {
token: SecretToken("hf".into()),
expires_at_micros: 7.into(),
endpoint: "https://huggingface.co".to_owned(),
})),
);
assert!(refused.is_err());
}
}