Skip to content

Commit 9bbe9fd

Browse files
authored
Fix namespace-is to search in namespace and its children (#1089)
1 parent 116cfd7 commit 9bbe9fd

5 files changed

Lines changed: 96 additions & 5 deletions

File tree

components/renku_data_services/namespace/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ class UnsavedGroup:
2020
name: str
2121
description: str | None = None
2222

23+
@property
24+
def path(self) -> NamespacePath:
25+
"""Return the path of this group."""
26+
return NamespacePath.from_strings(self.slug)
27+
2328

2429
@dataclass(kw_only=True)
2530
class Group(UnsavedGroup):

components/renku_data_services/search/solr_token.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ def from_str(input: str) -> SolrToken:
4848
return SolrToken(__escape_query(input))
4949

5050

51+
def prefix_search(input: str) -> SolrToken:
52+
"""Create a solr query part matching input prefixes."""
53+
return SolrToken(f"{__escape_query(input)}*")
54+
55+
5156
def from_visibility(v: Visibility) -> SolrToken:
5257
"""Create a solr query value for a visibility."""
5358
return SolrToken(v.value.lower())
@@ -103,6 +108,13 @@ def field_is_any(field: FieldName, value: Nel[SolrToken]) -> SolrToken:
103108
return field_is(field, SolrToken(f"({vs})"))
104109

105110

111+
def namespace_path_is_any(value: Nel[str]) -> SolrToken:
112+
"""Search for entities in the given namespaces or below."""
113+
path_or_below = [x for k in [[from_str(e), prefix_search(f"{e}/")] for e in value] for x in k]
114+
vs = fold_or(path_or_below)
115+
return field_is(Fields.namespace_path, SolrToken(f"({vs})"))
116+
117+
106118
def type_is(et: EntityType) -> SolrToken:
107119
"""Search for the type field."""
108120
return field_is(Fields.entity_type, from_entity_type(et))

components/renku_data_services/search/solr_user_query.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ async def visit_keyword_is(self, ft: KeywordIs) -> None:
354354

355355
async def visit_namespace_is(self, ft: NamespaceIs) -> None:
356356
"""Process the namespace-is segment."""
357-
self.__append(st.field_is_any(Fields.namespace_path, ft.values.map(st.from_str)))
357+
self.__append(st.namespace_path_is_any(ft.values))
358358

359359
async def visit_created_by_is(self, ft: CreatedByIs) -> None:
360360
"""Process the created-by segment."""

test/components/renku_data_services/search/test_db.py

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
"""Tests for the repository."""
22

3+
import uuid
34
from datetime import datetime
45

56
import pytest
67
from ulid import ULID
78

89
from renku_data_services.authz.models import Visibility
9-
from renku_data_services.base_models.core import NamespacePath, NamespaceSlug, ProjectPath, ProjectSlug
10-
from renku_data_services.data_connectors.models import CloudStorageCore, DataConnector
10+
from renku_data_services.base_models.core import (
11+
AuthenticatedAPIUser,
12+
NamespacePath,
13+
NamespaceSlug,
14+
ProjectPath,
15+
ProjectSlug,
16+
)
17+
from renku_data_services.data_api.dependencies import DependencyManager
18+
from renku_data_services.data_connectors.models import CloudStorageCore, DataConnector, UnsavedDataConnector
1119
from renku_data_services.migrations.core import run_migrations_for_app
12-
from renku_data_services.namespace.models import ProjectNamespace, UserNamespace
20+
from renku_data_services.namespace.models import ProjectNamespace, UnsavedGroup, UserNamespace
21+
from renku_data_services.project.models import UnsavedProject
1322
from renku_data_services.search.db import SearchUpdatesRepo
1423
from renku_data_services.search.models import DeleteDoc
1524
from renku_data_services.solr.entity_documents import DataConnector as DataConnectorDoc
@@ -30,6 +39,71 @@
3039
)
3140

3241

42+
@pytest.mark.asyncio
43+
async def test_dc_in_group_project(app_manager_instance: DependencyManager) -> None:
44+
run_migrations_for_app("common")
45+
46+
user_repo = app_manager_instance.kc_user_repo
47+
group_repo = app_manager_instance.group_repo
48+
proj_repo = app_manager_instance.project_repo
49+
dc_repo = app_manager_instance.data_connector_repo
50+
repo = SearchUpdatesRepo(app_manager_instance.config.db.async_session_maker)
51+
52+
user = AuthenticatedAPIUser(id=str(uuid.uuid4()), access_token="abc", first_name="Huhu")
53+
u = await user_repo.get_or_create_user(user, user.id)
54+
assert u
55+
56+
group = await group_repo.insert_group(user, UnsavedGroup(slug="grr1", name="Group Grr"))
57+
58+
proj1 = await proj_repo.insert_project(
59+
user,
60+
UnsavedProject(
61+
namespace=group.slug,
62+
name=f"proj of group {group.name}",
63+
slug="proj-group-1",
64+
visibility=Visibility.PUBLIC,
65+
created_by=user.id,
66+
),
67+
)
68+
dc_in_proj = await dc_repo.insert_namespaced_data_connector(
69+
user,
70+
UnsavedDataConnector(
71+
name="dc in group project",
72+
slug="dc2",
73+
visibility=Visibility.PUBLIC,
74+
created_by=user.id,
75+
namespace=proj1.path,
76+
storage=CloudStorageCore(
77+
storage_type="csc", configuration={}, source_path="", target_path="", readonly=True
78+
),
79+
),
80+
)
81+
dc_in_group = await dc_repo.insert_namespaced_data_connector(
82+
user,
83+
UnsavedDataConnector(
84+
name="dc in group",
85+
slug="dc1",
86+
visibility=Visibility.PUBLIC,
87+
created_by=user.id,
88+
namespace=group.path,
89+
storage=CloudStorageCore(
90+
storage_type="csc", configuration={}, source_path="", target_path="", readonly=True
91+
),
92+
),
93+
)
94+
95+
updates = await repo.select_next(10)
96+
assert len(updates) == 5
97+
e1 = next(e for e in updates if e.entity_type == "User")
98+
assert e1.entity_id == user.id
99+
e2 = next(e for e in updates if e.entity_type == "Project")
100+
assert e2.entity_id == proj1.id
101+
e3 = next(e for e in updates if e.entity_type == "Group")
102+
assert e3.entity_id == group.id
103+
e45 = {e.entity_id for e in updates if e.entity_type == "DataConnector"}
104+
assert e45 == {str(dc_in_proj.id), str(dc_in_group.id)}
105+
106+
33107
@pytest.mark.asyncio
34108
async def test_data_connector_within_project(app_manager_instance):
35109
run_migrations_for_app("common")

test/components/renku_data_services/search/test_solr_user_query.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ async def test_from_term() -> None:
8888
Fields.keywords, Nel.of(st.from_str("k1"), st.from_str("w2"))
8989
)
9090
assert await to_solr(ctx, S.namespace_is("ns12")) == st.field_is_any(
91-
Fields.namespace_path, Nel.of(st.from_str("ns12"))
91+
Fields.namespace_path, Nel.of(st.from_str("ns12"), st.prefix_search("ns12/"))
9292
)
9393
assert await to_solr(ctx, S.created_by_is("12-34")) == st.field_is_any(
9494
Fields.created_by, Nel.of(st.from_str("12-34"))

0 commit comments

Comments
 (0)