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
99 changes: 99 additions & 0 deletions backend/src/apps/common/index_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"""Type definitions for search index results."""

from __future__ import annotations

from typing import TypedDict


class UserSearchHit(TypedDict, total=False):
"""GitHub user search hit."""

idx_bio: str | None
idx_company: str | None
idx_followers_count: int
idx_following_count: int
idx_location: str | None
idx_login: str
idx_name: str | None
idx_public_repositories_count: int
idx_url: str


class ChapterSearchHit(TypedDict, total=False):
"""OWASP chapter search hit."""

idx_country: str
idx_key: str
idx_leaders: list[str]
idx_name: str
idx_suggested_location: str | None
idx_summary: str
idx_url: str


class CommitteeSearchHit(TypedDict, total=False):
"""OWASP committee search hit."""

idx_leaders: list[str]
idx_name: str
idx_summary: str
idx_url: str


class IssueSearchHit(TypedDict, total=False):
"""GitHub issue search hit."""

idx_project_name: str
idx_project_url: str
idx_summary: str
idx_title: str
idx_url: str


class ProjectSearchHit(TypedDict, total=False):
"""OWASP project search hit."""

idx_contributors_count: int
idx_forks_count: int
idx_key: str
idx_leaders: list[str]
idx_name: str
idx_stars_count: int
idx_summary: str
idx_updated_at: int
idx_url: str


class UserSearchResult(TypedDict):
"""Result returned by a GitHub user search."""

hits: list[UserSearchHit]
nbPages: int


class ChapterSearchResult(TypedDict):
"""Result returned by an OWASP chapter search."""

hits: list[ChapterSearchHit]
nbPages: int


class CommitteeSearchResult(TypedDict):
"""Result returned by an OWASP committee search."""

hits: list[CommitteeSearchHit]
nbPages: int


class IssueSearchResult(TypedDict):
"""Result returned by a GitHub issue search."""

hits: list[IssueSearchHit]
nbPages: int


class ProjectSearchResult(TypedDict):
"""Result returned by an OWASP project search."""

hits: list[ProjectSearchHit]
nbPages: int
11 changes: 8 additions & 3 deletions backend/src/apps/github/index/search/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,23 @@

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from apps.common.index_types import UserSearchResult

from algoliasearch_django import raw_search

from apps.github.models.user import User


def get_users(
query: str,
attributes: list | None = None,
attributes: list[str] | None = None,
limit: int = 25,
page: int = 1,
searchable_attributes: list | None = None,
) -> dict:
searchable_attributes: list[str] | None = None,
) -> UserSearchResult:
"""Return users relevant to a search query.

Args:
Expand Down
11 changes: 8 additions & 3 deletions backend/src/apps/owasp/index/search/chapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from apps.common.index_types import ChapterSearchResult

from algoliasearch_django import raw_search

from apps.owasp.models.chapter import Chapter
Expand All @@ -10,11 +15,11 @@
def get_chapters(
query: str,
*,
attributes: list | None = None,
attributes: list[str] | None = None,
limit: int = 25,
page: int = 1,
searchable_attributes: list | None = None,
) -> dict:
searchable_attributes: list[str] | None = None,
) -> ChapterSearchResult:
"""Return chapters relevant to a search query.

Args:
Expand Down
9 changes: 7 additions & 2 deletions backend/src/apps/owasp/index/search/committee.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from apps.common.index_types import CommitteeSearchResult

from algoliasearch_django import raw_search

from apps.owasp.models.committee import Committee
Expand All @@ -10,10 +15,10 @@
def get_committees(
query: str,
*,
attributes: list | None = None,
attributes: list[str] | None = None,
limit: int = 25,
page: int = 1,
) -> dict:
) -> CommitteeSearchResult:
"""Return committees relevant to a search query.

Args:
Expand Down
9 changes: 7 additions & 2 deletions backend/src/apps/owasp/index/search/issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from apps.common.index_types import IssueSearchResult

from algoliasearch_django import raw_search

from apps.github.models.issue import Issue
Expand All @@ -12,11 +17,11 @@
def get_issues(
query: str,
*,
attributes: list | None = None,
attributes: list[str] | None = None,
distinct: bool = False,
limit: int = 25,
page: int = 1,
) -> dict:
) -> IssueSearchResult:
"""Return issues relevant to a search query.

Args:
Expand Down
11 changes: 8 additions & 3 deletions backend/src/apps/owasp/index/search/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from apps.common.index_types import ProjectSearchResult

from algoliasearch_django import raw_search

from apps.owasp.models.project import Project
Expand All @@ -10,11 +15,11 @@
def get_projects(
query: str,
*,
attributes: list | None = None,
attributes: list[str] | None = None,
limit: int = 25,
page: int = 1,
searchable_attributes: list | None = None,
) -> dict:
searchable_attributes: list[str] | None = None,
) -> ProjectSearchResult:
"""Return projects relevant to a search query.

Args:
Expand Down
4 changes: 2 additions & 2 deletions backend/src/apps/slack/common/handlers/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ def get_blocks(

bio = truncate(escape(user.get("idx_bio", "") or ""), presentation.summary_truncation)

location = escape(user.get("idx_location", ""))
company = escape(user.get("idx_company", ""))
location = escape(user.get("idx_location", "") or "")
company = escape(user.get("idx_company", "") or "")
followers_count = user.get("idx_followers_count", 0)
following_count = user.get("idx_following_count", 0)
public_repositories = user.get("idx_public_repositories_count", 0)
Expand Down
26 changes: 26 additions & 0 deletions backend/tests/unit/apps/slack/common/handlers/users_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,32 @@ def test_get_blocks_with_empty_metadata_fields(self, mocker):
assert "Location:" not in user_block_text
assert "Followers:" not in user_block_text

def test_get_blocks_with_none_metadata_fields(self, mocker):
"""Test users with nullable metadata fields."""
mock_data = {
"hits": [
{
"idx_name": "User NoMeta",
"idx_login": "user_nometa",
"idx_url": "https://github.com/user_nometa",
"idx_bio": None,
"idx_location": None,
"idx_company": None,
"idx_followers_count": 0,
"idx_following_count": 0,
"idx_public_repositories_count": 0,
}
],
"nbPages": 1,
}
mocker.patch("apps.github.index.search.user.get_users", return_value=mock_data)

blocks = get_blocks()

user_block_text = blocks[1]["text"]["text"]
assert "Company:" not in user_block_text
assert "Location:" not in user_block_text

def test_get_blocks_with_no_name_uses_login(self, mocker):
"""Test users with no name field uses login instead."""
mock_data = {
Expand Down