use async_trait::async_trait;
use http::{Method, StatusCode};
use std::sync::Arc;
use tokio::sync::Mutex as AsyncMutex;
use fakecloud_core::service::{AwsRequest, AwsResponse, AwsService, AwsServiceError};
use fakecloud_persistence::SnapshotStore;
use crate::state::SharedCodeArtifactState;
pub const CODEARTIFACT_ACTIONS: &[&str] = &[
"AssociateExternalConnection",
"CopyPackageVersions",
"CreateDomain",
"CreatePackageGroup",
"CreateRepository",
"DeleteDomain",
"DeleteDomainPermissionsPolicy",
"DeletePackage",
"DeletePackageGroup",
"DeletePackageVersions",
"DeleteRepository",
"DeleteRepositoryPermissionsPolicy",
"DescribeDomain",
"DescribePackage",
"DescribePackageGroup",
"DescribePackageVersion",
"DescribeRepository",
"DisassociateExternalConnection",
"DisposePackageVersions",
"GetAssociatedPackageGroup",
"GetAuthorizationToken",
"GetDomainPermissionsPolicy",
"GetPackageVersionAsset",
"GetPackageVersionReadme",
"GetRepositoryEndpoint",
"GetRepositoryPermissionsPolicy",
"ListAllowedRepositoriesForGroup",
"ListAssociatedPackages",
"ListDomains",
"ListPackageGroups",
"ListPackageVersionAssets",
"ListPackageVersionDependencies",
"ListPackageVersions",
"ListPackages",
"ListRepositories",
"ListRepositoriesInDomain",
"ListSubPackageGroups",
"ListTagsForResource",
"PublishPackageVersion",
"PutDomainPermissionsPolicy",
"PutPackageOriginConfiguration",
"PutRepositoryPermissionsPolicy",
"TagResource",
"UntagResource",
"UpdatePackageGroup",
"UpdatePackageGroupOriginConfiguration",
"UpdatePackageVersionsStatus",
"UpdateRepository",
];
const MUTATING: &[&str] = &[
"AssociateExternalConnection",
"CopyPackageVersions",
"CreateDomain",
"CreatePackageGroup",
"CreateRepository",
"DeleteDomain",
"DeleteDomainPermissionsPolicy",
"DeletePackage",
"DeletePackageGroup",
"DeletePackageVersions",
"DeleteRepository",
"DeleteRepositoryPermissionsPolicy",
"DisassociateExternalConnection",
"DisposePackageVersions",
"PublishPackageVersion",
"PutDomainPermissionsPolicy",
"PutPackageOriginConfiguration",
"PutRepositoryPermissionsPolicy",
"TagResource",
"UntagResource",
"UpdatePackageGroup",
"UpdatePackageGroupOriginConfiguration",
"UpdatePackageVersionsStatus",
"UpdateRepository",
];
pub struct CodeArtifactService {
state: SharedCodeArtifactState,
snapshot_store: Option<Arc<dyn SnapshotStore>>,
snapshot_lock: Arc<AsyncMutex<()>>,
}
impl CodeArtifactService {
pub fn new(state: SharedCodeArtifactState) -> Self {
Self {
state,
snapshot_store: None,
snapshot_lock: Arc::new(AsyncMutex::new(())),
}
}
pub fn with_snapshot_store(mut self, store: Arc<dyn SnapshotStore>) -> Self {
self.snapshot_store = Some(store);
self
}
async fn save(&self) {
crate::persistence::save_snapshot(
&self.state,
self.snapshot_store.clone(),
&self.snapshot_lock,
)
.await;
}
pub fn snapshot_hook(&self) -> Option<fakecloud_persistence::SnapshotHook> {
let store = self.snapshot_store.clone()?;
let state = self.state.clone();
let lock = self.snapshot_lock.clone();
Some(Arc::new(move || {
let state = state.clone();
let store = store.clone();
let lock = lock.clone();
Box::pin(async move {
crate::persistence::save_snapshot(&state, Some(store), &lock).await;
})
}))
}
fn resolve_action(req: &AwsRequest) -> Option<&'static str> {
let raw = req.raw_path.split('?').next().unwrap_or(&req.raw_path);
let trimmed = raw.strip_prefix('/').unwrap_or(raw);
let trimmed = trimmed.strip_suffix('/').unwrap_or(trimmed);
let segs: Vec<&str> = if trimmed.is_empty() {
Vec::new()
} else {
trimmed.split('/').collect()
};
let m = &req.method;
let post = m == Method::POST;
let get = m == Method::GET;
let del = m == Method::DELETE;
let put = m == Method::PUT;
let action = match segs.as_slice() {
["v1", "domain"] if post => "CreateDomain",
["v1", "domain"] if get => "DescribeDomain",
["v1", "domain"] if del => "DeleteDomain",
["v1", "domains"] if post => "ListDomains",
["v1", "domain", "permissions", "policy"] if put => "PutDomainPermissionsPolicy",
["v1", "domain", "permissions", "policy"] if get => "GetDomainPermissionsPolicy",
["v1", "domain", "permissions", "policy"] if del => "DeleteDomainPermissionsPolicy",
["v1", "domain", "repositories"] if post => "ListRepositoriesInDomain",
["v1", "repository"] if post => "CreateRepository",
["v1", "repository"] if get => "DescribeRepository",
["v1", "repository"] if del => "DeleteRepository",
["v1", "repository"] if put => "UpdateRepository",
["v1", "repositories"] if post => "ListRepositories",
["v1", "repository", "external-connection"] if post => "AssociateExternalConnection",
["v1", "repository", "external-connection"] if del => "DisassociateExternalConnection",
["v1", "repository", "permissions", "policy"] if get => {
"GetRepositoryPermissionsPolicy"
}
["v1", "repository", "permissions", "policy"] if put => {
"PutRepositoryPermissionsPolicy"
}
["v1", "repository", "permissions", "policies"] if del => {
"DeleteRepositoryPermissionsPolicy"
}
["v1", "repository", "endpoint"] if get => "GetRepositoryEndpoint",
["v1", "package"] if get => "DescribePackage",
["v1", "package"] if del => "DeletePackage",
["v1", "package"] if post => "PutPackageOriginConfiguration",
["v1", "package", "version"] if get => "DescribePackageVersion",
["v1", "package", "version", "asset"] if get => "GetPackageVersionAsset",
["v1", "package", "version", "readme"] if get => "GetPackageVersionReadme",
["v1", "package", "version", "publish"] if post => "PublishPackageVersion",
["v1", "package", "version", "assets"] if post => "ListPackageVersionAssets",
["v1", "package", "version", "dependencies"] if post => {
"ListPackageVersionDependencies"
}
["v1", "package", "versions"] if post => "ListPackageVersions",
["v1", "package", "versions", "copy"] if post => "CopyPackageVersions",
["v1", "package", "versions", "delete"] if post => "DeletePackageVersions",
["v1", "package", "versions", "dispose"] if post => "DisposePackageVersions",
["v1", "package", "versions", "update_status"] if post => "UpdatePackageVersionsStatus",
["v1", "packages"] if post => "ListPackages",
["v1", "package-group"] if post => "CreatePackageGroup",
["v1", "package-group"] if get => "DescribePackageGroup",
["v1", "package-group"] if del => "DeletePackageGroup",
["v1", "package-group"] if put => "UpdatePackageGroup",
["v1", "package-groups"] if post => "ListPackageGroups",
["v1", "package-groups", "sub-groups"] if post => "ListSubPackageGroups",
["v1", "package-group-allowed-repositories"] if get => {
"ListAllowedRepositoriesForGroup"
}
["v1", "package-group-origin-configuration"] if put => {
"UpdatePackageGroupOriginConfiguration"
}
["v1", "get-associated-package-group"] if get => "GetAssociatedPackageGroup",
["v1", "list-associated-packages"] if get => "ListAssociatedPackages",
["v1", "authorization-token"] if post => "GetAuthorizationToken",
["v1", "tags"] if post => "ListTagsForResource",
["v1", "tag"] if post => "TagResource",
["v1", "untag"] if post => "UntagResource",
_ => return None,
};
Some(action)
}
}
#[async_trait]
impl AwsService for CodeArtifactService {
fn service_name(&self) -> &str {
"codeartifact"
}
async fn handle(&self, req: AwsRequest) -> Result<AwsResponse, AwsServiceError> {
let Some(action) = Self::resolve_action(&req) else {
return Err(AwsServiceError::aws_error(
StatusCode::NOT_FOUND,
"UnknownOperationException",
format!("Unknown operation: {} {}", req.method, req.raw_path),
));
};
let result = self.dispatch(action, &req);
if MUTATING.contains(&action)
&& matches!(result.as_ref(), Ok(resp) if resp.status.is_success())
{
self.save().await;
}
result
}
fn supported_actions(&self) -> &[&str] {
CODEARTIFACT_ACTIONS
}
}
impl CodeArtifactService {
#[allow(clippy::too_many_lines)]
fn dispatch(&self, action: &str, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
match action {
"CreateDomain" => self.create_domain(req),
"DescribeDomain" => self.describe_domain(req),
"DeleteDomain" => self.delete_domain(req),
"ListDomains" => self.list_domains(req),
"PutDomainPermissionsPolicy" => self.put_domain_policy(req),
"GetDomainPermissionsPolicy" => self.get_domain_policy(req),
"DeleteDomainPermissionsPolicy" => self.delete_domain_policy(req),
"CreateRepository" => self.create_repository(req),
"DescribeRepository" => self.describe_repository(req),
"UpdateRepository" => self.update_repository(req),
"DeleteRepository" => self.delete_repository(req),
"ListRepositories" => self.list_repositories(req),
"ListRepositoriesInDomain" => self.list_repositories_in_domain(req),
"GetRepositoryEndpoint" => self.get_repository_endpoint(req),
"AssociateExternalConnection" => self.associate_external_connection(req),
"DisassociateExternalConnection" => self.disassociate_external_connection(req),
"GetRepositoryPermissionsPolicy" => self.get_repository_policy(req),
"PutRepositoryPermissionsPolicy" => self.put_repository_policy(req),
"DeleteRepositoryPermissionsPolicy" => self.delete_repository_policy(req),
"ListPackages" => self.list_packages(req),
"DescribePackage" => self.describe_package(req),
"DeletePackage" => self.delete_package(req),
"PutPackageOriginConfiguration" => self.put_package_origin(req),
"ListPackageVersions" => self.list_package_versions(req),
"DescribePackageVersion" => self.describe_package_version(req),
"DeletePackageVersions" => self.delete_package_versions(req),
"DisposePackageVersions" => self.dispose_package_versions(req),
"UpdatePackageVersionsStatus" => self.update_package_versions_status(req),
"CopyPackageVersions" => self.copy_package_versions(req),
"PublishPackageVersion" => self.publish_package_version(req),
"GetPackageVersionReadme" => self.get_package_version_readme(req),
"GetPackageVersionAsset" => self.get_package_version_asset(req),
"ListPackageVersionAssets" => self.list_package_version_assets(req),
"ListPackageVersionDependencies" => self.list_package_version_dependencies(req),
"CreatePackageGroup" => self.create_package_group(req),
"DescribePackageGroup" => self.describe_package_group(req),
"UpdatePackageGroup" => self.update_package_group(req),
"DeletePackageGroup" => self.delete_package_group(req),
"ListPackageGroups" => self.list_package_groups(req),
"ListSubPackageGroups" => self.list_sub_package_groups(req),
"ListAllowedRepositoriesForGroup" => self.list_allowed_repositories_for_group(req),
"GetAssociatedPackageGroup" => self.get_associated_package_group(req),
"ListAssociatedPackages" => self.list_associated_packages(req),
"UpdatePackageGroupOriginConfiguration" => self.update_package_group_origin(req),
"GetAuthorizationToken" => self.get_authorization_token(req),
"TagResource" => self.tag_resource(req),
"UntagResource" => self.untag_resource(req),
"ListTagsForResource" => self.list_tags_for_resource(req),
_ => Err(AwsServiceError::action_not_implemented(
self.service_name(),
action,
)),
}
}
}
include!("handlers.rs");