feat: migrate protocol and HTTP to structured interface references
T-20: identify interfaces by exact scope/name pairs; add optional opaque scope_ref metadata and separate shape validation from path-dependent scope compatibility. Preserve call error precedence, strict JSON, validators and transport boundaries. Mechanically migrate consumers and document API/wire compatibility and deferred lifecycle semantics. Validation: cargo fmt --all --check; git diff --check; cargo check --workspace --all-targets; cargo test -p wip-protocol -p wip-http; cargo test --workspace (221 passing tests including doctests); cargo test --workspace --all-targets; nix develop --command cargo clippy --workspace --all-targets -- -D warnings; RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps. All pass. cargo/rustc 1.95.0; development-shell Clippy/rustc 1.97.1.
This commit is contained in:
@@ -3,12 +3,19 @@ use std::collections::BTreeMap;
|
||||
use wip_protocol::{
|
||||
CallOperationRequest, CallOperationResponse, EnumCase, FetchInterfaceRequest,
|
||||
FetchInterfaceResponse, FieldDeclaration, INTERFACE_FORMAT_V1, InterfaceDescriptor,
|
||||
InterfaceTarget, MAX_SAFE_INTEGER, NameNamespace, Object, ObjectObservation, ObserveRequest,
|
||||
OperationDeclaration, ParameterDeclaration, PathSegment, ProtocolError, ProtocolErrorCode,
|
||||
ProtocolInteraction, ProtocolResult, ReturnDeclaration, Target, TypeDeclaration, TypeExpr,
|
||||
UnionCase, ValidationErrorKind, Value, ValueKind,
|
||||
InterfaceReference, InterfaceTarget, MAX_SAFE_INTEGER, NameNamespace, Object,
|
||||
ObjectObservation, ObserveRequest, OperationDeclaration, ParameterDeclaration, PathSegment,
|
||||
ProtocolError, ProtocolErrorCode, ProtocolInteraction, ProtocolResult, ReturnDeclaration,
|
||||
Target, TypeDeclaration, TypeExpr, UnionCase, ValidationErrorKind, Value, ValueKind,
|
||||
};
|
||||
|
||||
fn reference(scope: &str, name: &str) -> InterfaceReference {
|
||||
InterfaceReference {
|
||||
scope: scope.into(),
|
||||
name: name.into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn record(entries: impl IntoIterator<Item = (&'static str, Value)>) -> Value {
|
||||
Value::Record(
|
||||
entries
|
||||
@@ -135,7 +142,7 @@ fn object(name: &str) -> Object {
|
||||
Object {
|
||||
name: name.into(),
|
||||
description: Some(format!("Object {name}")),
|
||||
interfaces: vec!["catalog-v1".into(), "search-v2".into()],
|
||||
interfaces: vec![reference("/", "catalog-v1"), reference("/", "search-v2")],
|
||||
r#ref: Some("opaque-object-ref".into()),
|
||||
validator: Some(vec![1, 2, 3]),
|
||||
}
|
||||
@@ -146,16 +153,19 @@ fn object_fields_have_canonical_identity_and_ordered_set_semantics() {
|
||||
let object = object("article");
|
||||
object.validate().unwrap();
|
||||
object.validate_at_path("/articles/article").unwrap();
|
||||
assert_eq!(object.interfaces, ["catalog-v1", "search-v2"]);
|
||||
assert_eq!(
|
||||
object.interfaces,
|
||||
[reference("/", "catalog-v1"), reference("/", "search-v2")]
|
||||
);
|
||||
|
||||
let duplicate = Object {
|
||||
interfaces: vec!["catalog-v1".into(), "catalog-v1".into()],
|
||||
interfaces: vec![reference("/", "catalog-v1"), reference("/", "catalog-v1")],
|
||||
..object.clone()
|
||||
};
|
||||
assert_eq!(
|
||||
duplicate.validate().unwrap_err().kind,
|
||||
ValidationErrorKind::DuplicateInterface {
|
||||
reference: "catalog-v1".into()
|
||||
reference: reference("/", "catalog-v1")
|
||||
}
|
||||
);
|
||||
|
||||
@@ -574,7 +584,8 @@ fn calls_validate_canonical_paths_named_arguments_and_unit_results() {
|
||||
validator: Some(vec![1]),
|
||||
},
|
||||
interface: InterfaceTarget {
|
||||
reference: "catalog-v1".into(),
|
||||
scope_ref: None,
|
||||
reference: reference("/", "catalog-v1"),
|
||||
validator: Some(vec![2]),
|
||||
},
|
||||
operation: "publish".into(),
|
||||
@@ -659,17 +670,18 @@ fn observe_and_fetch_interface_dtos_validate_request_response_correspondence() {
|
||||
}
|
||||
|
||||
let request = FetchInterfaceRequest {
|
||||
interface: "catalog-v1".into(),
|
||||
interface: reference("/", "catalog-v1"),
|
||||
};
|
||||
let response = FetchInterfaceResponse {
|
||||
interface: "catalog-v1".into(),
|
||||
scope_ref: None,
|
||||
interface: reference("/", "catalog-v1"),
|
||||
descriptor: descriptor(),
|
||||
validator: Some(vec![9]),
|
||||
};
|
||||
response.validate_for(&request).unwrap();
|
||||
assert!(matches!(
|
||||
FetchInterfaceResponse {
|
||||
interface: "other".into(),
|
||||
interface: reference("/", "other"),
|
||||
..response
|
||||
}
|
||||
.validate_for(&request)
|
||||
@@ -847,7 +859,7 @@ fn invalid_success_payloads_remain_client_local_validation_failures() {
|
||||
));
|
||||
|
||||
let interface_request = FetchInterfaceRequest {
|
||||
interface: "catalog-v1".into(),
|
||||
interface: reference("/", "catalog-v1"),
|
||||
};
|
||||
let mut invalid_descriptor = descriptor();
|
||||
invalid_descriptor
|
||||
@@ -856,6 +868,7 @@ fn invalid_success_payloads_remain_client_local_validation_failures() {
|
||||
assert!(matches!(
|
||||
FetchInterfaceResponse {
|
||||
interface: interface_request.interface.clone(),
|
||||
scope_ref: None,
|
||||
descriptor: invalid_descriptor,
|
||||
validator: None,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user