Skip to content
Merged
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ ODP separates Service discovery from catalog discovery. An Agent searches the ca
for candidate Services, inspects each Service's live ODP document, and then navigates or searches
that Service's Collections and Offerings.

`DirectoryClient::search` discovers indexed Services and submitted Collections. Use
`search_services` for a Service-only response or `collect_services` for bounded Service-only
aggregation. `suggest` returns mixed target names; `suggest_services` returns Service-only
keyword suggestions. See the [Directory guide](./crates/odp-directory/README.md) for result types,
the 100-result cap, and migration from the earlier method names.

## Workspace

| Goal | Crate | Guide |
Expand Down
5 changes: 5 additions & 0 deletions crates/odp-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ MiB. These are fixed SDK safety ceilings. Cross-document schema composition uses

## Search across Services

Federated discovery uses `DirectoryClient::collect_services` and remains Service-only. For mixed
discovery, use `DirectoryClient::search`, inspect each Collection result's owning Service, then
call `ServiceClient::get_collection` with its Collection ID. Collection results are not separate
Services. See the [Directory guide](../odp-directory/README.md#search-services-and-collections).

```rust,no_run
use odp_agent::{Agent, FederatedSearchRequest};
use odp_core::{OfferingSearchRequest, VERSION};
Expand Down
2 changes: 1 addition & 1 deletion crates/odp-agent/src/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ impl Agent {
let concurrency = bounded(request.concurrency, 4, 16, "concurrency")?;
let services = self
.directory
.search_services(
.collect_services(
&request.services,
IterationOptions {
max_items: maximum_services,
Expand Down
2 changes: 2 additions & 0 deletions crates/odp-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ serde.workspace = true
serde_json.workspace = true
thiserror.workspace = true
url.workspace = true
# yoke-derive 0.8.3 does not compile on Rust 1.85; constrain consumer resolution too.
yoke-derive = "=0.8.2"

[lints]
workspace = true
82 changes: 78 additions & 4 deletions crates/odp-directory/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use odp_directory::{
# async fn main() -> Result<(), Box<dyn std::error::Error>> {
let directory = DirectoryClient::new(Environment::Production)?;
let services = directory
.search_services(
.collect_services(
&SearchRequest {
filters: Some(ServiceFilters {
payments: vec![PaymentFilter {
Expand All @@ -45,17 +45,19 @@ for service in services {
}

let suggestions = directory
.suggest(&SuggestionRequest {
.suggest_services(&SuggestionRequest {
limit: 5,
prefix: "pla".to_owned(),
..Default::default()
})
.await?;
# Ok(())
# }
```

`search` returns one page. `continue_search` follows one opaque `next` reference.
`search_pages` and `search_services` perform bounded traversal for callers that want aggregation.
`search_services` returns one Service-only response. `continue_search_services` follows one opaque
`next` reference. `collect_services` performs bounded Service-only traversal, stopping at the
response or item limit without fetching another response.
Search filters cover keywords, ODP operations, enrollment protocols, payment protocols, payment
options, trust protocols, and the authentication requirements attached to operations and payments.
Search responses can also carry facets for building data-driven filters without packaging the
Expand All @@ -66,3 +68,75 @@ validated.

See the [workspace guide](../../README.md) and the
[ODP specification](https://www.offeringprotocol.org/).

## Search Services and Collections

```rust,no_run
use odp_directory::{DirectoryClient, DirectoryResult, Environment, ResourceSearchRequest};

# #[tokio::main(flavor = "current_thread")]
# async fn main() -> Result<(), Box<dyn std::error::Error>> {
let directory = DirectoryClient::new(Environment::Production)?;
let response = directory.search(&ResourceSearchRequest {
query: "weather forecast".to_owned(),
limit: 25,
..ResourceSearchRequest::default()
}).await?;
for item in response.items {
match item {
DirectoryResult::Service(item) => println!("Service: {}", item.service.name),
DirectoryResult::Collection(item) => println!("Collection: {} ({}, through {})",
item.collection.name, item.collection.id, item.service.service_origin),
DirectoryResult::Unknown { kind, .. } => println!("Unsupported result type: {kind}"),
}
}
for issue in response.issues {
eprintln!("Skipped result {}: {}", issue.index, issue.message);
}
# Ok(())
# }
```

`ResourceSearchRequest.types` can restrict results to `ResultType::Service` or
`ResultType::Collection`. `None` selects both; explicit lists must be nonempty and distinct.
Filters apply to the owning Service. An empty query is omitted, allowing browsing.

Collection identity is its owning Service origin plus its case-sensitive Collection ID.
The result's `indexed_at` describes the Collection's freshness; `service.indexed_at` describes
the parent's freshness. Both are timestamp strings. `service.service_id()` identifies the
Directory's Service record. Service results may include `available_through` platform attribution;
Collection attribution is the owning `service` itself.

Inspect the owning Service's live ODP document, then use the Agent client's `get_collection` to
retrieve current details. Directory metadata is not authority to execute an Action or send
credentials. Unknown future result types retain their full raw JSON and are not interpreted as
Services. Malformed known results become indexed `issues` without discarding valid results.
Additional fields are retained in `additional` maps.

Mixed search returns at most 100 results. `limit: 0` omits the limit, using the server's default
of 100. The server does not currently offer continuation: absent `next` does not mean every match
was returned. `continue_search` accepts an opaque same-origin continuation if one is supplied.
Each call returns one response. Facets count all matching targets, not just returned items;
a Service and two Collections count as three. Collection search does not depend on permission
to display its card on the Directory landing page.

`suggest` sends POST `/v1/directory/suggestions`. `SuggestionRequest.filters` accepts the same
`ServiceFilters` as search, including AEP, keywords, ODP operations, payments and trust.
Collection filters apply to their owning Service. Matching spans names, descriptions and keywords,
but output contains deduplicated **names of matching Services and Collections**. Despite the
argument name `prefix`, matching uses substrings and whitespace-separated alternative terms.
`suggest_services` uses GET `/v1/services/suggestions` for Service-only keyword-prefix suggestions
and does not accept filters.
Both return strings from the server's `items` array; the default and maximum limit are 25.

See the [canonical Directory example](../../examples/README.md#canonical-directory-discovery).

## Migration

- Service-only `search` calls become `search_services`; `continue_search` calls become
`continue_search_services`.
- Aggregating `search_services(request, options)` calls become `collect_services(request, options)`.
- `search_pages` is removed. To retain individual responses, call `search_services` followed by
`continue_search_services` with an explicit application limit.
- Service-only `suggest` calls become `suggest_services`.
- `search`, `continue_search`, and `suggest` select mixed discovery.
Loading
Loading