feat(foundry): support restricted subject aspects

This commit is contained in:
Codex
2026-07-24 09:31:34 +03:00
parent 03aa9e3e7e
commit 49f0c449c1
13 changed files with 313 additions and 15 deletions
+9
View File
@@ -80,6 +80,7 @@ function safeTarget(target) {
delivery: "snapshot+patch",
semanticTypes: [...(binding.semanticTypes || [])],
fieldProjection: [...(binding.fieldProjection || [])],
dataClass: binding.dataClass === "restricted" ? "restricted" : "operational",
};
}
@@ -271,11 +272,16 @@ function validatePolicy(policy, product) {
throw consumerError("data_product_consumer_policy_invalid", 500);
}
const consumerContract = validateConsumerContract(policy.consumerContract);
const dataClass = policy.dataClass ?? "operational";
if (!["operational", "restricted"].includes(dataClass)) {
throw consumerError("data_product_consumer_policy_invalid", 500);
}
return {
id: String(policy.id || ""),
version: String(policy.version || ""),
dataProductId: product.id,
productVersion: product.version,
dataClass,
freshness,
staleAfterMs: policy.staleAfterMs,
terminalStatuses: [...terminalStatuses],
@@ -450,6 +456,9 @@ export function createFoundryDataProductConsumerManager({
throw consumerError("data_product_consumer_semantic_scope_mismatch", 409);
}
const policy = validatePolicy(resolvePolicy(product), product);
if ((target.binding.dataClass ?? "operational") !== policy.dataClass) {
throw consumerError("data_product_consumer_data_class_mismatch", 409);
}
assertConsumerContract(policy.consumerContract, product, target.binding);
if (policy.statusContract && !target.binding.fieldProjection.includes(policy.statusContract.attribute)) {
throw consumerError("data_product_consumer_status_field_not_projected", 409);