Skip to content

Conversation

@jcrichlake
Copy link
Collaborator

Summary

Changes proposed

What was added, updated, or removed in this PR.
Added a generic search endpoint to the client as well as a opportunity specific search endpoint

Context for reviewers

Testing instructions, background context, more in-depth details of the implementation, and anything else you'd like to call out or ask reviewers. Explain how the changes were verified.

I set up the opportunity search to take the opportunity search request object since that seemed like a better developer experience for users of the SDK.

You can run the example code for search_opportunity.py to verify the return value of the search

Additional information

Screenshots, GIF demos, code examples or output to help show the changes working as expected.

@github-actions github-actions bot added python Issue or PR related to Python tooling sdk Issue or PR related to our SDKs py-sdk Related to Python SDK labels Nov 21, 2025
search: OpportunitySearchRequest,
page: int | None = None,
page_size: int | None = None,
) -> OpportunitiesListResponse:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this return OpportunitiesSearchResponse?

Copy link
Collaborator

@widal001 widal001 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the general pattern here is looking pretty solid, but had some questions about:

  • Extending some of the existing Client.list_some_items() and Client.list_all_items() rather than re-implementing most of the same logic in new methods.
  • Whether we wanted to simplify the filter methods we support out of the gate, but keeping them as params on the search() method for a better DX, or explore other patterns for building complex queries.

Comment on lines +20 to +38
request = {
"filters": {
"closeDateRange": {
"operator": "between",
"value": {"max": "2025-12-31", "min": "2025-01-01"},
},
"status": {"operator": "in", "value": ["open", "forecasted"]},
"totalFundingAvailableRange": {
"operator": "between",
"value": {
"max": {"amount": "1000000", "currency": "USD"},
"min": {"amount": "10000", "currency": "USD"},
},
},
},
"pagination": {"page": 1, "pageSize": 10},
"search": "local",
"sorting": {"sortBy": "lastModifiedAt", "sortOrder": "desc"},
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm I'm torn on this.

Part of the reason we want to provide a client SDK is to abstract away some of the complexity of the raw API calls, especially formatting the search and filters correctly.

In some ways, I'd prefer to handle formatting this payload correctly within the Opportunities.search() method rather than offloading it to the SDK user. And instead provide simple optional params in the method. Something like:

Client.opportunities.search(
  search="local",
  status=[OppStatus.open, OppStatus.forecasted],
)

But I also recognize that this could get quite messy if the goal is to support all filters and sorting options.

There are some other patterns we could explore that might be a hybrid of (or alternative to) a totally flattened set of params directly on the search() method:

Something like this, with a semi-flattened OppFilters

Client.opportunities.search(
  search="local",
  filters=OppFilters(
    status=[OppStatus.open, OppStatus.forecasted],
    max_close_date="2025-03-01",
    min_close_date="2025-01-01,
  )
)

Or (future PR) create a search builder:

search = (
    Client.opportunities
        .searchBuilder(search="local")
        .addStatuses([OppStatus.open, OppStatus.forecasted])
        .addDateRange(min="2025-01-01", max="2025-03-01")
)
search.execute()

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd love to get @DavidDudas-Intuitial's thoughts on this. For this first iteration, do we want to:

  • Support a more limited set of filters, but keep them as params in the search() method for ease of use
  • Keep the pattern in this PR, expecting SDK users to pass in the full OpportunitySearchRequest to the method

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great question @widal001. My guidance would be to keep the scope of this PR narrow with a simple interface as proposed in #386. But let's add an issue to next sprint to define the full-fledged interface to support filters and sorting. Until then, this should be good enough for sdk v0.3:

Client.opportunities.search(
  search="local",
  status=[OppStatus.open, OppStatus.forecasted],
)

Tthe Opportunities.search() method should hydrate an OpportunitySearchRequest instance and then pass that as an arg to Client.search(). Then Opportunities.search() should return OpportunitiesSearchResponse (not OpportunitiesListResponse).
cc: @jcrichlake

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

py-sdk Related to Python SDK python Issue or PR related to Python tooling sdk Issue or PR related to our SDKs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Py client] Implement Client.opportunities.search()

4 participants