Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement real time suggestion #292

Merged
merged 30 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
69cb115
implement real time suggestion
Rajgupta36 Dec 28, 2024
871905f
optimization and css changes
Rajgupta36 Dec 29, 2024
1b4c443
pre_commit
Rajgupta36 Dec 29, 2024
6253fc4
added jest config
Rajgupta36 Dec 29, 2024
f4fcb53
updated jest config
Rajgupta36 Dec 29, 2024
b0a1719
Add env-file
arkid15r Dec 29, 2024
fe17939
updated test case
Rajgupta36 Dec 29, 2024
c998e0c
updated indexName for suggestions
Rajgupta36 Dec 30, 2024
bd418ad
fix lint
Rajgupta36 Dec 30, 2024
aac7e4b
Merge branch 'main' into AutocompleteSearchBar
Rajgupta36 Dec 30, 2024
1b0a696
updated
Rajgupta36 Dec 30, 2024
b007c09
fixes
Rajgupta36 Dec 31, 2024
cac9e15
Merge branch 'main' into AutocompleteSearchBar
Rajgupta36 Dec 31, 2024
ab14a08
Merge branch 'main' into AutocompleteSearchBar
Rajgupta36 Jan 2, 2025
5f6cd78
fixes
Rajgupta36 Jan 2, 2025
39b3cf7
Merge branch 'main' into AutocompleteSearchBar
Rajgupta36 Jan 3, 2025
72acf5c
update var
Rajgupta36 Jan 3, 2025
0a46784
Merge branch 'main' into AutocompleteSearchBar
Rajgupta36 Jan 7, 2025
43f0f12
added_suggestion_index
Rajgupta36 Jan 7, 2025
ffbf926
pre-commit
Rajgupta36 Jan 7, 2025
ae44f85
optimization
Rajgupta36 Jan 7, 2025
70d285c
pre-commit
Rajgupta36 Jan 7, 2025
80489d1
Merge branch 'main' into AutocompleteSearchBar
Rajgupta36 Jan 8, 2025
a9987a7
Update code
arkid15r Jan 8, 2025
cd7ae7f
Update code
arkid15r Jan 8, 2025
249d024
added test case for algolia_update_suggestion
Rajgupta36 Jan 8, 2025
d26344a
Merge branch 'main' into AutocompleteSearchBar
Rajgupta36 Jan 8, 2025
287c3a8
add index for users
Rajgupta36 Jan 8, 2025
558f606
fixes
Rajgupta36 Jan 8, 2025
f3eb782
Update frontend/__tests__/src/pages/Chapters.test.tsx
arkid15r Jan 8, 2025
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 .github/workflows/ci-cd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ jobs:
run: |
# Backend
echo "DJANGO_ALGOLIA_APPLICATION_ID=${{ secrets.DJANGO_ALGOLIA_APPLICATION_ID }}" > .env.backend
echo "DJANGO_ALGOLIA_APPLICATION_REGION=${{ secrets.DJANGO_ALGOLIA_APPLICATION_REGION }}" >> .env.backend
echo "DJANGO_ALGOLIA_WRITE_API_KEY=${{ secrets.DJANGO_ALGOLIA_WRITE_API_KEY }}" >> .env.backend
echo "DJANGO_ALLOWED_HOSTS=${{ secrets.DJANGO_ALLOWED_HOSTS }}" >> .env.backend
echo "DJANGO_AWS_ACCESS_KEY_ID=${{ secrets.DJANGO_AWS_ACCESS_KEY_ID }}" >> .env.backend
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ Follow these steps to set up the OWASP Nest application:
```plaintext
DJANGO_ALGOLIA_APPLICATION_ID=<your-algolia-application-id>
DJANGO_ALGOLIA_WRITE_API_KEY=<your-algolia-write-api-key>
DJANGO_ALGOLIA_APPLICATION_REGION=<your-algolia-application-region> // eu or us
```

- Update your `frontend/.env` file with the following keys from your Algolia app (use **search** API key for frontend):
Expand Down
1 change: 1 addition & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
DJANGO_ALGOLIA_APPLICATION_ID=None
DJANGO_ALGOLIA_APPLICATION_REGION=None
DJANGO_ALGOLIA_WRITE_API_KEY=None
DJANGO_ALLOWED_HOSTS=*
DJANGO_AWS_ACCESS_KEY_ID=None
Expand Down
1 change: 1 addition & 0 deletions backend/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ index-data:
@echo "Indexing Nest data"
@CMD="poetry run python manage.py algolia_reindex" $(MAKE) exec-backend-command
@CMD="poetry run python manage.py algolia_update_synonyms" $(MAKE) exec-backend-command
@CMD="poetry run python manage.py algolia_update_suggestions" $(MAKE) exec-backend-command

load-data:
@echo "Loading Nest data"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"""A command to update OWASP Nest suggestions index."""

from algoliasearch.query_suggestions.client import QuerySuggestionsClientSync
from django.conf import settings
from django.core.management.base import BaseCommand


class Command(BaseCommand):
help = "Create query suggestions for Algolia indices"

def handle(self, *args, **kwargs):
client = QuerySuggestionsClientSync(
settings.ALGOLIA_APPLICATION_ID,
settings.ALGOLIA_WRITE_API_KEY,
settings.ALGOLIA_APPLICATION_REGION,
)

entity_configs = {
"chapters": {
"facets": [
{"attribute": "idx_key"},
{"attribute": "idx_name"},
{"attribute": "idx_tags"},
{"attribute": "idx_country"},
{"attribute": "idx_region"},
{"attribute": "idx_suggested_location"},
],
"generate": [
["idx_name"],
["idx_tags"],
["idx_country"],
["idx_region"],
["idx_suggested_location"],
],
},
"committees": {
"facets": [
{"attribute": "idx_key"},
{"attribute": "idx_name"},
{"attribute": "idx_tags"},
],
"generate": [
["idx_name"],
["idx_tags"],
],
},
"issues": {
"facets": [
{"attribute": "idx_title"},
{"attribute": "idx_project_name"},
{"attribute": "idx_repository_name"},
{"attribute": "idx_project_tags"},
{"attribute": "idx_repository_topics"},
],
"generate": [
["idx_title"],
["idx_project_name"],
["idx_repository_name"],
["idx_project_tags"],
["idx_repository_topics"],
],
},
"projects": {
"facets": [
{"attribute": "idx_key"},
{"attribute": "idx_name"},
{"attribute": "idx_repository_names"},
{"attribute": "idx_tags"},
],
"generate": [
["idx_name"],
["idx_tags"],
["idx_repository_names"],
],
},
"users": {
"facets": [
{"attribute": "idx_key"},
{"attribute": "idx_name"},
{"attribute": "idx_title"},
],
"generate": [
["idx_name"],
["idx_title"],
],
},
}

for entity, suggestion_settings in entity_configs.items():
source_index_name = f"{settings.ENVIRONMENT.lower()}_{entity}"
suggestions_index_name = f"{settings.ENVIRONMENT.lower()}_{entity}_suggestions"

configuration = {
"sourceIndices": [
{
"indexName": source_index_name,
**suggestion_settings,
}
]
}
client.update_config(
index_name=suggestions_index_name,
configuration=configuration,
)
print(f"Updated query suggestions index for {entity.capitalize()}")
7 changes: 7 additions & 0 deletions backend/apps/github/index/issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ class IssueIndex(AlgoliaIndex, IndexBase):
)

settings = {
"attributesForFaceting": [
"idx_title",
"idx_project_name",
"idx_repository_name",
"idx_project_tags",
"idx_repository_topics",
],
"attributeForDistinct": "idx_project_name",
"minProximity": 4,
"indexLanguages": ["en"],
Expand Down
5 changes: 5 additions & 0 deletions backend/apps/github/index/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ class UserIndex(AlgoliaIndex, IndexBase):
)

settings = {
"attributesForFaceting": [
"idx_key",
"idx_name",
"idx_title",
],
"attributeForDistinct": "idx_login",
"minProximity": 4,
"customRanking": [
Expand Down
3 changes: 3 additions & 0 deletions backend/apps/owasp/index/chapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ class ChapterIndex(AlgoliaIndex):
settings = {
"attributesForFaceting": [
"filterOnly(idx_key)",
"idx_country",
"idx_name",
"idx_tags",
],
"indexLanguages": ["en"],
"customRanking": [
Expand Down
2 changes: 2 additions & 0 deletions backend/apps/owasp/index/committee.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class CommitteeIndex(AlgoliaIndex):
settings = {
"attributesForFaceting": [
"filterOnly(idx_key)",
"idx_name",
"idx_tags",
],
"indexLanguages": ["en"],
"customRanking": [
Expand Down
3 changes: 3 additions & 0 deletions backend/apps/owasp/index/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ class ProjectIndex(AlgoliaIndex, IndexBase):
settings = {
"attributesForFaceting": [
"filterOnly(idx_key)",
"idx_name",
"idx_tags",
"idx_repository_names",
],
"indexLanguages": ["en"],
"customRanking": [
Expand Down
1 change: 1 addition & 0 deletions backend/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ class Base(Configuration):
WSGI_APPLICATION = "wsgi.application"

ALGOLIA_APPLICATION_ID = values.SecretValue(environ_name="ALGOLIA_APPLICATION_ID")
ALGOLIA_APPLICATION_REGION = values.SecretValue(environ_name="ALGOLIA_APPLICATION_REGION")
ALGOLIA_WRITE_API_KEY = values.SecretValue(environ_name="ALGOLIA_WRITE_API_KEY")

ALGOLIA = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,122 @@

import pytest

from apps.common.management.commands.algolia_update_synonyms import Command


@pytest.mark.parametrize(
"indexes",
[["IssueIndex", "ProjectIndex"]],
)
class TestAlgoliaUpdateSynonyms:
@patch("builtins.print")
@patch("apps.common.management.commands.algolia_update_synonyms.ProjectIndex")
@patch("apps.common.management.commands.algolia_update_synonyms.IssueIndex")
def test_handle(self, mock_issue_index, mock_project_index, mock_print, indexes):
mock_indexes = {
"IssueIndex": mock_issue_index,
"ProjectIndex": mock_project_index,
}
for index_name, index_instance in mock_indexes.items():
index_instance.update_synonyms = MagicMock()
index_instance.index_name = index_name
from apps.common.management.commands.algolia_update_suggestions import Command


class TestUpdateSuggestionsCommand:
@pytest.mark.parametrize(
("entity", "facets", "generate"),
[
(
"chapters",
[
{"attribute": "idx_key"},
{"attribute": "idx_name"},
{"attribute": "idx_tags"},
{"attribute": "idx_country"},
{"attribute": "idx_region"},
{"attribute": "idx_suggested_location"},
],
[
["idx_name"],
["idx_tags"],
["idx_country"],
["idx_region"],
["idx_suggested_location"],
],
),
(
"committees",
[
{"attribute": "idx_key"},
{"attribute": "idx_name"},
{"attribute": "idx_tags"},
],
[
["idx_name"],
["idx_tags"],
],
),
(
"issues",
[
{"attribute": "idx_title"},
{"attribute": "idx_project_name"},
{"attribute": "idx_repository_name"},
{"attribute": "idx_project_tags"},
{"attribute": "idx_repository_topics"},
],
[
["idx_title"],
["idx_project_name"],
["idx_repository_name"],
["idx_project_tags"],
["idx_repository_topics"],
],
),
(
"projects",
[
{"attribute": "idx_key"},
{"attribute": "idx_name"},
{"attribute": "idx_repository_names"},
{"attribute": "idx_tags"},
],
[
["idx_name"],
["idx_tags"],
["idx_repository_names"],
],
),
(
"users",
[
{"attribute": "idx_key"},
{"attribute": "idx_name"},
{"attribute": "idx_title"},
],
[
["idx_name"],
["idx_title"],
],
),
],
)
@patch("apps.common.management.commands.algolia_update_suggestions.settings")
@patch("apps.common.management.commands.algolia_update_suggestions.QuerySuggestionsClientSync")
def test_handle(self, mock_query_suggestions_client, mock_settings, entity, facets, generate):
# mocks
mock_settings.ENVIRONMENT = "testenv"
mock_settings.ALGOLIA_APPLICATION_ID = "mock_app_id"
mock_settings.ALGOLIA_WRITE_API_KEY = "mock_api_key"
mock_settings.ALGOLIA_APPLICATION_REGION = "eu"

mock_client = MagicMock()
mock_query_suggestions_client.return_value = mock_client

mock_client.update_config.return_value = None

expected_call_count = 5
# Act

command = Command()
command.handle()

for index_name in indexes:
index_instance = mock_indexes[index_name]
index_instance.update_synonyms.assert_called_once()
mock_print.assert_any_call(f"Updated {index_name.capitalize()} synonyms")
mock_query_suggestions_client.assert_called_once_with("mock_app_id", "mock_api_key", "eu")

mock_client.update_config.assert_any_call(
index_name=f"testenv_{entity}_suggestions",
configuration={
"sourceIndices": [
{
"indexName": f"testenv_{entity}",
"facets": facets,
"generate": generate,
}
]
},
)

# Use constant for the expected call count
assert mock_client.update_config.call_count == expected_call_count
4 changes: 2 additions & 2 deletions frontend/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ shell-frontend:
@CMD="/bin/bash" $(MAKE) exec-frontend-command-it

test-frontend:
@docker build -f frontend/Dockerfile.test frontend -t nest-frontend-test 2>/dev/null
@docker run nest-frontend-test npm run test
@docker build -f frontend/Dockerfile.test frontend -t nest-frontend-test
@docker run --env-file frontend/.env.example nest-frontend-test npm run test
2 changes: 0 additions & 2 deletions frontend/__tests__/src/pages/Chapters.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,10 @@ describe('ChaptersPage Component', () => {
const loadingSpinner = screen.getAllByAltText('Loading indicator')
await waitFor(() => {
expect(loadingSpinner.length).toBeGreaterThan(0)
expect(screen.queryByPlaceholderText('Search for OWASP chapters...')).not.toBeInTheDocument()
arkid15r marked this conversation as resolved.
Show resolved Hide resolved
expect(screen.queryByText('Next Page')).not.toBeInTheDocument()
})
await waitFor(() => {
expect(screen.getByPlaceholderText('Search for OWASP chapters...')).toBeInTheDocument()
expect(screen.getByPlaceholderText('Search for OWASP chapters...')).toHaveFocus()
expect(screen.getByText('Chapter 1')).toBeInTheDocument()
expect(screen.getByText('Next Page')).toBeInTheDocument()
})
Expand Down
5 changes: 1 addition & 4 deletions frontend/__tests__/src/pages/Committees.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,12 @@ describe('Committees Component', () => {
const loadingSpinner = screen.getAllByAltText('Loading indicator')
await waitFor(() => {
expect(loadingSpinner.length).toBeGreaterThan(0)
expect(
screen.queryByPlaceholderText('Search for OWASP committees...')
).not.toBeInTheDocument()

expect(screen.queryByText('Next Page')).not.toBeInTheDocument()
})

await waitFor(() => {
expect(screen.getByPlaceholderText('Search for OWASP committees...')).toBeInTheDocument()
expect(screen.getByPlaceholderText('Search for OWASP committees...')).toHaveFocus()
expect(screen.getByText('Committee 1')).toBeInTheDocument()
expect(screen.getByText('Next Page')).toBeInTheDocument()
})
Expand Down
2 changes: 0 additions & 2 deletions frontend/__tests__/src/pages/Projects.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,10 @@ describe('ProjectPage Component', () => {
const loadingSpinner = screen.getAllByAltText('Loading indicator')
await waitFor(() => {
expect(loadingSpinner.length).toBeGreaterThan(0)
expect(screen.queryByPlaceholderText('Search for OWASP projects...')).not.toBeInTheDocument()
expect(screen.queryByText('Next Page')).not.toBeInTheDocument()
})
await waitFor(() => {
expect(screen.getByPlaceholderText('Search for OWASP projects...')).toBeInTheDocument()
expect(screen.getByPlaceholderText('Search for OWASP projects...')).toHaveFocus()
expect(screen.getByText('Project 1')).toBeInTheDocument()
expect(screen.getByText('Next Page')).toBeInTheDocument()
})
Expand Down
Loading
Loading