From a3c7ce6d8004ac8358a0e1509de9a5d3fd4e3032 Mon Sep 17 00:00:00 2001 From: Calvin Prewitt Date: Fri, 17 Nov 2023 11:04:52 -0600 Subject: [PATCH] rename `PackageId` to `PackageName` on issue #229 (#233) Implements #229 Should not have breaking changes. Using `serde` alias for both the API schema change for the publish package record endpoint as well as the client storage JSON objects. --- README.md | 14 +- crates/api/src/v1/fetch.rs | 6 +- crates/api/src/v1/package.rs | 11 +- crates/client/src/api.rs | 4 +- crates/client/src/lib.rs | 120 +++++++------- crates/client/src/storage.rs | 22 +-- crates/client/src/storage/fs.rs | 10 +- crates/protocol/src/operator/state.rs | 4 +- crates/protocol/src/registry.rs | 66 ++++---- crates/server/openapi.yaml | 4 +- crates/server/src/api/debug/mod.rs | 22 +-- crates/server/src/api/v1/package.rs | 12 +- crates/server/src/datastore/memory.rs | 49 +++--- crates/server/src/datastore/mod.rs | 14 +- crates/server/src/datastore/postgres/mod.rs | 32 ++-- .../server/src/policy/record/authorization.rs | 44 ++--- crates/server/src/policy/record/mod.rs | 8 +- src/bin/warg.rs | 8 +- src/commands/download.rs | 21 +-- src/commands/info.rs | 6 +- src/commands/publish.rs | 150 +++++++++--------- src/commands/run.rs | 21 +-- tests/client.rs | 18 +-- tests/postgres/mod.rs | 12 +- tests/server.rs | 108 ++++++------- tests/support/mod.rs | 24 ++- 26 files changed, 425 insertions(+), 385 deletions(-) diff --git a/README.md b/README.md index 892148f9..24addee7 100644 --- a/README.md +++ b/README.md @@ -108,15 +108,15 @@ A new package can be initialized by running: warg publish init example:hello ``` -This creates a new package in the `example` namespace with the package ID `hello`. +This creates a new package in the `example` namespace with the name `hello`. A version of the package can be published by running: ``` -warg publish release --id example:hello --version 0.1.0 hello.wasm +warg publish release --name example:hello --version 0.1.0 hello.wasm ``` -This publishes a package named `hello` in the `example` namespace with version `0.1.0` and content from +This publishes a package named `example:hello` with version `0.1.0` and content from `hello.wasm`. Alternatively, the above can be batched into a single publish operation: @@ -124,7 +124,7 @@ Alternatively, the above can be batched into a single publish operation: ``` warg publish start example:hello warg publish init example:hello -warg publish release --id example:hello --version 0.1.0 hello.wasm +warg publish release --name example:hello --version 0.1.0 hello.wasm warg publish submit ``` @@ -140,7 +140,7 @@ Use `warg publish abort` to abort a pending publish operation. You can grant permissions to another public key with the `warg publish grant` subcommand: ``` -warg publish grant --id example:hello ecdsa-p256:ABC... +warg publish grant --name example:hello ecdsa-p256:ABC... ``` > You can get your own public key with the `warg key info` subcommand. @@ -151,7 +151,7 @@ Similarly, permissions may be revoked via `warg publish revoke`. Note that keys are identified by ID (fingerprint) for revocation: ``` -warg publish revoke --id example:hello sha256:abc... +warg publish revoke --name example:hello sha256:abc... ``` ### Running a package @@ -168,7 +168,7 @@ To publish the demo module: ``` warg publish start demo:simple-grep warg publish init demo:simple-grep -warg publish release --id demo:simple-grep --version 1.0.0 demo/simple-grep-1.0.0.wasm +warg publish release --name demo:simple-grep --version 1.0.0 demo/simple-grep-1.0.0.wasm warg publish submit ``` diff --git a/crates/api/src/v1/fetch.rs b/crates/api/src/v1/fetch.rs index 081e9e8e..31515405 100644 --- a/crates/api/src/v1/fetch.rs +++ b/crates/api/src/v1/fetch.rs @@ -6,7 +6,7 @@ use std::{borrow::Cow, collections::HashMap}; use thiserror::Error; use warg_crypto::hash::AnyHash; use warg_protocol::{ - registry::{LogId, PackageId, RegistryLen}, + registry::{LogId, PackageName, RegistryLen}, PublishedProtoEnvelopeBody, }; @@ -67,8 +67,8 @@ pub struct FetchPackageNamesRequest<'a> { #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct FetchPackageNamesResponse { - /// The log ID hash mapping to a package ID. If `None`, the package name cannot be provided. - pub packages: HashMap>, + /// The log ID hash mapping to a package name. If `None`, the package name cannot be provided. + pub packages: HashMap>, } /// Represents a fetch API error. diff --git a/crates/api/src/v1/package.rs b/crates/api/src/v1/package.rs index a7eb482e..9b58bdaf 100644 --- a/crates/api/src/v1/package.rs +++ b/crates/api/src/v1/package.rs @@ -7,7 +7,7 @@ use std::{borrow::Cow, collections::HashMap}; use thiserror::Error; use warg_crypto::hash::AnyHash; use warg_protocol::{ - registry::{LogId, PackageId, RecordId, RegistryIndex}, + registry::{LogId, PackageName, RecordId, RegistryIndex}, ProtoEnvelopeBody, }; @@ -42,8 +42,9 @@ pub struct MissingContent { #[serde(rename_all = "camelCase")] pub struct PublishRecordRequest<'a> { /// The package name being published. - #[serde(alias = "packageName")] - pub package_id: Cow<'a, PackageId>, + /// TODO: Remove the alias for `packageId` according to compatibility release schedule. + #[serde(alias = "packageId")] + pub package_name: Cow<'a, PackageName>, /// The publish record to add to the package log. pub record: Cow<'a, ProtoEnvelopeBody>, /// The complete set of content sources for the record. @@ -132,7 +133,7 @@ pub enum PackageError { NamespaceConflict(String), /// The provided package name conflicts with an existing package where the name only differs by case. #[error("the package conflicts with existing package name `{0}`; package names must be unique in a case insensitive way")] - PackageNameConflict(PackageId), + PackageNameConflict(PackageName), /// The operation was not authorized by the registry. #[error("unauthorized operation: {0}")] Unauthorized(String), @@ -325,7 +326,7 @@ impl<'de> Deserialize<'de> for PackageError { EntityType::Namespace => Ok(Self::NamespaceConflict(id.into_owned())), EntityType::NamespaceImport => Ok(Self::NamespaceImported(id.into_owned())), EntityType::Name => Ok(Self::PackageNameConflict( - PackageId::new(id.into_owned()).unwrap(), + PackageName::new(id.into_owned()).unwrap(), )), _ => Err(serde::de::Error::invalid_value( Unexpected::Enum, diff --git a/crates/client/src/api.rs b/crates/client/src/api.rs index 9e54c0dd..03b33e39 100644 --- a/crates/client/src/api.rs +++ b/crates/client/src/api.rs @@ -237,8 +237,8 @@ impl Client { ) -> Result { let url = self.url.join(&paths::publish_package_record(log_id)); tracing::debug!( - "appending record to package `{id}` at `{url}`", - id = request.package_id + "appending record to package `{name}` at `{url}`", + name = request.package_name ); let response = self.client.post(url).json(&request).send().await?; diff --git a/crates/client/src/lib.rs b/crates/client/src/lib.rs index 065f2d62..f4d91397 100644 --- a/crates/client/src/lib.rs +++ b/crates/client/src/lib.rs @@ -25,7 +25,7 @@ use warg_crypto::{ }; use warg_protocol::{ operator, package, - registry::{LogId, LogLeaf, PackageId, RecordId, TimestampedCheckpoint}, + registry::{LogId, LogLeaf, PackageName, RecordId, TimestampedCheckpoint}, PublishedProtoEnvelope, SerdeEnvelope, Version, VersionReq, }; @@ -121,24 +121,24 @@ impl Client { ) -> ClientResult { if info.entries.is_empty() { return Err(ClientError::NothingToPublish { - id: info.id.clone(), + name: info.name.clone(), }); } let initializing = info.initializing(); tracing::info!( - "publishing {new}package `{id}`", - id = info.id, + "publishing {new}package `{name}`", + name = info.name, new = if initializing { "new " } else { "" } ); tracing::debug!("entries: {:?}", info.entries); let mut package = self .registry - .load_package(&info.id) + .load_package(&info.name) .await? - .unwrap_or_else(|| PackageInfo::new(info.id.clone())); + .unwrap_or_else(|| PackageInfo::new(info.name.clone())); // If we're not initializing the package and a head was not explicitly specified, // updated to the latest checkpoint to get the latest known head. @@ -150,19 +150,23 @@ impl Client { } match (initializing, info.head.is_some()) { - (true, true) => return Err(ClientError::CannotInitializePackage { id: package.id }), - (false, false) => return Err(ClientError::MustInitializePackage { id: package.id }), + (true, true) => { + return Err(ClientError::CannotInitializePackage { name: package.name }) + } + (false, false) => { + return Err(ClientError::MustInitializePackage { name: package.name }) + } _ => (), } let record = info.finalize(signing_key)?; - let log_id = LogId::package_log::(&package.id); + let log_id = LogId::package_log::(&package.name); let record = self .api .publish_package_record( &log_id, PublishRecordRequest { - package_id: Cow::Borrowed(&package.id), + package_name: Cow::Borrowed(&package.name), record: Cow::Owned(record.into()), content_sources: Default::default(), }, @@ -171,7 +175,7 @@ impl Client { .map_err(|e| { ClientError::translate_log_not_found(e, |id| { if id == &log_id { - Some(package.id.clone()) + Some(package.name.clone()) } else { None } @@ -205,7 +209,7 @@ impl Client { .map_err(|e| match e { api::ClientError::Package(PackageError::Rejection(reason)) => { ClientError::PublishRejected { - id: package.id.clone(), + name: package.name.clone(), record_id: record.record_id.clone(), reason, } @@ -224,7 +228,7 @@ impl Client { /// Returns an error if the package record was rejected. pub async fn wait_for_publish( &self, - package: &PackageId, + package: &PackageName, record_id: &RecordId, interval: Duration, ) -> ClientResult<()> { @@ -241,7 +245,7 @@ impl Client { } PackageRecordState::Rejected { reason } => { return Err(ClientError::PublishRejected { - id: package.clone(), + name: package.clone(), record_id: record_id.clone(), reason, }); @@ -269,7 +273,7 @@ impl Client { /// the latest registry checkpoint. pub async fn upsert<'a, I>(&self, packages: I) -> Result<(), ClientError> where - I: IntoIterator, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { tracing::info!("updating specific packages to latest checkpoint"); @@ -306,11 +310,11 @@ impl Client { /// the resolved version. pub async fn download( &self, - id: &PackageId, + name: &PackageName, requirement: &VersionReq, ) -> Result, ClientError> { - tracing::info!("downloading package `{id}` with requirement `{requirement}`"); - let info = self.fetch_package(id).await?; + tracing::info!("downloading package `{name}` with requirement `{requirement}`"); + let info = self.fetch_package(name).await?; match info.state.find_latest_release(requirement) { Some(release) => { @@ -340,7 +344,7 @@ impl Client { /// the specified version. pub async fn download_exact( &self, - package: &PackageId, + package: &PackageName, version: &Version, ) -> Result { tracing::info!("downloading version {version} of package `{package}`"); @@ -351,14 +355,14 @@ impl Client { .release(version) .ok_or_else(|| ClientError::PackageVersionDoesNotExist { version: version.clone(), - id: package.clone(), + name: package.clone(), })?; let digest = release .content() .ok_or_else(|| ClientError::PackageVersionDoesNotExist { version: version.clone(), - id: package.clone(), + name: package.clone(), })?; Ok(PackageDownload { @@ -381,15 +385,15 @@ impl Client { let mut operator = self.registry.load_operator().await?.unwrap_or_default(); - // Map package identifiers to package logs that need to be updated + // Map package names to package logs that need to be updated let mut packages = packages .into_iter() .filter_map(|p| match &p.checkpoint { // Don't bother updating if the package is already at the specified checkpoint Some(c) if c == checkpoint => None, - _ => Some((LogId::package_log::(&p.id), p)), + _ => Some((LogId::package_log::(&p.name), p)), }) - .inspect(|(_, p)| tracing::info!("package `{id}` will be updated", id = p.id)) + .inspect(|(_, p)| tracing::info!("package `{name}` will be updated", name = p.name)) .collect::>(); if packages.is_empty() { return Ok(()); @@ -415,7 +419,7 @@ impl Client { .await .map_err(|e| { ClientError::translate_log_not_found(e, |id| { - packages.get(id).map(|p| p.id.clone()) + packages.get(id).map(|p| p.name.clone()) }) })?; @@ -453,7 +457,7 @@ impl Client { .state .validate(&proto_envelope.envelope) .map_err(|inner| ClientError::PackageValidationFailed { - id: package.id.clone(), + name: package.name.clone(), inner, })?; package.head_registry_index = Some(proto_envelope.registry_index); @@ -464,7 +468,7 @@ impl Client { // At this point, the package log should not be empty if package.state.head().is_none() { return Err(ClientError::PackageLogEmpty { - id: package.id.clone(), + name: package.name.clone(), }); } } @@ -516,7 +520,7 @@ impl Client { }); } else { return Err(ClientError::PackageLogEmpty { - id: package.id.clone(), + name: package.name.clone(), }); } } @@ -559,14 +563,14 @@ impl Client { Ok(()) } - async fn fetch_package(&self, id: &PackageId) -> Result { - match self.registry.load_package(id).await? { + async fn fetch_package(&self, name: &PackageName) -> Result { + match self.registry.load_package(name).await? { Some(info) => { - tracing::info!("log for package `{id}` already exists in storage"); + tracing::info!("log for package `{name}` already exists in storage"); Ok(info) } None => { - let mut info = PackageInfo::new(id.clone()); + let mut info = PackageInfo::new(name.clone()); self.update_checkpoint(&self.api.latest_checkpoint().await?, [&mut info]) .await?; @@ -577,7 +581,7 @@ impl Client { async fn get_package_record( &self, - package: &PackageId, + package: &PackageName, log_id: &LogId, record_id: &RecordId, ) -> ClientResult { @@ -737,17 +741,17 @@ pub enum ClientError { }, /// The package already exists and cannot be initialized. - #[error("package `{id}` already exists and cannot be initialized")] + #[error("package `{name}` already exists and cannot be initialized")] CannotInitializePackage { - /// The identifier of the package that already exists. - id: PackageId, + /// The package name that already exists. + name: PackageName, }, /// The package must be initialized before publishing. - #[error("package `{id}` must be initialized before publishing")] + #[error("package `{name}` must be initialized before publishing")] MustInitializePackage { /// The name of the package that must be initialized. - id: PackageId, + name: PackageName, }, /// There is no publish operation in progress. @@ -755,33 +759,33 @@ pub enum ClientError { NotPublishing, /// The package has no records to publish. - #[error("package `{id}` has no records to publish")] + #[error("package `{name}` has no records to publish")] NothingToPublish { - /// The identifier of the package that has no publish operations. - id: PackageId, + /// The package that has no publish operations. + name: PackageName, }, /// The package does not exist. - #[error("package `{id}` does not exist")] + #[error("package `{name}` does not exist")] PackageDoesNotExist { - /// The identifier of the missing package. - id: PackageId, + /// The missing package. + name: PackageName, }, /// The package version does not exist. - #[error("version `{version}` of package `{id}` does not exist")] + #[error("version `{version}` of package `{name}` does not exist")] PackageVersionDoesNotExist { /// The missing version of the package. version: Version, - /// The identifier of the package with the missing version. - id: PackageId, + /// The package with the missing version. + name: PackageName, }, /// The package failed validation. - #[error("package `{id}` failed validation: {inner}")] + #[error("package `{name}` failed validation: {inner}")] PackageValidationFailed { - /// The identifier of the package that failed validation. - id: PackageId, + /// The package that failed validation. + name: PackageName, /// The validation error. inner: package::ValidationError, }, @@ -796,15 +800,15 @@ pub enum ClientError { /// The package log is empty and cannot be validated. #[error("package log is empty and cannot be validated")] PackageLogEmpty { - /// The identifier of the package with an empty package log. - id: PackageId, + /// The package with an empty package log. + name: PackageName, }, /// A publish operation was rejected. - #[error("the publishing of package `{id}` was rejected due to: {reason}")] + #[error("the publishing of package `{name}` was rejected due to: {reason}")] PublishRejected { - /// The identifier of the package that was rejected. - id: PackageId, + /// The package that was rejected. + name: PackageName, /// The record identifier for the record that was rejected. record_id: RecordId, /// The reason it was rejected. @@ -827,13 +831,13 @@ pub enum ClientError { impl ClientError { fn translate_log_not_found( e: api::ClientError, - lookup: impl Fn(&LogId) -> Option, + lookup: impl Fn(&LogId) -> Option, ) -> Self { match &e { api::ClientError::Fetch(FetchError::LogNotFound(id)) | api::ClientError::Package(PackageError::LogNotFound(id)) => { - if let Some(id) = lookup(id) { - return Self::PackageDoesNotExist { id }; + if let Some(name) = lookup(id) { + return Self::PackageDoesNotExist { name }; } } _ => {} diff --git a/crates/client/src/storage.rs b/crates/client/src/storage.rs index c1384fb8..3adc954d 100644 --- a/crates/client/src/storage.rs +++ b/crates/client/src/storage.rs @@ -13,7 +13,7 @@ use warg_crypto::{ use warg_protocol::{ operator, package::{self, PackageRecord, Permission, PACKAGE_RECORD_VERSION}, - registry::{Checkpoint, PackageId, RecordId, RegistryIndex, TimestampedCheckpoint}, + registry::{Checkpoint, PackageName, RecordId, RegistryIndex, TimestampedCheckpoint}, ProtoEnvelope, SerdeEnvelope, Version, }; @@ -55,7 +55,7 @@ pub trait RegistryStorage: Send + Sync { /// Loads the package information from the storage. /// /// Returns `Ok(None)` if the information is not present. - async fn load_package(&self, package: &PackageId) -> Result>; + async fn load_package(&self, package: &PackageName) -> Result>; /// Stores the package information in the storage. async fn store_package(&self, info: &PackageInfo) -> Result<()>; @@ -127,8 +127,10 @@ pub struct OperatorInfo { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PackageInfo { - /// The id of the package to publish. - pub id: PackageId, + /// The package name to publish. + /// TODO: drop alias after sufficient time according to release policy. + #[serde(alias = "id")] + pub name: PackageName, /// The last known checkpoint of the package. #[serde(default, skip_serializing_if = "Option::is_none")] pub checkpoint: Option, @@ -144,10 +146,10 @@ pub struct PackageInfo { } impl PackageInfo { - /// Creates a new package info for the given package id. - pub fn new(id: impl Into) -> Self { + /// Creates a new package info for the given package name. + pub fn new(name: impl Into) -> Self { Self { - id: id.into(), + name: name.into(), checkpoint: None, state: package::LogState::default(), head_registry_index: None, @@ -194,8 +196,10 @@ pub enum PublishEntry { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PublishInfo { - /// The id of the package being published. - pub id: PackageId, + /// The package name being published. + /// TODO: drop alias after sufficient time according to release policy. + #[serde(alias = "id")] + pub name: PackageName, /// The last known head of the package log to use. /// /// If `None` and the package is not being initialized, diff --git a/crates/client/src/storage/fs.rs b/crates/client/src/storage/fs.rs index ae0967ff..a6ab4bb5 100644 --- a/crates/client/src/storage/fs.rs +++ b/crates/client/src/storage/fs.rs @@ -19,7 +19,7 @@ use tokio_util::io::ReaderStream; use walkdir::WalkDir; use warg_crypto::hash::{AnyHash, Digest, Hash, Sha256}; use warg_protocol::{ - registry::{LogId, PackageId, TimestampedCheckpoint}, + registry::{LogId, PackageName, TimestampedCheckpoint}, SerdeEnvelope, }; @@ -70,9 +70,9 @@ impl FileSystemRegistryStorage { self.base_dir.join("operator.log") } - fn package_path(&self, id: &PackageId) -> PathBuf { + fn package_path(&self, name: &PackageName) -> PathBuf { self.base_dir.join(PACKAGE_LOGS_DIR).join( - LogId::package_log::(id) + LogId::package_log::(name) .to_string() .replace(':', "/"), ) @@ -150,12 +150,12 @@ impl RegistryStorage for FileSystemRegistryStorage { store(&self.operator_path(), info).await } - async fn load_package(&self, package: &PackageId) -> Result> { + async fn load_package(&self, package: &PackageName) -> Result> { Ok(load(&self.package_path(package)).await?) } async fn store_package(&self, info: &PackageInfo) -> Result<()> { - store(&self.package_path(&info.id), info).await + store(&self.package_path(&info.name), info).await } async fn load_publish(&self) -> Result> { diff --git a/crates/protocol/src/operator/state.rs b/crates/protocol/src/operator/state.rs index 1c1598b8..57c1ed4d 100644 --- a/crates/protocol/src/operator/state.rs +++ b/crates/protocol/src/operator/state.rs @@ -1,5 +1,5 @@ use super::{model, OPERATOR_RECORD_VERSION}; -use crate::registry::PackageId; +use crate::registry::PackageName; use crate::registry::RecordId; use crate::ProtoEnvelope; use indexmap::{IndexMap, IndexSet}; @@ -411,7 +411,7 @@ impl LogState { namespace: &str, state: NamespaceState, ) -> Result<(), ValidationError> { - if !PackageId::is_valid_namespace(namespace) { + if !PackageName::is_valid_namespace(namespace) { return Err(ValidationError::InvalidNamespace { namespace: namespace.to_string(), }); diff --git a/crates/protocol/src/registry.rs b/crates/protocol/src/registry.rs index bc2d01e1..c7761be5 100644 --- a/crates/protocol/src/registry.rs +++ b/crates/protocol/src/registry.rs @@ -122,44 +122,48 @@ impl VisitBytes for LogLeaf { } } -/// Represents a valid package identifier in the registry. +/// Represents a valid package name in the registry. /// -/// Valid package identifiers conform to the component model specification -/// for identifiers. +/// Valid package names conform to the component model specification. /// -/// A valid component model identifier is the format `:`, +/// A valid component model package name is the format `:`, /// where both parts are also valid WIT identifiers (i.e. kebab-cased). #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub struct PackageId { - id: String, +pub struct PackageName { + package_name: String, colon: usize, } -impl PackageId { - /// Creates a package identifier from the given string. +impl PackageName { + /// Creates a package name from the given string. /// - /// Returns an error if the given string is not a valid package identifier. - pub fn new(id: impl Into) -> anyhow::Result { - let id = id.into(); + /// Returns an error if the given string is not a valid package name. + pub fn new(name: impl Into) -> anyhow::Result { + let name = name.into(); - if let Some(colon) = id.rfind(':') { + if let Some(colon) = name.rfind(':') { // Validate the namespace and name parts are valid kebab strings - if KebabStr::new(&id[colon + 1..]).is_some() && Self::is_valid_namespace(&id[..colon]) { - return Ok(Self { id, colon }); + if KebabStr::new(&name[colon + 1..]).is_some() + && Self::is_valid_namespace(&name[..colon]) + { + return Ok(Self { + package_name: name, + colon, + }); } } - bail!("invalid package identifier `{id}`: expected format is `:`") + bail!("invalid package name `{name}`: expected format is `:`") } /// Gets the namespace part of the package identifier. pub fn namespace(&self) -> &str { - &self.id[..self.colon] + &self.package_name[..self.colon] } /// Gets the name part of the package identifier. pub fn name(&self) -> &str { - &self.id[self.colon + 1..] + &self.package_name[self.colon + 1..] } /// Check if string is a valid namespace. @@ -168,13 +172,13 @@ impl PackageId { } } -impl AsRef for PackageId { +impl AsRef for PackageName { fn as_ref(&self) -> &str { - &self.id + &self.package_name } } -impl FromStr for PackageId { +impl FromStr for PackageName { type Err = anyhow::Error; fn from_str(s: &str) -> Result { @@ -182,35 +186,35 @@ impl FromStr for PackageId { } } -impl fmt::Display for PackageId { +impl fmt::Display for PackageName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{id}", id = self.id) + write!(f, "{package_name}", package_name = self.package_name) } } -impl prefix::VisitPrefixEncode for PackageId { +impl prefix::VisitPrefixEncode for PackageName { fn visit_pe(&self, visitor: &mut prefix::PrefixEncodeVisitor) { visitor.visit_str_raw("WARG-PACKAGE-ID-V0"); - visitor.visit_str(&self.id); + visitor.visit_str(&self.package_name); } } -impl VisitBytes for PackageId { +impl VisitBytes for PackageName { fn visit(&self, visitor: &mut BV) { self.visit_bv(visitor); } } -impl Serialize for PackageId { +impl Serialize for PackageName { fn serialize(&self, serializer: S) -> Result { - serializer.serialize_str(&self.id) + serializer.serialize_str(&self.package_name) } } -impl<'de> Deserialize<'de> for PackageId { +impl<'de> Deserialize<'de> for PackageName { fn deserialize>(deserializer: D) -> Result { let id = String::deserialize(deserializer)?; - PackageId::new(id).map_err(serde::de::Error::custom) + PackageName::new(id).map_err(serde::de::Error::custom) } } @@ -225,9 +229,9 @@ impl LogId { Self(hash.into()) } - pub fn package_log(id: &PackageId) -> Self { + pub fn package_log(name: &PackageName) -> Self { let prefix: &[u8] = b"WARG-PACKAGE-LOG-ID-V0:".as_slice(); - let hash: Hash = Hash::of((prefix, id)); + let hash: Hash = Hash::of((prefix, name)); Self(hash.into()) } } diff --git a/crates/server/openapi.yaml b/crates/server/openapi.yaml index 6620fb6f..bdcae8e1 100644 --- a/crates/server/openapi.yaml +++ b/crates/server/openapi.yaml @@ -841,10 +841,10 @@ components: description: A request to publish a record to a package log. additionalProperties: false required: - - packageId + - packageName - record properties: - packageId: + packageName: type: string description: The name of the package log being published to. maxLength: 128 diff --git a/crates/server/src/api/debug/mod.rs b/crates/server/src/api/debug/mod.rs index ea6da319..3e12694c 100644 --- a/crates/server/src/api/debug/mod.rs +++ b/crates/server/src/api/debug/mod.rs @@ -16,7 +16,7 @@ use warg_crypto::{ }; use warg_protocol::{ package::{LogState, Permission, Release}, - registry::{LogId, PackageId, RecordId}, + registry::{LogId, PackageName, RecordId}, Version, }; @@ -35,7 +35,7 @@ impl Config { pub fn into_router(self) -> Router { Router::new() .route("/packages", get(list_package_names)) - .route("/package/:package_id", get(get_package_info)) + .route("/package/:package_name", get(get_package_info)) .with_state(self) } } @@ -43,15 +43,19 @@ impl Config { #[debug_handler] async fn list_package_names( State(config): State, -) -> Result>, DebugError> { - let ids = config.core_service.store().debug_list_package_ids().await?; - Ok(Json(ids)) +) -> Result>, DebugError> { + let names = config + .core_service + .store() + .debug_list_package_names() + .await?; + Ok(Json(names)) } #[derive(Serialize)] #[serde(rename_all = "camelCase")] struct PackageInfo { - package_id: PackageId, + package_name: PackageName, log_id: LogId, records: Vec, releases: Vec, @@ -84,7 +88,7 @@ struct EntryInfo { #[debug_handler] async fn get_package_info( State(config): State, - Path(package_id): Path, + Path(package_name): Path, ) -> Result, DebugError> { let store = config.core_service.store(); @@ -94,7 +98,7 @@ async fn get_package_info( .context("get_latest_checkpoint")?; let checkpoint_log_length = checkpoint.as_ref().checkpoint.log_length; - let log_id = LogId::package_log::(&package_id); + let log_id = LogId::package_log::(&package_name); let records = store .get_package_records(&log_id, checkpoint_log_length, None, u16::MAX) .await @@ -173,7 +177,7 @@ async fn get_package_info( let releases = package_state.releases().cloned().collect(); Ok(Json(PackageInfo { - package_id, + package_name, log_id, records, releases, diff --git a/crates/server/src/api/v1/package.rs b/crates/server/src/api/v1/package.rs index b997de6e..a36c6c51 100644 --- a/crates/server/src/api/v1/package.rs +++ b/crates/server/src/api/v1/package.rs @@ -192,11 +192,11 @@ async fn publish_record( RegistryHeader(_registry_header): RegistryHeader, Json(body): Json>, ) -> Result { - let expected_log_id = LogId::package_log::(&body.package_id); + let expected_log_id = LogId::package_log::(&body.package_name); if expected_log_id != log_id { return Err(PackageApiError::bad_request(format!( - "package log identifier `{expected_log_id}` derived from `{id}` does not match provided log identifier `{log_id}`", - id = body.package_id + "package log identifier `{expected_log_id}` derived from `{name}` does not match provided log identifier `{log_id}`", + name = body.package_name ))); } @@ -219,13 +219,13 @@ async fn publish_record( config .core_service .store() - .verify_can_publish_package(&LogId::operator_log::(), &body.package_id) + .verify_can_publish_package(&LogId::operator_log::(), &body.package_name) .await?; // Preemptively perform the policy check on the record before storing it // This is performed here so that we never store an unauthorized record if let Some(policy) = &config.record_policy { - policy.check(&body.package_id, &record)?; + policy.check(&body.package_name, &record)?; } // Verify the signature on the record itself before storing it @@ -242,7 +242,7 @@ async fn publish_record( config .core_service .store() - .store_package_record(&log_id, &body.package_id, &record_id, &record, &missing) + .store_package_record(&log_id, &body.package_name, &record_id, &record, &missing) .await?; // If there's no missing content, submit the record for processing now diff --git a/crates/server/src/datastore/memory.rs b/crates/server/src/datastore/memory.rs index c75afed8..938bd857 100644 --- a/crates/server/src/datastore/memory.rs +++ b/crates/server/src/datastore/memory.rs @@ -12,7 +12,7 @@ use warg_protocol::{ operator, package::{self, PackageEntry}, registry::{ - LogId, LogLeaf, PackageId, RecordId, RegistryIndex, RegistryLen, TimestampedCheckpoint, + LogId, LogLeaf, PackageName, RecordId, RegistryIndex, RegistryLen, TimestampedCheckpoint, }, ProtoEnvelope, PublishedProtoEnvelope, SerdeEnvelope, }; @@ -77,8 +77,8 @@ enum RecordStatus { struct State { operators: HashMap>, packages: HashMap>, - package_ids: HashMap>, - package_ids_lowercase: HashMap, + package_names: HashMap>, + package_names_lowercase: HashMap, checkpoints: IndexMap>, records: HashMap>, log_leafs: HashMap, @@ -166,19 +166,19 @@ impl DataStore for MemoryDataStore { async fn get_package_names( &self, log_ids: &[LogId], - ) -> Result>, DataStoreError> { + ) -> Result>, DataStoreError> { let state = self.0.read().await; log_ids .iter() .map(|log_id| { - if let Some(opt_package_name) = state.package_ids.get(log_id) { + if let Some(opt_package_name) = state.package_names.get(log_id) { Ok((log_id.clone(), opt_package_name.clone())) } else { Err(DataStoreError::LogNotFound(log_id.clone())) } }) - .collect::>, _>>() + .collect::>, _>>() } async fn store_operator_record( @@ -292,7 +292,7 @@ impl DataStore for MemoryDataStore { async fn store_package_record( &self, log_id: &LogId, - package_id: &PackageId, + package_name: &PackageName, record_id: &RecordId, record: &ProtoEnvelope, missing: &HashSet<&AnyHash>, @@ -313,11 +313,12 @@ impl DataStore for MemoryDataStore { }), ); state - .package_ids - .insert(log_id.clone(), Some(package_id.clone())); - state - .package_ids_lowercase - .insert(package_id.as_ref().to_ascii_lowercase(), package_id.clone()); + .package_names + .insert(log_id.clone(), Some(package_name.clone())); + state.package_names_lowercase.insert( + package_name.as_ref().to_ascii_lowercase(), + package_name.clone(), + ); assert!(prev.is_none()); Ok(()) @@ -721,7 +722,7 @@ impl DataStore for MemoryDataStore { async fn verify_can_publish_package( &self, operator_log_id: &LogId, - package_id: &PackageId, + package_name: &PackageName, ) -> Result<(), DataStoreError> { let state = self.0.read().await; @@ -731,24 +732,24 @@ impl DataStore for MemoryDataStore { .get(operator_log_id) .ok_or_else(|| DataStoreError::LogNotFound(operator_log_id.clone()))? .validator - .namespace_state(package_id.namespace()) + .namespace_state(package_name.namespace()) { Ok(Some(state)) => match state { operator::NamespaceState::Defined => {} operator::NamespaceState::Imported { .. } => { return Err(DataStoreError::PackageNamespaceImported( - package_id.namespace().to_string(), + package_name.namespace().to_string(), )) } }, Ok(None) => { return Err(DataStoreError::PackageNamespaceNotDefined( - package_id.namespace().to_string(), + package_name.namespace().to_string(), )) } Err(existing_namespace) => { return Err(DataStoreError::PackageNamespaceConflict { - namespace: package_id.namespace().to_string(), + namespace: package_name.namespace().to_string(), existing: existing_namespace.to_string(), }) } @@ -756,12 +757,12 @@ impl DataStore for MemoryDataStore { // verify package name is unique in a case insensitive way match state - .package_ids_lowercase - .get(&package_id.as_ref().to_ascii_lowercase()) + .package_names_lowercase + .get(&package_name.as_ref().to_ascii_lowercase()) { - Some(existing) if existing.as_ref() != package_id.as_ref() => { + Some(existing) if existing.as_ref() != package_name.as_ref() => { Err(DataStoreError::PackageNameConflict { - name: package_id.clone(), + name: package_name.clone(), existing: existing.clone(), }) } @@ -803,12 +804,12 @@ impl DataStore for MemoryDataStore { } #[cfg(feature = "debug")] - async fn debug_list_package_ids(&self) -> anyhow::Result> { + async fn debug_list_package_names(&self) -> anyhow::Result> { let state = self.0.read().await; Ok(state - .package_ids + .package_names .values() - .filter_map(|opt_package_id| opt_package_id.clone()) + .filter_map(|opt_package_name| opt_package_name.clone()) .collect()) } } diff --git a/crates/server/src/datastore/mod.rs b/crates/server/src/datastore/mod.rs index a5310d96..a91e253e 100644 --- a/crates/server/src/datastore/mod.rs +++ b/crates/server/src/datastore/mod.rs @@ -11,7 +11,7 @@ use warg_crypto::{ use warg_protocol::{ operator, package, registry::{ - LogId, LogLeaf, PackageId, RecordId, RegistryIndex, RegistryLen, TimestampedCheckpoint, + LogId, LogLeaf, PackageName, RecordId, RegistryIndex, RegistryLen, TimestampedCheckpoint, }, ProtoEnvelope, PublishedProtoEnvelope, SerdeEnvelope, }; @@ -58,8 +58,8 @@ pub enum DataStoreError { #[error("the package `{name}` conflicts with package `{existing}`; package names must be unique in a case insensitive way")] PackageNameConflict { - name: PackageId, - existing: PackageId, + name: PackageName, + existing: PackageName, }, #[error("the package namespace `{namespace}` conflicts with existing namespace `{existing}`; package namespaces must be unique in a case insensitive way")] @@ -187,7 +187,7 @@ pub trait DataStore: Send + Sync { async fn store_package_record( &self, log_id: &LogId, - package_id: &PackageId, + package_name: &PackageName, record_id: &RecordId, record: &ProtoEnvelope, missing: &HashSet<&AnyHash>, @@ -262,7 +262,7 @@ pub trait DataStore: Send + Sync { async fn get_package_names( &self, log_ids: &[LogId], - ) -> Result>, DataStoreError>; + ) -> Result>, DataStoreError>; /// Gets a batch of log leafs starting with a registry log index. async fn get_log_leafs_starting_with_registry_index( @@ -321,7 +321,7 @@ pub trait DataStore: Send + Sync { async fn verify_can_publish_package( &self, operator_log_id: &LogId, - package_id: &PackageId, + package_name: &PackageName, ) -> Result<(), DataStoreError>; /// Verifies the TimestampedCheckpoint signature. @@ -334,7 +334,7 @@ pub trait DataStore: Send + Sync { // Returns a list of package names, for debugging only. #[cfg(feature = "debug")] #[doc(hidden)] - async fn debug_list_package_ids(&self) -> anyhow::Result> { + async fn debug_list_package_names(&self) -> anyhow::Result> { anyhow::bail!("not implemented") } } diff --git a/crates/server/src/datastore/postgres/mod.rs b/crates/server/src/datastore/postgres/mod.rs index 93808026..757e9b4f 100644 --- a/crates/server/src/datastore/postgres/mod.rs +++ b/crates/server/src/datastore/postgres/mod.rs @@ -26,7 +26,7 @@ use warg_protocol::{ operator, package::{self, PackageEntry}, registry::{ - Checkpoint, LogId, LogLeaf, PackageId, RecordId, RegistryIndex, RegistryLen, + Checkpoint, LogId, LogLeaf, PackageName, RecordId, RegistryIndex, RegistryLen, TimestampedCheckpoint, }, ProtoEnvelope, PublishedProtoEnvelope, Record as _, SerdeEnvelope, Validator, @@ -529,7 +529,7 @@ impl DataStore for PostgresDataStore { async fn get_package_names( &self, log_ids: &[LogId], - ) -> Result>, DataStoreError> { + ) -> Result>, DataStoreError> { let mut conn = self.pool.get().await?; let map = schema::logs::table @@ -544,10 +544,10 @@ impl DataStore for PostgresDataStore { .map(|(log_id, opt_package_name)| { ( log_id.0.into(), - opt_package_name.map(|name| PackageId::new(name).unwrap()), + opt_package_name.map(|name| PackageName::new(name).unwrap()), ) }) - .collect::>>(); + .collect::>>(); // check if any log IDs were not found for log_id in log_ids { @@ -624,7 +624,7 @@ impl DataStore for PostgresDataStore { async fn store_package_record( &self, log_id: &LogId, - package_id: &PackageId, + package_name: &PackageName, record_id: &RecordId, record: &ProtoEnvelope, missing: &HashSet<&AnyHash>, @@ -633,7 +633,7 @@ impl DataStore for PostgresDataStore { insert_record::( conn.as_mut(), log_id, - Some(package_id.as_ref()), + Some(package_name.as_ref()), record_id, record, missing, @@ -963,7 +963,7 @@ impl DataStore for PostgresDataStore { async fn verify_can_publish_package( &self, operator_log_id: &LogId, - package_id: &PackageId, + package_name: &PackageName, ) -> Result<(), DataStoreError> { let mut conn = self.pool.get().await?; @@ -976,23 +976,23 @@ impl DataStore for PostgresDataStore { .ok_or_else(|| DataStoreError::LogNotFound(operator_log_id.clone()))?; // verify namespace is defined and not imported - match validator.namespace_state(package_id.namespace()) { + match validator.namespace_state(package_name.namespace()) { Ok(Some(state)) => match state { operator::NamespaceState::Defined => {} operator::NamespaceState::Imported { .. } => { return Err(DataStoreError::PackageNamespaceImported( - package_id.namespace().to_string(), + package_name.namespace().to_string(), )) } }, Ok(None) => { return Err(DataStoreError::PackageNamespaceNotDefined( - package_id.namespace().to_string(), + package_name.namespace().to_string(), )) } Err(existing_namespace) => { return Err(DataStoreError::PackageNamespaceConflict { - namespace: package_id.namespace().to_string(), + namespace: package_name.namespace().to_string(), existing: existing_namespace.to_string(), }) } @@ -1002,16 +1002,16 @@ impl DataStore for PostgresDataStore { match schema::logs::table .select(schema::logs::name) .filter( - lower(schema::logs::name).eq(TextRef(&package_id.as_ref().to_ascii_lowercase())), + lower(schema::logs::name).eq(TextRef(&package_name.as_ref().to_ascii_lowercase())), ) .first::>(&mut conn) .await .optional()? { - Some(Some(name)) if name != package_id.as_ref() => { + Some(Some(name)) if name != package_name.as_ref() => { Err(DataStoreError::PackageNameConflict { - name: package_id.clone(), - existing: PackageId::new(name).unwrap(), + name: package_name.clone(), + existing: PackageName::new(name).unwrap(), }) } _ => Ok(()), @@ -1054,7 +1054,7 @@ impl DataStore for PostgresDataStore { } #[cfg(feature = "debug")] - async fn debug_list_package_ids(&self) -> anyhow::Result> { + async fn debug_list_package_names(&self) -> anyhow::Result> { let mut conn = self.pool.get().await?; let names = schema::logs::table .select(schema::logs::name) diff --git a/crates/server/src/policy/record/authorization.rs b/crates/server/src/policy/record/authorization.rs index 8ef0e7d1..59115781 100644 --- a/crates/server/src/policy/record/authorization.rs +++ b/crates/server/src/policy/record/authorization.rs @@ -5,10 +5,9 @@ use std::collections::{HashMap, HashSet}; use warg_crypto::signing::KeyID; use warg_protocol::{ package::{PackageEntry, PackageRecord}, - registry::PackageId, + registry::PackageName, ProtoEnvelope, }; -use wasmparser::names::KebabStr; /// A policy that ensures a published record is signed by an authorized key. #[derive(Default, Deserialize)] @@ -19,7 +18,7 @@ pub struct AuthorizedKeyPolicy { #[serde(default, rename = "namespace")] namespaces: HashMap, #[serde(default, rename = "package")] - packages: HashMap, + packages: HashMap, } #[derive(Default, Deserialize)] @@ -66,34 +65,37 @@ impl AuthorizedKeyPolicy { fn namespace_or_default_mut(&mut self, namespace: impl Into) -> Result<&mut LogPolicy> { let namespace = namespace.into(); - if KebabStr::new(&namespace).is_none() { - bail!("namespace `{namespace}` is not a legal kebab-case identifier"); + if !PackageName::is_valid_namespace(&namespace) { + bail!("namespace `{namespace}` is not a valid kebab-cased string"); } Ok(self.namespaces.entry(namespace).or_default()) } /// Sets an authorized key for publishing to a particular package. - pub fn with_package_key(mut self, package_id: impl Into, key: KeyID) -> Result { - self.package_or_default_mut(package_id)?.keys.insert(key); + pub fn with_package_key(mut self, package_name: impl Into, key: KeyID) -> Result { + self.package_or_default_mut(package_name)?.keys.insert(key); Ok(self) } /// Enables delegation for a particular package. - pub fn with_package_delegation(mut self, package_id: impl Into) -> Result { - self.package_or_default_mut(package_id)?.delegation = true; + pub fn with_package_delegation(mut self, package_name: impl Into) -> Result { + self.package_or_default_mut(package_name)?.delegation = true; Ok(self) } - fn package_or_default_mut(&mut self, package_id: impl Into) -> Result<&mut LogPolicy> { - let package_id = PackageId::new(package_id)?; - Ok(self.packages.entry(package_id).or_default()) + fn package_or_default_mut( + &mut self, + package_name: impl Into, + ) -> Result<&mut LogPolicy> { + let package_name = PackageName::new(package_name)?; + Ok(self.packages.entry(package_name).or_default()) } pub fn key_authorized_for_entry( &self, key: &KeyID, - package: &PackageId, + package: &PackageName, is_init: bool, ) -> bool { if self.superuser_keys.contains(key) { @@ -119,15 +121,15 @@ impl AuthorizedKeyPolicy { impl RecordPolicy for AuthorizedKeyPolicy { fn check( &self, - id: &PackageId, + name: &PackageName, record: &ProtoEnvelope, ) -> RecordPolicyResult<()> { let key = record.key_id(); for entry in &record.as_ref().entries { let is_init = matches!(entry, PackageEntry::Init { .. }); - if !self.key_authorized_for_entry(key, id, is_init) { + if !self.key_authorized_for_entry(key, name, is_init) { return Err(RecordPolicyError::Unauthorized(format!( - "key id `{key}` is not authorized to publish to package `{id}`", + "key id `{key}` is not authorized to publish to package `{name}`", ))); } } @@ -151,9 +153,9 @@ mod tests { .with_namespace_key("my-namespace", namespace_key.clone())? .with_package_key("my-namespace:my-package", package_key.clone())?; - let my_package: PackageId = "my-namespace:my-package".parse()?; - let my_ns_other_package: PackageId = "my-namespace:other-package".parse()?; - let other_namespace: PackageId = "other-namespace:any-package".parse()?; + let my_package: PackageName = "my-namespace:my-package".parse()?; + let my_ns_other_package: PackageName = "my-namespace:other-package".parse()?; + let other_namespace: PackageName = "other-namespace:any-package".parse()?; assert!(policy.key_authorized_for_entry(&super_key, &my_package, false)); assert!(policy.key_authorized_for_entry(&super_key, &my_ns_other_package, false)); @@ -185,8 +187,8 @@ mod tests { .with_package_key("ns2:pkg", authed_key.clone())? .with_package_delegation("ns2:pkg")?; - let ns1_pkg: PackageId = "ns1:pkg".parse()?; - let ns2_pkg: PackageId = "ns2:pkg".parse()?; + let ns1_pkg: PackageName = "ns1:pkg".parse()?; + let ns2_pkg: PackageName = "ns2:pkg".parse()?; assert!(policy.key_authorized_for_entry(&authed_key, &ns1_pkg, true)); assert!(policy.key_authorized_for_entry(&authed_key, &ns1_pkg, false)); diff --git a/crates/server/src/policy/record/mod.rs b/crates/server/src/policy/record/mod.rs index 818de11d..04c3579e 100644 --- a/crates/server/src/policy/record/mod.rs +++ b/crates/server/src/policy/record/mod.rs @@ -1,6 +1,6 @@ //! Module for server record policy implementations. use thiserror::Error; -use warg_protocol::{package::PackageRecord, registry::PackageId, ProtoEnvelope}; +use warg_protocol::{package::PackageRecord, registry::PackageName, ProtoEnvelope}; mod authorization; pub use authorization::*; @@ -27,7 +27,7 @@ pub trait RecordPolicy: Send + Sync { /// Checks the record against the policy. fn check( &self, - id: &PackageId, + name: &PackageName, record: &ProtoEnvelope, ) -> RecordPolicyResult<()>; } @@ -56,11 +56,11 @@ impl RecordPolicyCollection { impl RecordPolicy for RecordPolicyCollection { fn check( &self, - id: &PackageId, + name: &PackageName, record: &ProtoEnvelope, ) -> RecordPolicyResult<()> { for policy in &self.policies { - policy.check(id, record)?; + policy.check(name, record)?; } Ok(()) diff --git a/src/bin/warg.rs b/src/bin/warg.rs index 5fa4b07d..9d2d1a4e 100644 --- a/src/bin/warg.rs +++ b/src/bin/warg.rs @@ -67,11 +67,11 @@ fn describe_client_error(e: &ClientError) { ClientError::NoDefaultUrl => { eprintln!("error: {e}; use the `config` subcommand to set a default URL"); } - ClientError::PackageValidationFailed { id, inner } => { - eprintln!("error: the log for package `{id}` is invalid: {inner}") + ClientError::PackageValidationFailed { name, inner } => { + eprintln!("error: the log for package `{name}` is invalid: {inner}") } - ClientError::PackageLogEmpty { id } => { - eprintln!("error: the log for package `{id}` is empty (the registry could be lying)"); + ClientError::PackageLogEmpty { name } => { + eprintln!("error: the log for package `{name}` is empty (the registry could be lying)"); eprintln!("see issue https://github.com/bytecodealliance/registry/issues/66"); } _ => eprintln!("error: {e}"), diff --git a/src/commands/download.rs b/src/commands/download.rs index 4b71c8d8..67c615d0 100644 --- a/src/commands/download.rs +++ b/src/commands/download.rs @@ -1,7 +1,7 @@ use super::CommonOptions; use anyhow::{anyhow, Result}; use clap::Args; -use warg_protocol::{registry::PackageId, VersionReq}; +use warg_protocol::{registry::PackageName, VersionReq}; /// Download a warg registry package. #[derive(Args)] @@ -10,9 +10,9 @@ pub struct DownloadCommand { /// The common command options. #[clap(flatten)] pub common: CommonOptions, - /// The identifier of the package to download. + /// The package name to download. #[clap(value_name = "PACKAGE")] - pub id: PackageId, + pub name: PackageName, #[clap(long, short, value_name = "VERSION")] /// The version requirement of the package to download; defaults to `*`. pub version: Option, @@ -24,22 +24,25 @@ impl DownloadCommand { let config = self.common.read_config()?; let client = self.common.create_client(&config)?; - println!("downloading package `{id}`...", id = self.id); + println!("downloading package `{name}`...", name = self.name); let res = client - .download(&self.id, self.version.as_ref().unwrap_or(&VersionReq::STAR)) + .download( + &self.name, + self.version.as_ref().unwrap_or(&VersionReq::STAR), + ) .await? .ok_or_else(|| { anyhow!( - "a version of package `{id}` that satisfies `{version}` was not found", - id = self.id, + "a version of package `{name}` that satisfies `{version}` was not found", + name = self.name, version = self.version.as_ref().unwrap_or(&VersionReq::STAR) ) })?; println!( - "downloaded version {version} of package `{id}` ({digest})", - id = self.id, + "downloaded version {version} of package `{name}` ({digest})", + name = self.name, version = res.version, digest = res.digest ); diff --git a/src/commands/info.rs b/src/commands/info.rs index 921b0ec9..0d328129 100644 --- a/src/commands/info.rs +++ b/src/commands/info.rs @@ -3,7 +3,7 @@ use anyhow::Result; use clap::Args; use warg_client::storage::{PackageInfo, RegistryStorage}; use warg_crypto::hash::AnyHash; -use warg_protocol::{registry::PackageId, Version}; +use warg_protocol::{registry::PackageName, Version}; /// Display client storage information. #[derive(Args)] @@ -14,7 +14,7 @@ pub struct InfoCommand { /// Only show information for the specified package. #[clap(value_name = "PACKAGE")] - pub package: Option, + pub package: Option, } impl InfoCommand { @@ -45,7 +45,7 @@ impl InfoCommand { } fn print_package_info(info: &PackageInfo) { - println!(" id: {id}", id = info.id); + println!(" name: {name}", name = info.name); println!(" versions:"); info.state.releases().for_each(|r| { if let Some(content) = r.content() { diff --git a/src/commands/publish.rs b/src/commands/publish.rs index e67727b7..dab66c9d 100644 --- a/src/commands/publish.rs +++ b/src/commands/publish.rs @@ -16,7 +16,7 @@ use warg_crypto::{ }; use warg_protocol::{ package::Permission, - registry::{PackageId, RecordId}, + registry::{PackageName, RecordId}, Version, }; @@ -27,7 +27,7 @@ const DEFAULT_WAIT_INTERVAL: Duration = Duration::from_secs(1); /// was no pending publish. async fn enqueue<'a, T>( client: &'a FileSystemClient, - id: &PackageId, + name: &PackageName, entry: impl FnOnce(&'a FileSystemClient) -> T, ) -> Result> where @@ -35,17 +35,17 @@ where { match client.registry().load_publish().await? { Some(mut info) => { - if &info.id != id { + if &info.name != name { bail!( - "there is already publish in progress for package `{id}`", - id = info.id + "there is already publish in progress for package `{name}`", + name = info.name ); } let entry = entry(client).await?; if matches!(entry, PublishEntry::Init) && info.initializing() { - bail!("there is already a pending initializing for package `{id}`"); + bail!("there is already a pending initializing for package `{name}`"); } info.entries.push(entry); @@ -106,9 +106,9 @@ pub struct PublishInitCommand { /// The common command options. #[clap(flatten)] pub common: CommonOptions, - /// The identifier of the package being initialized. + /// The package name being initialized. #[clap(value_name = "PACKAGE")] - pub id: PackageId, + pub name: PackageName, /// Whether to wait for the publish to complete. #[clap(long)] pub no_wait: bool, @@ -120,7 +120,7 @@ impl PublishInitCommand { let config = self.common.read_config()?; let client = self.common.create_client(&config)?; - match enqueue(&client, &self.id, |_| { + match enqueue(&client, &self.name, |_| { std::future::ready(Ok(PublishEntry::Init)) }) .await? @@ -131,7 +131,7 @@ impl PublishInitCommand { .publish_with_info( &signing_key, PublishInfo { - id: self.id.clone(), + name: self.name.clone(), head: None, entries: vec![entry], }, @@ -142,16 +142,19 @@ impl PublishInitCommand { println!("submitted record `{record_id}` for publishing"); } else { client - .wait_for_publish(&self.id, &record_id, DEFAULT_WAIT_INTERVAL) + .wait_for_publish(&self.name, &record_id, DEFAULT_WAIT_INTERVAL) .await?; - println!("published initialization of package `{id}`", id = self.id,); + println!( + "published initialization of package `{name}`", + name = self.name, + ); } } None => { println!( - "added initialization of package `{id}` to pending publish", - id = self.id + "added initialization of package `{name}` to pending publish", + name = self.name ); } } @@ -167,9 +170,9 @@ pub struct PublishReleaseCommand { /// The common command options. #[clap(flatten)] pub common: CommonOptions, - /// The identifier of the package being published. + /// The package name being published. #[clap(long, short, value_name = "PACKAGE")] - pub id: PackageId, + pub name: PackageName, /// The version of the package being published. #[clap(long, short, value_name = "VERSION")] pub version: Version, @@ -189,7 +192,7 @@ impl PublishReleaseCommand { let path = self.path.clone(); let version = self.version.clone(); - match enqueue(&client, &self.id, move |c| async move { + match enqueue(&client, &self.name, move |c| async move { let content = c .content() .store_content( @@ -215,7 +218,7 @@ impl PublishReleaseCommand { .publish_with_info( &signing_key, PublishInfo { - id: self.id.clone(), + name: self.name.clone(), head: None, entries: vec![entry], }, @@ -226,21 +229,21 @@ impl PublishReleaseCommand { println!("submitted record `{record_id}` for publishing"); } else { client - .wait_for_publish(&self.id, &record_id, DEFAULT_WAIT_INTERVAL) + .wait_for_publish(&self.name, &record_id, DEFAULT_WAIT_INTERVAL) .await?; println!( - "published version {version} of package `{id}`", + "published version {version} of package `{name}`", version = self.version, - id = self.id + name = self.name ); } } None => { println!( - "added release of version {version} for package `{id}` to pending publish", + "added release of version {version} for package `{name}` to pending publish", version = self.version, - id = self.id + name = self.name ); } } @@ -256,9 +259,9 @@ pub struct PublishYankCommand { /// The common command options. #[clap(flatten)] pub common: CommonOptions, - /// The identifier of the package being yanked. + /// The package name being yanked. #[clap(long, short, value_name = "PACKAGE")] - pub id: PackageId, + pub name: PackageName, /// The version of the package being yanked. #[clap(long, short, value_name = "VERSION")] pub version: Version, @@ -274,7 +277,7 @@ impl PublishYankCommand { let client = self.common.create_client(&config)?; let version = self.version.clone(); - match enqueue(&client, &self.id, move |_| async move { + match enqueue(&client, &self.name, move |_| async move { Ok(PublishEntry::Yank { version }) }) .await? @@ -285,7 +288,7 @@ impl PublishYankCommand { .publish_with_info( &signing_key, PublishInfo { - id: self.id.clone(), + name: self.name.clone(), head: None, entries: vec![entry], }, @@ -296,21 +299,21 @@ impl PublishYankCommand { println!("submitted record `{record_id}` for publishing"); } else { client - .wait_for_publish(&self.id, &record_id, DEFAULT_WAIT_INTERVAL) + .wait_for_publish(&self.name, &record_id, DEFAULT_WAIT_INTERVAL) .await?; println!( - "yanked version {version} of package `{id}`", + "yanked version {version} of package `{name}`", version = self.version, - id = self.id + name = self.name ); } } None => { println!( - "added yank of version {version} for package `{id}` to pending publish", + "added yank of version {version} for package `{name}` to pending publish", version = self.version, - id = self.id + name = self.name ); } } @@ -326,9 +329,9 @@ pub struct PublishGrantCommand { /// The common command options. #[clap(flatten)] pub common: CommonOptions, - /// The identifier of the package. + /// The package name. #[clap(long, short, value_name = "PACKAGE")] - pub id: PackageId, + pub name: PackageName, /// The public key to grant permissions to. #[clap(value_name = "PUBLIC_KEY")] pub public_key: PublicKey, @@ -350,7 +353,7 @@ impl PublishGrantCommand { let config = self.common.read_config()?; let client = self.common.create_client(&config)?; - match enqueue(&client, &self.id, |_| async { + match enqueue(&client, &self.name, |_| async { Ok(PublishEntry::Grant { key: self.public_key.clone(), permissions: self.permissions.clone(), @@ -364,7 +367,7 @@ impl PublishGrantCommand { .publish_with_info( &signing_key, PublishInfo { - id: self.id.clone(), + name: self.name.clone(), head: None, entries: vec![entry], }, @@ -375,23 +378,23 @@ impl PublishGrantCommand { println!("submitted record `{record_id}` for publishing"); } else { client - .wait_for_publish(&self.id, &record_id, DEFAULT_WAIT_INTERVAL) + .wait_for_publish(&self.name, &record_id, DEFAULT_WAIT_INTERVAL) .await?; println!( - "granted ({permissions_str}) to key ID `{key_id}` for package `{id}`", + "granted ({permissions_str}) to key ID `{key_id}` for package `{name}`", permissions_str = self.permissions.iter().join(","), key_id = self.public_key.fingerprint(), - id = self.id + name = self.name ); } } None => { println!( - "added grant of ({permissions_str}) to key ID `{key_id}` for package `{id}` to pending publish", + "added grant of ({permissions_str}) to key ID `{key_id}` for package `{name}` to pending publish", permissions_str = self.permissions.iter().join(","), key_id = self.public_key.fingerprint(), - id = self.id + name = self.name ); } } @@ -407,9 +410,9 @@ pub struct PublishRevokeCommand { /// The common command options. #[clap(flatten)] pub common: CommonOptions, - /// The identifier of the package. + /// The package name. #[clap(long, short, value_name = "PACKAGE")] - pub id: PackageId, + pub name: PackageName, /// The key ID to revoke permissions from. #[clap(value_name = "KEY_ID")] pub key: KeyID, @@ -431,7 +434,7 @@ impl PublishRevokeCommand { let config = self.common.read_config()?; let client = self.common.create_client(&config)?; - match enqueue(&client, &self.id, |_| async { + match enqueue(&client, &self.name, |_| async { Ok(PublishEntry::Revoke { key_id: self.key.clone(), permissions: self.permissions.clone(), @@ -445,7 +448,7 @@ impl PublishRevokeCommand { .publish_with_info( &signing_key, PublishInfo { - id: self.id.clone(), + name: self.name.clone(), head: None, entries: vec![entry], }, @@ -456,23 +459,23 @@ impl PublishRevokeCommand { println!("submitted record `{record_id}` for publishing"); } else { client - .wait_for_publish(&self.id, &record_id, DEFAULT_WAIT_INTERVAL) + .wait_for_publish(&self.name, &record_id, DEFAULT_WAIT_INTERVAL) .await?; println!( - "revoked ({permissions_str}) from key ID `{key_id}` for package `{id}`", + "revoked ({permissions_str}) from key ID `{key_id}` for package `{name}`", permissions_str = self.permissions.iter().join(","), key_id = self.key, - id = self.id + name = self.name ); } } None => { println!( - "added revoke of ({permissions_str}) from key ID `{key_id}` for package `{id}` to pending publish", + "added revoke of ({permissions_str}) from key ID `{key_id}` for package `{name}` to pending publish", permissions_str = self.permissions.iter().join(","), key_id = self.key, - id = self.id + name = self.name ); } } @@ -488,9 +491,9 @@ pub struct PublishStartCommand { /// The common command options. #[clap(flatten)] pub common: CommonOptions, - /// The identifier of the package being published. + /// The package name being published. #[clap(value_name = "PACKAGE")] - pub id: PackageId, + pub name: PackageName, } impl PublishStartCommand { @@ -500,18 +503,18 @@ impl PublishStartCommand { let client = self.common.create_client(&config)?; match client.registry().load_publish().await? { - Some(info) => bail!("a publish is already in progress for package `{id}`; use `publish abort` to abort the current publish", id = info.id), + Some(info) => bail!("a publish is already in progress for package `{name}`; use `publish abort` to abort the current publish", name = info.name), None => { client.registry().store_publish(Some(&PublishInfo { - id: self.id.clone(), + name: self.name.clone(), head: None, entries: Default::default(), })) .await?; println!( - "started new pending publish for package `{id}`", - id = self.id + "started new pending publish for package `{name}`", + name = self.name ); Ok(()) }, @@ -536,8 +539,8 @@ impl PublishListCommand { match client.registry().load_publish().await? { Some(info) => { println!( - "publishing package `{id}` with {count} record(s) to publish\n", - id = info.id, + "publishing package `{name}` with {count} record(s) to publish\n", + name = info.name, count = info.entries.len() ); @@ -593,8 +596,8 @@ impl PublishAbortCommand { Some(info) => { client.registry().store_publish(None).await?; println!( - "aborted the pending publish for package `{id}`", - id = info.id + "aborted the pending publish for package `{name}`", + name = info.name ); } None => bail!("no pending publish to abort"), @@ -623,7 +626,10 @@ impl PublishSubmitCommand { match client.registry().load_publish().await? { Some(info) => { - println!("submitting publish for package `{id}`...", id = info.id); + println!( + "submitting publish for package `{name}`...", + name = info.name + ); let signing_key = self.common.signing_key(client.url())?; let record_id = client.publish_with_info(&signing_key, info.clone()).await?; @@ -634,20 +640,20 @@ impl PublishSubmitCommand { println!("submitted record `{record_id}` for publishing"); } else { client - .wait_for_publish(&info.id, &record_id, DEFAULT_WAIT_INTERVAL) + .wait_for_publish(&info.name, &record_id, DEFAULT_WAIT_INTERVAL) .await?; for entry in &info.entries { - let id = &info.id; + let name = &info.name; match entry { PublishEntry::Init => { - println!("published initialization of package `{id}`"); + println!("published initialization of package `{name}`"); } PublishEntry::Release { version, .. } => { - println!("published version {version} of package `{id}`"); + println!("published version {version} of package `{name}`"); } PublishEntry::Yank { version } => { - println!("yanked version {version} of package `{id}`") + println!("yanked version {version} of package `{name}`") } PublishEntry::Grant { key, permissions } => { println!( @@ -683,7 +689,7 @@ pub struct PublishWaitCommand { /// The name of the published package. #[clap(value_name = "PACKAGE")] - pub id: PackageId, + pub name: PackageName, /// The identifier of the package record to wait for completion. #[clap(value_name = "RECORD")] @@ -698,17 +704,17 @@ impl PublishWaitCommand { let record_id = RecordId::from(self.record_id); println!( - "waiting for record `{record_id} of package `{id}` to be published...", - id = self.id + "waiting for record `{record_id} of package `{name}` to be published...", + name = self.name ); client - .wait_for_publish(&self.id, &record_id, Duration::from_secs(1)) + .wait_for_publish(&self.name, &record_id, Duration::from_secs(1)) .await?; println!( - "record `{record_id} of package `{id}` has been published", - id = self.id + "record `{record_id} of package `{name}` has been published", + name = self.name ); Ok(()) diff --git a/src/commands/run.rs b/src/commands/run.rs index 73cb504c..ef97aa29 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -3,7 +3,7 @@ use crate::demo; use anyhow::{anyhow, Result}; use clap::Args; use warg_client::storage::ContentStorage; -use warg_protocol::{registry::PackageId, VersionReq}; +use warg_protocol::{registry::PackageName, VersionReq}; /// Run a package. #[derive(Args)] @@ -15,9 +15,9 @@ pub struct RunCommand { /// The version requirement of the package to download; defaults to `*`. #[clap(long, short, value_name = "VERSION")] pub version: Option, - /// The identifier of the package to run. + /// The package name to run. #[clap(value_name = "PACKAGE")] - pub id: PackageId, + pub name: PackageName, /// The arguments to the package. pub args: Vec, } @@ -28,22 +28,25 @@ impl RunCommand { let config = self.common.read_config()?; let client = self.common.create_client(&config)?; - println!("downloading package `{id}`...", id = self.id); + println!("downloading package `{name}`...", name = self.name); let res = client - .download(&self.id, self.version.as_ref().unwrap_or(&VersionReq::STAR)) + .download( + &self.name, + self.version.as_ref().unwrap_or(&VersionReq::STAR), + ) .await? .ok_or_else(|| { anyhow!( - "a version of package `{id}` that satisfies `{version}` was not found", - id = self.id, + "a version of package `{name}` that satisfies `{version}` was not found", + name = self.name, version = self.version.as_ref().unwrap_or(&VersionReq::STAR) ) })?; println!( - "running version {version} of package `{id}` ({digest})", - id = self.id, + "running version {version} of package `{name}` ({digest})", + name = self.name, version = res.version, digest = res.digest ); diff --git a/tests/client.rs b/tests/client.rs index 255ae64a..66ad3c0f 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -5,7 +5,7 @@ use warg_client::{ storage::{ContentStorage, PublishEntry, PublishInfo, RegistryStorage}, Config, FileSystemClient, StorageLockResult, }; -use warg_protocol::registry::PackageId; +use warg_protocol::registry::PackageName; pub mod support; @@ -19,7 +19,7 @@ fn create_client(config: &Config) -> Result { #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn client_incrementally_fetches() -> Result<()> { const RELEASE_COUNT: usize = 300; - const PACKAGE_ID: &str = "test:package"; + const PACKAGE_NAME: &str = "test:package"; let (_server, config) = spawn_server(&root().await?, None, None, None).await?; @@ -39,12 +39,12 @@ async fn client_incrementally_fetches() -> Result<()> { // Here we don't wait for a single publish operation to complete, except for the last one // If the last one is accepted, it implies that all the previous ones were accepted as well - let id = PackageId::new(PACKAGE_ID)?; + let name = PackageName::new(PACKAGE_NAME)?; let mut head = client .publish_with_info( &signing_key, PublishInfo { - id: id.clone(), + name: name.clone(), head: None, entries: vec![PublishEntry::Init], }, @@ -56,7 +56,7 @@ async fn client_incrementally_fetches() -> Result<()> { .publish_with_info( &signing_key, PublishInfo { - id: id.clone(), + name: name.clone(), head: Some(head), entries: vec![PublishEntry::Release { version: format!("0.{i}.0").parse().unwrap(), @@ -68,7 +68,7 @@ async fn client_incrementally_fetches() -> Result<()> { } client - .wait_for_publish(&id, &head, Duration::from_millis(100)) + .wait_for_publish(&name, &head, Duration::from_millis(100)) .await?; drop(client); @@ -84,16 +84,16 @@ async fn client_incrementally_fetches() -> Result<()> { client.update().await?; // Fetch the package log - client.upsert([&id]).await?; + client.upsert([&name]).await?; // Ensure that the package is in the packages list let packages = client.registry().load_packages().await?; - assert_eq!(packages[0].id.as_ref(), PACKAGE_ID); + assert_eq!(packages[0].name.as_ref(), PACKAGE_NAME); // Ensure the package log exists and has releases with all with the same digest let package = client .registry() - .load_package(&id) + .load_package(&name) .await? .context("package does not exist in client storage")?; diff --git a/tests/postgres/mod.rs b/tests/postgres/mod.rs index 9b69cf97..dab03752 100644 --- a/tests/postgres/mod.rs +++ b/tests/postgres/mod.rs @@ -58,11 +58,11 @@ async fn it_works_with_postgres() -> TestResult { test_get_ledger(&config).await?; let mut packages = vec![ - PackageId::new("test:component")?, - PackageId::new("test:yankee")?, - PackageId::new("test:wit-package")?, - PackageId::new("test:unauthorized-key")?, - PackageId::new("test:name")?, + PackageName::new("test:component")?, + PackageName::new("test:yankee")?, + PackageName::new("test:wit-package")?, + PackageName::new("test:unauthorized-key")?, + PackageName::new("test:name")?, ]; // There should be two log entries in the registry @@ -82,7 +82,7 @@ async fn it_works_with_postgres() -> TestResult { test_unknown_signing_key(&config).await?; - packages.push(PackageId::new("test:unknown-key")?); + packages.push(PackageName::new("test:unknown-key")?); let client = api::Client::new(config.default_url.as_ref().unwrap())?; let ts_checkpoint = client.latest_checkpoint().await?; diff --git a/tests/server.rs b/tests/server.rs index 35f564df..1fd019bf 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -27,7 +27,7 @@ use warg_crypto::{ }; use warg_protocol::{ package::{PackageEntry, PackageRecord, PACKAGE_RECORD_VERSION}, - registry::{LogId, PackageId}, + registry::{LogId, PackageName}, ProtoEnvelope, ProtoEnvelopeBody, Version, }; use wit_component::DecodedWasm; @@ -67,15 +67,15 @@ async fn test_initial_checkpoint(config: &Config) -> Result<()> { } async fn test_component_publishing(config: &Config) -> Result<()> { - const PACKAGE_ID: &str = "test:component"; + const PACKAGE_NAME: &str = "test:component"; const PACKAGE_VERSION: &str = "0.1.0"; - let id = PackageId::new(PACKAGE_ID)?; + let name = PackageName::new(PACKAGE_NAME)?; let client = create_client(config)?; let signing_key = test_signing_key(); let digest = publish_component( &client, - &id, + &name, PACKAGE_VERSION, "(component)", true, @@ -84,9 +84,9 @@ async fn test_component_publishing(config: &Config) -> Result<()> { .await?; // Assert that the package can be downloaded - client.upsert([&id]).await?; + client.upsert([&name]).await?; let download = client - .download(&id, &PACKAGE_VERSION.parse()?) + .download(&name, &PACKAGE_VERSION.parse()?) .await? .context("failed to resolve package")?; @@ -109,22 +109,22 @@ async fn test_component_publishing(config: &Config) -> Result<()> { } // Assert that a different version can't be downloaded - assert!(client.download(&id, &"0.2.0".parse()?).await?.is_none()); + assert!(client.download(&name, &"0.2.0".parse()?).await?.is_none()); Ok(()) } async fn test_package_yanking(config: &Config) -> Result<()> { - const PACKAGE_ID: &str = "test:yankee"; + const PACKAGE_NAME: &str = "test:yankee"; const PACKAGE_VERSION: &str = "0.1.0"; // Publish release - let id = PackageId::new(PACKAGE_ID)?; + let name = PackageName::new(PACKAGE_NAME)?; let client = create_client(config)?; let signing_key = test_signing_key(); publish( &client, - &id, + &name, PACKAGE_VERSION, wat::parse_str("(component)")?, true, @@ -137,7 +137,7 @@ async fn test_package_yanking(config: &Config) -> Result<()> { .publish_with_info( &signing_key, PublishInfo { - id: id.clone(), + name: name.clone(), head: None, entries: vec![PublishEntry::Yank { version: PACKAGE_VERSION.parse()?, @@ -146,37 +146,37 @@ async fn test_package_yanking(config: &Config) -> Result<()> { ) .await?; client - .wait_for_publish(&id, &record_id, Duration::from_millis(100)) + .wait_for_publish(&name, &record_id, Duration::from_millis(100)) .await?; // Assert that the package is yanked - client.upsert([&id]).await?; - let opt = client.download(&id, &PACKAGE_VERSION.parse()?).await?; + client.upsert([&name]).await?; + let opt = client.download(&name, &PACKAGE_VERSION.parse()?).await?; assert!(opt.is_none(), "expected no download, got {opt:?}"); Ok(()) } async fn test_wit_publishing(config: &Config) -> Result<()> { - const PACKAGE_ID: &str = "test:wit-package"; + const PACKAGE_NAME: &str = "test:wit-package"; const PACKAGE_VERSION: &str = "0.1.0"; - let id = PackageId::new(PACKAGE_ID)?; + let name = PackageName::new(PACKAGE_NAME)?; let client = create_client(config)?; let signing_key = test_signing_key(); let digest = publish_wit( &client, - &id, + &name, PACKAGE_VERSION, - &format!("package {PACKAGE_ID}\nworld foo {{}}"), + &format!("package {PACKAGE_NAME}\nworld foo {{}}"), true, &signing_key, ) .await?; // Assert that the package can be downloaded - client.upsert([&id]).await?; + client.upsert([&name]).await?; let download = client - .download(&id, &PACKAGE_VERSION.parse()?) + .download(&name, &PACKAGE_VERSION.parse()?) .await? .context("failed to resolve package")?; @@ -199,23 +199,23 @@ async fn test_wit_publishing(config: &Config) -> Result<()> { } // Assert that a different version can't be downloaded - assert!(client.download(&id, &"0.2.0".parse()?).await?.is_none()); + assert!(client.download(&name, &"0.2.0".parse()?).await?.is_none()); Ok(()) } async fn test_wasm_content_policy(config: &Config) -> Result<()> { - const PACKAGE_ID: &str = "test:bad-content"; + const PACKAGE_NAME: &str = "test:bad-content"; const PACKAGE_VERSION: &str = "0.1.0"; // Publish empty content to the server // This should be rejected by policy because it is not valid WebAssembly - let id = PackageId::new(PACKAGE_ID)?; + let name = PackageName::new(PACKAGE_NAME)?; let client = create_client(config)?; let signing_key = test_signing_key(); match publish( &client, - &id, + &name, PACKAGE_VERSION, Vec::new(), true, @@ -226,11 +226,11 @@ async fn test_wasm_content_policy(config: &Config) -> Result<()> { .downcast::() { Ok(ClientError::PublishRejected { - id: rejected_id, + name: rejected_name, record_id, reason, }) => { - assert_eq!(id, rejected_id); + assert_eq!(name, rejected_name); assert_eq!( reason, "content is not valid WebAssembly: unexpected end-of-file (at offset 0x0)" @@ -238,16 +238,16 @@ async fn test_wasm_content_policy(config: &Config) -> Result<()> { // Waiting on the publish should fail with a rejection as well match client - .wait_for_publish(&id, &record_id, Duration::from_millis(100)) + .wait_for_publish(&name, &record_id, Duration::from_millis(100)) .await .expect_err("expected wait for publish to fail") { ClientError::PublishRejected { - id: rejected_id, + name: rejected_name, record_id: other, reason, } => { - assert_eq!(id, rejected_id); + assert_eq!(name, rejected_name); assert_eq!(record_id, other); assert_eq!( reason, @@ -264,16 +264,16 @@ async fn test_wasm_content_policy(config: &Config) -> Result<()> { } async fn test_unauthorized_signing_key(config: &Config) -> Result<()> { - const PACKAGE_ID: &str = "test:unauthorized-key"; + const PACKAGE_NAME: &str = "test:unauthorized-key"; const PACKAGE_VERSION: &str = "0.1.0"; // Start by publishing a new component package - let id = PackageId::new(PACKAGE_ID)?; + let name = PackageName::new(PACKAGE_NAME)?; let client = create_client(config)?; let signing_key = test_signing_key(); publish_component( &client, - &id, + &name, PACKAGE_VERSION, "(component)", true, @@ -286,7 +286,7 @@ async fn test_unauthorized_signing_key(config: &Config) -> Result<()> { let message = format!( "{:#}", - publish_component(&client, &id, "0.2.0", "(component)", false, &signing_key,) + publish_component(&client, &name, "0.2.0", "(component)", false, &signing_key,) .await .expect_err("expected publish to fail") ); @@ -300,16 +300,16 @@ async fn test_unauthorized_signing_key(config: &Config) -> Result<()> { } async fn test_unknown_signing_key(config: &Config) -> Result<()> { - const PACKAGE_ID: &str = "test:unknown-key"; + const PACKAGE_NAME: &str = "test:unknown-key"; const PACKAGE_VERSION: &str = "0.1.0"; // Start by publishing a new component package - let id = PackageId::new(PACKAGE_ID)?; + let name = PackageName::new(PACKAGE_NAME)?; let client = create_client(config)?; let signing_key = test_signing_key(); publish_component( &client, - &id, + &name, PACKAGE_VERSION, "(component)", true, @@ -323,7 +323,7 @@ async fn test_unknown_signing_key(config: &Config) -> Result<()> { let message = format!( "{:#}", - publish_component(&client, &id, "0.2.0", "(component)", false, &signing_key,) + publish_component(&client, &name, "0.2.0", "(component)", false, &signing_key,) .await .expect_err("expected publish to fail") ); @@ -342,7 +342,7 @@ async fn test_publishing_name_conflict(config: &Config) -> Result<()> { publish_component( &client, - &PackageId::new("test:name")?, + &PackageName::new("test:name")?, "0.1.0", "(component)", true, @@ -353,7 +353,7 @@ async fn test_publishing_name_conflict(config: &Config) -> Result<()> { // should be rejected publish_component( &client, - &PackageId::new("test:NAME")?, + &PackageName::new("test:NAME")?, "0.1.1", "(component)", true, @@ -366,11 +366,11 @@ async fn test_publishing_name_conflict(config: &Config) -> Result<()> { } async fn test_invalid_signature(config: &Config) -> Result<()> { - const PACKAGE_ID: &str = "test:invalid-signature"; + const PACKAGE_NAME: &str = "test:invalid-signature"; // Use a reqwest client directly here as we're going to be sending an invalid signature - let id = PackageId::new(PACKAGE_ID)?; - let log_id = LogId::package_log::(&id); + let name = PackageName::new(PACKAGE_NAME)?; + let log_id = LogId::package_log::(&name); let url = Url::parse(config.default_url.as_ref().unwrap())? .join(&paths::publish_package_record(&log_id)) .unwrap(); @@ -390,7 +390,7 @@ async fn test_invalid_signature(config: &Config) -> Result<()> { )?; let body = PublishRecordRequest { - package_id: Cow::Borrowed(&id), + package_name: Cow::Borrowed(&name), record: Cow::Owned(ProtoEnvelopeBody::from(record)), content_sources: Default::default(), }; @@ -422,15 +422,15 @@ async fn test_invalid_signature(config: &Config) -> Result<()> { } async fn test_custom_content_url(config: &Config) -> Result<()> { - const PACKAGE_ID: &str = "test:custom-content-url"; + const PACKAGE_NAME: &str = "test:custom-content-url"; const PACKAGE_VERSION: &str = "0.1.0"; - let id = PackageId::new(PACKAGE_ID)?; + let name = PackageName::new(PACKAGE_NAME)?; let client = create_client(config)?; let signing_key = test_signing_key(); let digest = publish_component( &client, - &id, + &name, PACKAGE_VERSION, "(component)", true, @@ -438,10 +438,10 @@ async fn test_custom_content_url(config: &Config) -> Result<()> { ) .await?; - client.upsert([&id]).await?; + client.upsert([&name]).await?; let package = client .registry() - .load_package(&id) + .load_package(&name) .await? .expect("expected the package to exist"); package @@ -473,8 +473,8 @@ async fn test_custom_content_url(config: &Config) -> Result<()> { } async fn test_fetch_package_names(config: &Config) -> Result<()> { - let id_1 = PackageId::new("test:component")?; - let log_id_1 = LogId::package_log::(&id_1); + let name_1 = PackageName::new("test:component")?; + let log_id_1 = LogId::package_log::(&name_1); let url = Url::parse(config.default_url.as_ref().unwrap())? .join(paths::fetch_package_names()) @@ -500,11 +500,11 @@ async fn test_fetch_package_names(config: &Config) -> Result<()> { "unexpected response from server: {status}", ); - let lookup_id_1 = names_resp.packages.get(&log_id_1); + let lookup_name_1 = names_resp.packages.get(&log_id_1); assert_eq!( - lookup_id_1, - Some(&Some(id_1.clone())), - "fetch of package name {id_1} mismatched to {lookup_id_1:?}" + lookup_name_1, + Some(&Some(name_1.clone())), + "fetch of package name {name_1} mismatched to {lookup_name_1:?}" ); Ok(()) diff --git a/tests/support/mod.rs b/tests/support/mod.rs index f261b34e..75c06fd0 100644 --- a/tests/support/mod.rs +++ b/tests/support/mod.rs @@ -17,7 +17,7 @@ use warg_crypto::{ hash::AnyHash, signing::{KeyID, PrivateKey}, }; -use warg_protocol::{operator, registry::PackageId}; +use warg_protocol::{operator, registry::PackageName}; use warg_server::{ datastore::DataStore, policy::{content::WasmContentPolicy, record::AuthorizedKeyPolicy}, @@ -170,7 +170,7 @@ pub async fn spawn_server( pub async fn publish( client: &FileSystemClient, - id: &PackageId, + name: &PackageName, version: &str, content: Vec, init: bool, @@ -197,7 +197,7 @@ pub async fn publish( .publish_with_info( signing_key, PublishInfo { - id: id.clone(), + name: name.clone(), head: None, entries, }, @@ -205,7 +205,7 @@ pub async fn publish( .await?; client - .wait_for_publish(id, &record_id, Duration::from_millis(100)) + .wait_for_publish(name, &record_id, Duration::from_millis(100)) .await?; Ok(digest) @@ -213,18 +213,26 @@ pub async fn publish( pub async fn publish_component( client: &FileSystemClient, - id: &PackageId, + name: &PackageName, version: &str, wat: &str, init: bool, signing_key: &PrivateKey, ) -> Result { - publish(client, id, version, wat::parse_str(wat)?, init, signing_key).await + publish( + client, + name, + version, + wat::parse_str(wat)?, + init, + signing_key, + ) + .await } pub async fn publish_wit( client: &FileSystemClient, - id: &PackageId, + name: &PackageName, version: &str, wit: &str, init: bool, @@ -235,7 +243,7 @@ pub async fn publish_wit( publish( client, - id, + name, version, wit_component::encode(&resolve, pkg)?, init,