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
Original file line number Diff line number Diff line change
Expand Up @@ -2164,7 +2164,7 @@ class SubscriptionListenerAsync:

class SubscriptionsServiceSync:
def create(self, input: list[Subscription]) -> list[Subscription]: ...
def list(
def filter(
self,
form: SubscriptionFilterForm | None = None,
timeseries: list[SubscriptionTimeseriesId] | None = None,
Expand All @@ -2177,7 +2177,7 @@ class SubscriptionsServiceSync:

class SubscriptionsServiceAsync:
async def create(self, input: list[Subscription]) -> list[Subscription]: ...
async def list(
async def filter(
self,
form: SubscriptionFilterForm | None = None,
timeseries: list[SubscriptionTimeseriesId] | None = None,
Expand Down
4 changes: 2 additions & 2 deletions datahub_python_bindings/src/subscriptions/async_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl PySubscriptionsServiceAsync {
}

#[pyo3(signature=(form=None, *, timeseries=None, limit=None, sort=None))]
fn list<'py>(
fn filter<'py>(
&self,
py: Python<'py>,
form: Option<PySubscriptionFilterForm>,
Expand All @@ -55,7 +55,7 @@ impl PySubscriptionsServiceAsync {
future_into_py(py, async move {
let result = service
.subscriptions
.list(&form)
.filter(&form)
.await
.map_err(|e| crate::datahub_err(e))?;
Ok(result
Expand Down
4 changes: 2 additions & 2 deletions datahub_python_bindings/src/subscriptions/sync_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl PySubscriptionsServiceSync {
}

#[pyo3(signature=(form=None, *, timeseries=None, limit=None, sort=None))]
fn list(
fn filter(
&self,
py: Python<'_>,
form: Option<PySubscriptionFilterForm>,
Expand All @@ -51,7 +51,7 @@ impl PySubscriptionsServiceSync {
py.detach(|| {
let result = self
.runtime
.block_on(service.subscriptions.list(&form))
.block_on(service.subscriptions.filter(&form))
.map_err(|e| crate::datahub_err(e))?;
Ok(result
.get_items()
Expand Down
4 changes: 2 additions & 2 deletions python_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,10 @@ def _sweep(client) -> None:
except Exception:
pass

# Subscriptions — plain list.
# Subscriptions — unrestricted filter.
try:
_safe_delete_each(
client.subscriptions.delete, _matching_prefix(client.subscriptions.list())
client.subscriptions.delete, _matching_prefix(client.subscriptions.filter())
)
except Exception:
pass
Expand Down
22 changes: 11 additions & 11 deletions python_tests/test_subscriptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def subscription_timeseries(make_ts):
return ts_a.external_id, ts_b.external_id


def test_create_list_delete(sync_client, subscription_timeseries):
def test_create_filter_delete(sync_client, subscription_timeseries):
ts_a_ext, ts_b_ext = subscription_timeseries
sub_ext = unique_id("sub")

Expand All @@ -43,26 +43,26 @@ def test_create_list_delete(sync_client, subscription_timeseries):
assert created[0].date_created is not None
assert len(created[0].timeseries) == 2

# Unfiltered list — backend may carry prior test data, so don't assert exact count.
all_subs = sync_client.subscriptions.list()
# Unrestricted filter — backend may carry prior test data, so don't assert exact count.
all_subs = sync_client.subscriptions.filter()
assert any(s.external_id == sub_ext for s in all_subs)

# Filter by timeseries via kwargs.
filtered = sync_client.subscriptions.list(timeseries=[ts_a_ext], limit=100)
filtered = sync_client.subscriptions.filter(timeseries=[ts_a_ext], limit=100)
assert any(s.external_id == sub_ext for s in filtered)

# Same call via an explicit form.
form = intellistream_datahub_sdk.SubscriptionFilterForm(
filter=intellistream_datahub_sdk.SubscriptionFilter(timeseries=[ts_a_ext]),
limit=100,
)
filtered_via_retriever = sync_client.subscriptions.list(form)
assert any(s.external_id == sub_ext for s in filtered_via_retriever)
filtered_via_form = sync_client.subscriptions.filter(form)
assert any(s.external_id == sub_ext for s in filtered_via_form)

# Delete and verify gone.
sync_client.subscriptions.delete([sub_ext])
time.sleep(0.5)
after = sync_client.subscriptions.list(timeseries=[ts_a_ext])
after = sync_client.subscriptions.filter(timeseries=[ts_a_ext])
assert not any(s.external_id == sub_ext for s in after)
finally:
# Best-effort cleanup (delete may have already run in the happy path).
Expand All @@ -86,15 +86,15 @@ def test_create_over_missing_timeseries_raises(sync_client):
sync_client.subscriptions.create([sub])


def test_list_rejects_retriever_and_kwargs_together(sync_client):
def test_filter_rejects_form_and_kwargs_together(sync_client):
form = intellistream_datahub_sdk.SubscriptionFilterForm()
with pytest.raises(ValueError):
sync_client.subscriptions.list(form, limit=10)
sync_client.subscriptions.filter(form, limit=10)


def test_list_default_returns_list(sync_client):
def test_filter_default_returns_list(sync_client):
# Default form — caller hasn't passed anything. Should not raise; result type only.
result = sync_client.subscriptions.list()
result = sync_client.subscriptions.filter()
assert isinstance(result, list)


Expand Down
15 changes: 13 additions & 2 deletions src/subscriptions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,22 @@ impl SubscriptionsService {
.await
}

pub async fn list(
/// `POST /subscriptions/filter` — the subscriptions matching [`SubscriptionFilterForm`].
///
/// This was `POST /subscriptions/list` until the api moved subscriptions onto the same filter
/// contract the rest of the collections use. Only the path moved: `filter`, `limit` and `sort`
/// are read exactly as before, and this type is a subset of the retriever the endpoint accepts.
/// The old path is gone rather than deprecated, so a client that has not moved gets a 404.
///
/// The method is named for the endpoint, the way every other `filter` in this SDK is, and the
/// Java client spells the same call `filter` too. That leaves `list` free on purpose: the api
/// gained a criteria-free `GET /subscriptions?limit=` at the same time, which is what `list`
/// means on every other collection here, and no client wraps it yet.
pub async fn filter(
&self,
form: &SubscriptionFilterForm,
) -> Result<DataWrapper<Subscription>, ResponseError> {
let path = &format!("{}/list", self.base_url);
let path = &format!("{}/filter", self.base_url);
self.execute_post_request::<DataWrapper<Subscription>, _>(path, form)
.await
}
Expand Down
16 changes: 8 additions & 8 deletions src/subscriptions/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,23 +135,23 @@ mod tests {
assert!(created_item.date_created.is_some());
assert_eq!(created_item.timeseries.len(), 2);

// 3. List — the unfiltered list may include prior test data, so we assert *at least*
// our subscription is present (per AGENTS.md: avoid exact-count assertions against
// shared backend state).
// 3. Filter with the default (unrestricted) form — the result may include prior test
// data, so we assert *at least* our subscription is present (per AGENTS.md: avoid
// exact-count assertions against shared backend state).
let all = api_service
.subscriptions
.list(&SubscriptionFilterForm::default())
.filter(&SubscriptionFilterForm::default())
.await?;
assert!(
all.get_items().iter().any(|s| s.external_id == sub_ext),
"unfiltered list must contain the subscription we just created"
);

// 4. List with a timeseries filter — only subscriptions bound to ts_a should come back.
// 4. Filter by timeseries — only subscriptions bound to ts_a should come back.
// Our subscription is bound to ts_a, so it must appear.
let filtered = api_service
.subscriptions
.list(&SubscriptionFilterForm {
.filter(&SubscriptionFilterForm {
filter: SubscriptionFilter {
timeseries: vec![IdAndExtId::from_external_id(&ts_a_ext)],
},
Expand All @@ -169,10 +169,10 @@ mod tests {
// Explicit delete succeeded — disarm the guard so it doesn't re-delete.
sub_cleanup.disarm();

// 6. Verify the subscription is gone from the filtered list.
// 6. Verify the subscription is gone from the filtered result.
let after_delete = api_service
.subscriptions
.list(&SubscriptionFilterForm {
.filter(&SubscriptionFilterForm {
filter: SubscriptionFilter {
timeseries: vec![IdAndExtId::from_external_id(&ts_a_ext)],
},
Expand Down
Loading