Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 39 additions & 19 deletions preprocess_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,12 @@ def _create_single_variant(
variant, op, file_path, global_variant_requirements
)

# Rewrite all external $refs in the variant schema to point to their
# corresponding request variants where applicable. This covers top-level
# oneOf/anyOf/allOf branches as well as array items.
rewrite_refs_to_variants(
variant, op, file_path, global_variant_requirements
)
return variant


Expand Down Expand Up @@ -570,19 +576,28 @@ def normalize_metadata_schemas(schemas, target_dir):


def extract_external_refs(schema, path):
"""Finds all relative external file references in the schema properties."""
"""Finds all relative external file references in the schema."""
refs = []
props = schema.get("properties", {})
if not isinstance(props, dict):
return refs

for name, data in props.items():
def _scan(name, data):
for node in iter_nodes(data):
if isinstance(node, dict) and "$ref" in node:
ref = node["$ref"]
if "#" not in ref:
abs_path = str((path.parent / ref).resolve())
refs.append((name, abs_path))

props = schema.get("properties", {})
if isinstance(props, dict):
for name, data in props.items():
_scan(name, data)

# Also scan top-level composition keywords (oneOf, anyOf, allOf, items)
for key in ["oneOf", "anyOf", "allOf"]:
if key in schema:
_scan(key, schema[key])
if "items" in schema:
_scan("items", schema["items"])
return refs


Expand All @@ -599,23 +614,28 @@ def propagate_needs_transitive(variant_needs, schema_refs, schemas):
continue

for op in list(variant_needs[path]):
for prop_name, child_path in refs:
for ref_name, child_path in refs:
if child_path not in schemas:
continue

# Only propagate if the property isn't 'omit'ted for this op
data = (
schemas[path].get("properties", {}).get(prop_name, {})
)
include, _ = eval_prop_inclusion(
prop_name, data, op, schemas[path].get("required", [])
)

if include:
target_set = variant_needs.setdefault(child_path, set())
if op not in target_set:
target_set.add(op)
changed = True
# For property refs, check if the property is included for this op.
# For non-property refs (oneOf, anyOf, allOf, items), always propagate.
props = schemas[path].get("properties", {})
if ref_name in props:
data = props[ref_name]
include, _ = eval_prop_inclusion(
ref_name,
data,
op,
schemas[path].get("required", []),
)
if not include:
continue

target_set = variant_needs.setdefault(child_path, set())
if op not in target_set:
target_set.add(op)
changed = True


# --- Main Flow ---
Expand Down
4 changes: 2 additions & 2 deletions src/ucp_sdk/models/schemas/shopping/cart_create_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

from pydantic import BaseModel, ConfigDict

from .checkout import Checkout as Checkout_1
from .checkout_create_request import CheckoutCreateRequest
from .types import (
attribution_create_request,
buyer_create_request,
Expand Down Expand Up @@ -56,7 +56,7 @@ class CartCreateRequest(BaseModel):
"""


class Checkout(Checkout_1):
class Checkout(CheckoutCreateRequest):
"""
Checkout extended with cart capability. Adds cart_id to create_checkout for cart-to-checkout conversion.
"""
Expand Down
4 changes: 2 additions & 2 deletions src/ucp_sdk/models/schemas/shopping/cart_update_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

from pydantic import BaseModel, ConfigDict

from .checkout import Checkout as Checkout_1
from .checkout_update_request import CheckoutUpdateRequest
from .types import (
attribution_update_request,
buyer_update_request,
Expand Down Expand Up @@ -60,7 +60,7 @@ class CartUpdateRequest(BaseModel):
"""


class Checkout(Checkout_1):
class Checkout(CheckoutUpdateRequest):
"""
Checkout extended with cart capability. Adds cart_id to create_checkout for cart-to-checkout conversion.
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Copyright 2026 UCP Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# generated by datamodel-codegen
# pylint: disable=all
# pyformat: disable

from __future__ import annotations

from typing import Annotated

from pydantic import Field
from typing_extensions import TypeAliasType

ErrorCodeCreateRequest = TypeAliasType(
"ErrorCodeCreateRequest",
Annotated[
str,
Field(
...,
examples=[
"not_found",
"out_of_stock",
"item_unavailable",
"address_undeliverable",
"payment_failed",
"eligibility_invalid",
"identity_required",
"insufficient_scope",
],
title="Error Code Create Request",
),
],
)
"""
Error code identifying the type of error. Standard errors are defined in specification (see examples), and have standardized semantics; freeform codes are permitted.
"""
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Copyright 2026 UCP Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# generated by datamodel-codegen
# pylint: disable=all
# pyformat: disable

from __future__ import annotations

from typing import Annotated

from pydantic import Field
from typing_extensions import TypeAliasType

ErrorCodeUpdateRequest = TypeAliasType(
"ErrorCodeUpdateRequest",
Annotated[
str,
Field(
...,
examples=[
"not_found",
"out_of_stock",
"item_unavailable",
"address_undeliverable",
"payment_failed",
"eligibility_invalid",
"identity_required",
"insufficient_scope",
],
title="Error Code Update Request",
),
],
)
"""
Error code identifying the type of error. Standard errors are defined in specification (see examples), and have standardized semantics; freeform codes are permitted.
"""
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@
from pydantic import Field
from typing_extensions import TypeAliasType

from . import retail_location, shipping_destination
from . import (
retail_location_create_request,
shipping_destination_create_request,
)

FulfillmentDestinationCreateRequest = TypeAliasType(
"FulfillmentDestinationCreateRequest",
Annotated[
shipping_destination.ShippingDestination
| retail_location.RetailLocation,
shipping_destination_create_request.ShippingDestinationCreateRequest
| retail_location_create_request.RetailLocationCreateRequest,
Field(..., title="Fulfillment Destination Create Request"),
],
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@
from pydantic import Field
from typing_extensions import TypeAliasType

from . import retail_location, shipping_destination
from . import (
retail_location_update_request,
shipping_destination_update_request,
)

FulfillmentDestinationUpdateRequest = TypeAliasType(
"FulfillmentDestinationUpdateRequest",
Annotated[
shipping_destination.ShippingDestination
| retail_location.RetailLocation,
shipping_destination_update_request.ShippingDestinationUpdateRequest
| retail_location_update_request.RetailLocationUpdateRequest,
Field(..., title="Fulfillment Destination Update Request"),
],
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright 2026 UCP Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# generated by datamodel-codegen
# pylint: disable=all
# pyformat: disable

from __future__ import annotations

from typing import Annotated

from pydantic import Field
from typing_extensions import TypeAliasType

InfoCodeCreateRequest = TypeAliasType(
"InfoCodeCreateRequest",
Annotated[
str,
Field(
...,
examples=[
"identity_optional",
"signal",
"free_shipping",
"not_found",
],
title="Info Code Create Request",
),
],
)
"""
Info code identifying the type of informational message. Standard codes are defined in capability specs (see examples), and have standardized semantics; freeform codes are permitted.
"""
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright 2026 UCP Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# generated by datamodel-codegen
# pylint: disable=all
# pyformat: disable

from __future__ import annotations

from typing import Annotated

from pydantic import Field
from typing_extensions import TypeAliasType

InfoCodeUpdateRequest = TypeAliasType(
"InfoCodeUpdateRequest",
Annotated[
str,
Field(
...,
examples=[
"identity_optional",
"signal",
"free_shipping",
"not_found",
],
title="Info Code Update Request",
),
],
)
"""
Info code identifying the type of informational message. Standard codes are defined in capability specs (see examples), and have standardized semantics; freeform codes are permitted.
"""
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,18 @@
from pydantic import Field
from typing_extensions import TypeAliasType

from . import message_error, message_info, message_warning
from . import (
message_error_create_request,
message_info_create_request,
message_warning_create_request,
)

MessageCreateRequest = TypeAliasType(
"MessageCreateRequest",
Annotated[
message_error.MessageError
| message_warning.MessageWarning
| message_info.MessageInfo,
message_error_create_request.MessageErrorCreateRequest
| message_warning_create_request.MessageWarningCreateRequest
| message_info_create_request.MessageInfoCreateRequest,
Field(..., title="Message Create Request"),
],
)
Expand Down
Loading
Loading