offering-protocol 0.1.1__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/PKG-INFO +7 -4
  2. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/README.md +6 -3
  3. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/pyproject.toml +3 -3
  4. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/directory/client.py +10 -0
  5. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/directory/models.py +14 -7
  6. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/service/service.py +15 -7
  7. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/.gitignore +0 -0
  8. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/LICENSE +0 -0
  9. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/__init__.py +0 -0
  10. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/agent/__init__.py +0 -0
  11. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/agent/agent.py +0 -0
  12. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/agent/cache.py +0 -0
  13. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/agent/capabilities.py +0 -0
  14. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/agent/client.py +0 -0
  15. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/agent/details.py +0 -0
  16. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/agent/schema.py +0 -0
  17. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/__init__.py +0 -0
  18. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/models.py +0 -0
  19. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/references.py +0 -0
  20. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/action-relation.schema.json +0 -0
  21. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/action-request.schema.json +0 -0
  22. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/action.schema.json +0 -0
  23. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/attribute-schema-reference.schema.json +0 -0
  24. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/authentication-requirement.schema.json +0 -0
  25. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/capability-identifier.schema.json +0 -0
  26. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/capability-link.schema.json +0 -0
  27. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/collection-search-request.schema.json +0 -0
  28. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/collection.schema.json +0 -0
  29. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/detail-fields.schema.json +0 -0
  30. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/enrollment-protocol.schema.json +0 -0
  31. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/filter-capability-source.schema.json +0 -0
  32. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/filter-definition-page.schema.json +0 -0
  33. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/filter-definition.schema.json +0 -0
  34. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/filter-expression.schema.json +0 -0
  35. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/filter-operator.schema.json +0 -0
  36. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/filter-type.schema.json +0 -0
  37. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/filter-unit.schema.json +0 -0
  38. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/http-action-target.schema.json +0 -0
  39. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/invalid-parameter.schema.json +0 -0
  40. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/local-resource-identifier-list.schema.json +0 -0
  41. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/local-resource-identifier.schema.json +0 -0
  42. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/mcp-endpoint.schema.json +0 -0
  43. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/offering-search-request.schema.json +0 -0
  44. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/offering-search-response.schema.json +0 -0
  45. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/offering.schema.json +0 -0
  46. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/openapi-action-target.schema.json +0 -0
  47. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/operation-descriptor.schema.json +0 -0
  48. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/page-envelope.schema.json +0 -0
  49. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/page-limit.schema.json +0 -0
  50. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/payment-option.schema.json +0 -0
  51. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/payment-protocol.schema.json +0 -0
  52. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/price-preview.schema.json +0 -0
  53. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/problem-code.schema.json +0 -0
  54. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/problem-details.schema.json +0 -0
  55. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/protocol-version.schema.json +0 -0
  56. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/refinement-bucket.schema.json +0 -0
  57. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/refinement-group.schema.json +0 -0
  58. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/representation.schema.json +0 -0
  59. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/resource-identity.schema.json +0 -0
  60. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/resource-image.schema.json +0 -0
  61. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/resource-reference.schema.json +0 -0
  62. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/schema-reference.schema.json +0 -0
  63. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/search-capabilities.schema.json +0 -0
  64. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/service-branding-image.schema.json +0 -0
  65. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/service-branding.schema.json +0 -0
  66. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/service-document.schema.json +0 -0
  67. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/service-openapi.schema.json +0 -0
  68. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/service-origin.schema.json +0 -0
  69. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/service-protocols.schema.json +0 -0
  70. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/sort-capability-source.schema.json +0 -0
  71. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/sort-definition-page.schema.json +0 -0
  72. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/sort-definition.schema.json +0 -0
  73. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/sort-key.schema.json +0 -0
  74. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/top-level-document.schema.json +0 -0
  75. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/schemas/trust-protocol.schema.json +0 -0
  76. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/core/validation.py +0 -0
  77. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/directory/__init__.py +0 -0
  78. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/directory/transport.py +0 -0
  79. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/py.typed +0 -0
  80. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/service/__init__.py +0 -0
  81. {offering_protocol-0.1.1 → offering_protocol-0.2.0}/src/offering_protocol/service/static_catalog.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: offering-protocol
3
- Version: 0.1.1
3
+ Version: 0.2.0
4
4
  Summary: Official Python SDK for the Offering Discovery Protocol
5
5
  Project-URL: Documentation, https://www.offeringprotocol.org/
6
6
  Project-URL: Issues, https://github.com/offering-protocol/odp-python/issues
@@ -94,8 +94,9 @@ async def main() -> None:
94
94
  asyncio.run(main())
95
95
  ```
96
96
 
97
- Use `search_services()` when the application wants bounded automatic pagination. Use `suggest()` to
98
- discover keyword completions supported by the Directory.
97
+ Use `search_services()` when the application wants bounded automatic pagination. Search responses
98
+ provide facets for enrollment protocols, keywords, operations, payment protocols, payment options,
99
+ and trust protocols. Use `suggest()` to discover keyword completions supported by the Directory.
99
100
 
100
101
  ## Inspect and navigate a Service
101
102
 
@@ -266,7 +267,9 @@ service = (
266
267
  Every Service integration must implement `list-offerings` and `get-offering`. `StaticCatalog` is the
267
268
  small-Service implementation: it adds Collection operations when Collections are provided and uses
268
269
  integrity-protected, stateless continuations that expire after one hour. Larger Services can
269
- implement the typed `Catalog` protocol over their existing indexed catalog and search infrastructure.
270
+ subclass the typed `Catalog` protocol over their existing indexed catalog and search infrastructure.
271
+ The protocol supplies rejecting defaults for optional operations; override an optional method and
272
+ include its matching `Operation` only when the Service implements it.
270
273
 
271
274
  Service responses are validated against the bundled normative schemas before they are returned.
272
275
  The handler enforces fixed operation paths and methods, ODP media types, request and response byte
@@ -65,8 +65,9 @@ async def main() -> None:
65
65
  asyncio.run(main())
66
66
  ```
67
67
 
68
- Use `search_services()` when the application wants bounded automatic pagination. Use `suggest()` to
69
- discover keyword completions supported by the Directory.
68
+ Use `search_services()` when the application wants bounded automatic pagination. Search responses
69
+ provide facets for enrollment protocols, keywords, operations, payment protocols, payment options,
70
+ and trust protocols. Use `suggest()` to discover keyword completions supported by the Directory.
70
71
 
71
72
  ## Inspect and navigate a Service
72
73
 
@@ -237,7 +238,9 @@ service = (
237
238
  Every Service integration must implement `list-offerings` and `get-offering`. `StaticCatalog` is the
238
239
  small-Service implementation: it adds Collection operations when Collections are provided and uses
239
240
  integrity-protected, stateless continuations that expire after one hour. Larger Services can
240
- implement the typed `Catalog` protocol over their existing indexed catalog and search infrastructure.
241
+ subclass the typed `Catalog` protocol over their existing indexed catalog and search infrastructure.
242
+ The protocol supplies rejecting defaults for optional operations; override an optional method and
243
+ include its matching `Operation` only when the Service implements it.
241
244
 
242
245
  Service responses are validated against the bundled normative schemas before they are returned.
243
246
  The handler enforces fixed operation paths and methods, ODP media types, request and response byte
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "offering-protocol"
7
- version = "0.1.1"
7
+ version = "0.2.0"
8
8
  description = "Official Python SDK for the Offering Discovery Protocol"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -41,12 +41,12 @@ Repository = "https://github.com/offering-protocol/odp-python"
41
41
 
42
42
  [dependency-groups]
43
43
  dev = [
44
- "build==1.5.0",
44
+ "build==1.6.1",
45
45
  "mypy==2.3.1",
46
46
  "pytest==9.1.1",
47
47
  "pytest-asyncio==1.4.0",
48
48
  "pytest-cov==7.1.0",
49
- "ruff==0.16.4",
49
+ "ruff==0.16.7",
50
50
  "twine==7.0.0",
51
51
  "types-jsonschema==4.26.0.20260518",
52
52
  ]
@@ -178,6 +178,10 @@ def _parse_search_page(body: bytes) -> SearchPage:
178
178
  raise DirectoryError(f"invalid Directory response: {error}") from error
179
179
  if len(page.items) > 100:
180
180
  raise DirectoryError("Directory search page exceeds 100 Services")
181
+ if page.facets is not None and any(
182
+ facet.value.name.value != "tap" for facet in page.facets.trust
183
+ ):
184
+ raise DirectoryError("Directory trust facets are invalid")
181
185
  for service in page.items:
182
186
  if derive_service_origin(service.service_origin) != service.service_origin:
183
187
  raise DirectoryError("Directory Service origin is not canonical")
@@ -217,6 +221,12 @@ def _validate_search_request(request: SearchRequest) -> None:
217
221
  or any(not keyword or len(keyword) > 64 for keyword in request.filters.keywords)
218
222
  ):
219
223
  raise DirectoryError("keywords must contain at most 32 values of at most 64 characters")
224
+ if (
225
+ request.filters is not None
226
+ and "trust" in request.filters.model_fields_set
227
+ and (len(request.filters.trust) != 1 or request.filters.trust[0].name.value != "tap")
228
+ ):
229
+ raise DirectoryError("trust must contain exactly one tap descriptor")
220
230
 
221
231
 
222
232
  def _consume_response(response: HttpResponse) -> HttpResponse:
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from enum import StrEnum
6
+ from typing import Generic, TypeVar
6
7
 
7
8
  from pydantic import Field
8
9
 
@@ -13,10 +14,14 @@ from offering_protocol.core.models import (
13
14
  Operation,
14
15
  OperationDescriptor,
15
16
  PaymentOption,
17
+ PaymentProtocol,
16
18
  Protocol,
17
19
  ServiceProtocols,
20
+ TrustProtocol,
18
21
  )
19
22
 
23
+ FacetValue = TypeVar("FacetValue")
24
+
20
25
 
21
26
  class Environment(StrEnum):
22
27
  PRODUCTION = "production"
@@ -45,6 +50,7 @@ class ServiceFilters(OdpModel):
45
50
  keywords: list[str] = Field(default_factory=list)
46
51
  operations: list[OperationFilter] = Field(default_factory=list)
47
52
  payments: list[PaymentFilter] = Field(default_factory=list)
53
+ trust: list[TrustProtocol] = Field(default_factory=list)
48
54
 
49
55
 
50
56
  class SearchRequest(OdpModel):
@@ -74,17 +80,18 @@ class PaymentOptionFacetValue(OdpModel):
74
80
  option: PaymentOption
75
81
 
76
82
 
77
- class Facet(OdpModel):
83
+ class Facet(OdpModel, Generic[FacetValue]):
78
84
  count: int
79
- value: object
85
+ value: FacetValue
80
86
 
81
87
 
82
88
  class Facets(OdpModel):
83
- enrollment: list[Facet] = Field(default_factory=list)
84
- keywords: list[Facet] = Field(default_factory=list)
85
- operations: list[Facet] = Field(default_factory=list)
86
- payment_options: list[Facet] = Field(default_factory=list)
87
- payments: list[Facet] = Field(default_factory=list)
89
+ enrollment: list[Facet[EnrollmentProtocol]] = Field(default_factory=list)
90
+ keywords: list[Facet[str]] = Field(default_factory=list)
91
+ operations: list[Facet[OperationDescriptor]] = Field(default_factory=list)
92
+ payment_options: list[Facet[PaymentOptionFacetValue]] = Field(default_factory=list)
93
+ payments: list[Facet[PaymentProtocol]] = Field(default_factory=list)
94
+ trust: list[Facet[TrustProtocol]] = Field(default_factory=list)
88
95
 
89
96
 
90
97
  class SearchPage(OdpModel):
@@ -98,21 +98,29 @@ class Catalog(Protocol):
98
98
 
99
99
  async def search_offerings(
100
100
  self, query: OfferingSearchRequest, request: CatalogRequest
101
- ) -> OfferingPage[Offering]: ...
101
+ ) -> OfferingPage[Offering]:
102
+ del query, request
103
+ raise CatalogError("Offering search is not supported")
102
104
 
103
- async def list_collections(self, request: CatalogRequest) -> Page[Collection]: ...
105
+ async def list_collections(self, request: CatalogRequest) -> Page[Collection]:
106
+ del request
107
+ raise CatalogError("Collection listing is not supported")
104
108
 
105
- async def get_collection(
106
- self, identifier: str, request: CatalogRequest
107
- ) -> Collection | None: ...
109
+ async def get_collection(self, identifier: str, request: CatalogRequest) -> Collection | None:
110
+ del identifier, request
111
+ raise CatalogError("Collection retrieval is not supported")
108
112
 
109
113
  async def search_collections(
110
114
  self, query: CollectionSearchRequest, request: CatalogRequest
111
- ) -> Page[Collection]: ...
115
+ ) -> Page[Collection]:
116
+ del query, request
117
+ raise CatalogError("Collection search is not supported")
112
118
 
113
119
  async def list_collection_offerings(
114
120
  self, collection_id: str, request: CatalogRequest
115
- ) -> OfferingPage[Offering]: ...
121
+ ) -> OfferingPage[Offering]:
122
+ del collection_id, request
123
+ raise CatalogError("Collection Offering listing is not supported")
116
124
 
117
125
 
118
126
  class ServiceBuilder: