Skip to content
Merged
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
20 changes: 20 additions & 0 deletions backend/apps/common/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,26 @@
from humanize import intword, naturaltime


def convert_to_camel_case(text: str) -> str:
"""Convert a string to camelCase.

Args:
text (str): The input string.

Returns:
str: The converted string in camelCase.

"""
parts = text.split("_")
offset = 1 if text.startswith("_") else 0
head = parts[offset : offset + 1] or [text]

segments = [f"_{head[0]}" if offset else head[0]]
segments.extend(word.capitalize() for word in parts[offset + 1 :])

return "".join(segments)


def convert_to_snake_case(text: str) -> str:
"""Convert a string to snake_case.

Expand Down
4 changes: 2 additions & 2 deletions backend/apps/core/api/algolia.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from apps.common.index import IndexBase
from apps.common.utils import get_user_ip_address
from apps.core.utils.index import get_params_for_index
from apps.core.utils.index import deep_camelize, get_params_for_index
from apps.core.validators import validate_search_params

CACHE_PREFIX = "algolia_proxy"
Expand Down Expand Up @@ -59,7 +59,7 @@ def get_search_results(
search_result = response.results[0].to_dict()

return {
"hits": search_result.get("hits", []),
"hits": deep_camelize(search_result["hits"]),
"nbPages": search_result.get("nbPages", 0),
}

Expand Down
22 changes: 22 additions & 0 deletions backend/apps/core/utils/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from algoliasearch_django.registration import RegistrationError
from django.apps import apps

from apps.common.utils import convert_to_camel_case


def get_params_for_index(index_name: str) -> dict:
"""Return search parameters based on the index name.
Expand Down Expand Up @@ -159,3 +161,23 @@ def unregister_indexes(app_names: tuple[str, ...] = ("github", "owasp")) -> None
for model in apps.get_app_config(app_name).get_models():
with contextlib.suppress(RegistrationError):
unregister(model)


def deep_camelize(obj) -> dict | list:
"""Deep camelize.

Args:
obj: The object to camelize.

Returns:
The camelize object.

"""
if isinstance(obj, dict):
return {
convert_to_camel_case(key.removeprefix("idx_")): deep_camelize(value)
for key, value in obj.items()
}
if isinstance(obj, list):
return [deep_camelize(item) for item in obj]
return obj
2 changes: 1 addition & 1 deletion backend/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions backend/tests/apps/common/utils_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from django.conf import settings

from apps.common.utils import (
convert_to_camel_case,
convert_to_snake_case,
get_absolute_url,
get_user_ip_address,
Expand All @@ -16,6 +17,23 @@


class TestUtils:
@pytest.mark.parametrize(
("input_text", "expected_output"),
[
("simple", "simple"),
("my_variable", "myVariable"),
("long_variable_name", "longVariableName"),
("top_contributor_project", "topContributorProject"),
("_leading_underscore", "_leadingUnderscore"),
("trailing_underscore_", "trailingUnderscore"),
("multiple__underscores", "multipleUnderscores"),
("multiple__under___scores", "multipleUnderScores"),
("alreadyCamelCase", "alreadyCamelCase"),
],
)
def test_convert_to_camel_case(self, input_text, expected_output):
assert convert_to_camel_case(input_text) == expected_output

@pytest.mark.parametrize(
("text", "expected"),
[
Expand Down
14 changes: 7 additions & 7 deletions frontend/__tests__/unit/data/mockChapterData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,28 @@ export const mockChapterData = {
active_chapters_count: 0,
chapters: [
{
created_at: 1568320077,
createdAt: 1568320077,
leaders: ['Isanori Sakanashi', 'Takeshi Murai', 'Yukiharu Niwa'],
name: 'Chapter 1',
related_urls: [
relatedUrls: [
'https://join.slack.com/t/owaspnagoya/shared_invite/enQtMzM0OTkwMTM1NDQxLTI5ODQzZDViMDg4NDA2MzA4OGFmZmVmMWRjM2Y0ODBkMGM2YzVkMzU0MGU5Y2IxMTE5NmM2Yjg3Zjg3YzRhOWU',
'https://www.facebook.com/login/?next=https%3A%2F%2Fwww.facebook.com%2Fowaspnagoya',
'https://x.com/owaspnagoya',
],
top_contributors: [
topContributors: [
{
avatar_url: 'https://avatars.githubusercontent.com/u/58754211?v=4',
contributions_count: 286,
avatarUrl: 'https://avatars.githubusercontent.com/u/58754211?v=4',
contributionsCount: 286,
login: 'Isanori-Sakanashi',
name: 'Isanori Sakanashi',
},
],
summary: 'This is a summary of Chapter 1.',
updated_at: 1723121926,
updatedAt: 1723121926,
key: 'chapter_1',
url: 'https://owasp.org/www-chapter-nagoya',
objectID: '539',
is_active: true,
isActive: true,
_geoloc: {
lat: 35.1815,
lng: 136.9066,
Expand Down
12 changes: 6 additions & 6 deletions frontend/__tests__/unit/data/mockCommitteeData.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
export const mockCommitteeData = {
committees: [
{
created_at: 1596744799,
createdAt: 1596744799,
leaders: ['Edmond Momartin', 'Garth Boyd', 'Kyle Smith'],
name: 'Committee 1',
related_urls: [
relatedUrls: [
'https://app.slack.com/client/T04T40NHX/C010AF25WSZ/details/top',
'https://www.youtube.com/channel/UCE2nt-oqRjwEAPSBtCtNVSw',
],
top_contributors: [
topContributors: [
{
avatar_url: 'https://avatars.githubusercontent.com/u/20112179?v=4',
contributions_count: 165,
avatarUrl: 'https://avatars.githubusercontent.com/u/20112179?v=4',
contributionsCount: 165,
login: 'securestep9',
name: 'Sam Stepanyan',
},
],
summary: 'This is a summary of Committee 1.',
updated_at: 1727390473,
updatedAt: 1727390473,
key: 'committee_1',
url: 'https://owasp.org/www-committee-chapter',
objectID: '4',
Expand Down
12 changes: 6 additions & 6 deletions frontend/__tests__/unit/data/mockContributeData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ dayjs.extend(relativeTime)
export const mockContributeData = {
issues: [
{
comments_count: 1,
created_at: dayjs().subtract(4, 'months').unix(),
commentsCount: 1,
createdAt: dayjs().subtract(4, 'months').unix(),
hint: 'Hint',
labels: [],
objectID: '9180',
project_key: 'project-nest',
project_name: 'Owasp Nest',
repository_languages: ['Python', 'TypeScript'],
projectKey: 'project-nest',
projectName: 'Owasp Nest',
repositoryLanguages: ['Python', 'TypeScript'],
summary: 'This is a summary of Contribution 1',
title: 'Contribution 1',
updated_at: 1734727031,
updatedAt: 1734727031,
url: 'https://github.com/OWASP/Nest/issues/225',
},
],
Expand Down
12 changes: 6 additions & 6 deletions frontend/__tests__/unit/data/mockHomeData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,13 @@ export const mockAlgoliaData = {
hits: [
{
objectID: '539',
idx_name: 'OWASP Nagoya',
idx_suggested_location: 'Nagoya, Aichi Prefecture, Japan',
idx_region: 'Asia',
idx_top_contributors: [
name: 'OWASP Nagoya',
suggestedLocation: 'Nagoya, Aichi Prefecture, Japan',
region: 'Asia',
topContributors: [
{
avatar_url: 'https://avatars.githubusercontent.com/u/58754211?v=4',
contributions_count: 286,
avatarUrl: 'https://avatars.githubusercontent.com/u/58754211?v=4',
contributionsCount: 286,
login: 'isanori-sakanashi-owasp',
name: 'Isanori Sakanashi',
},
Expand Down
24 changes: 12 additions & 12 deletions frontend/__tests__/unit/data/mockOrganizationData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,34 @@ export const mockOrganizationData = {
hits: [
{
objectID: 'org1',
avatar_url: 'https://avatars.githubusercontent.com/u/1234567?v=4',
collaborators_count: 50,
avatarUrl: 'https://avatars.githubusercontent.com/u/1234567?v=4',
collaboratorsCount: 50,
company: 'Test Company',
created_at: 1596744799,
createdAt: 1596744799,
description: 'This is a test organization',
email: 'org@example.com',
followers_count: 1000,
followersCount: 1000,
location: 'San Francisco, CA',
login: 'test-org',
name: 'Test Organization',
public_repositories_count: 2000,
updated_at: 1727390473,
publicRepositoriesCount: 2000,
updatedAt: 1727390473,
url: 'https://github.com/test-org',
},
{
objectID: 'org2',
avatar_url: 'https://avatars.githubusercontent.com/u/7654321?v=4',
collaborators_count: 75,
avatarUrl: 'https://avatars.githubusercontent.com/u/7654321?v=4',
collaboratorsCount: 75,
company: 'Another Company',
created_at: 1496744799,
createdAt: 1496744799,
description: 'Another test organization',
email: 'another-org@example.com',
followers_count: 2000,
followersCount: 2000,
location: 'New York, NY',
login: 'another-org',
name: 'Another Organization',
public_repositories_count: 1500,
updated_at: 1727390473,
publicRepositoriesCount: 1500,
updatedAt: 1727390473,
url: 'https://github.com/another-org',
},
],
Expand Down
12 changes: 6 additions & 6 deletions frontend/__tests__/unit/data/mockProjectData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const mockProjectData = {
summary: 'This is a summary of Project 1.',
level: '1',
leaders: ['Leader 1'],
top_contributors: [
topContributors: [
{
avatarUrl: 'https://avatars.githubusercontent.com/avatar1.png',
contributionsCount: 10,
Expand All @@ -18,12 +18,12 @@ export const mockProjectData = {
},
],
topics: ['Topic 1'],
updated_at: '2023-10-01',
forks_count: 10,
updatedAt: '2023-10-01',
forksCount: 10,
key: 'project_1',
stars_count: 20,
contributors_count: 5,
is_active: true,
starsCount: 20,
contributorsCount: 5,
isActive: true,
},
],
}
Expand Down
12 changes: 6 additions & 6 deletions frontend/__tests__/unit/data/mockUserData.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
export const mockUserData = {
users: [
{
avatar_url: 'https://avatars.githubusercontent.com/avatar1.jpg',
avatarUrl: 'https://avatars.githubusercontent.com/avatar1.jpg',
company: 'OWASP',
key: 'user_1',
login: 'user_1',
name: 'John Doe',
followers_count: 1000,
public_repositories_count: 2000,
followersCount: 1000,
publicRepositoriesCount: 2000,
},
{
avatar_url: 'https://avatars.githubusercontent.com/avatar2.jpg',
avatarUrl: 'https://avatars.githubusercontent.com/avatar2.jpg',
company: 'Security Co',
key: 'user_2',
login: 'user_2',
name: 'Jane Smith',
followers_count: 3000,
public_repositories_count: 4000,
followersCount: 3000,
publicRepositoriesCount: 4000,
},
],
}
14 changes: 7 additions & 7 deletions frontend/src/app/about/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ import FontAwesomeIconWrapper from 'wrappers/FontAwesomeIconWrapper'
import { ErrorDisplay, handleAppError } from 'app/global-error'
import { GET_PROJECT_METADATA, GET_TOP_CONTRIBUTORS } from 'server/queries/projectQueries'
import { GET_LEADER_DATA } from 'server/queries/userQueries'
import { TopContributorsTypeGraphql } from 'types/contributor'
import { ProjectTypeGraphql } from 'types/project'
import { User } from 'types/user'
import type { Contributor } from 'types/contributor'
import type { Project } from 'types/project'
import type { User } from 'types/user'
import { aboutText, technologies } from 'utils/aboutData'
import AnchorTitle from 'components/AnchorTitle'
import AnimatedCounter from 'components/AnimatedCounter'
import LoadingSpinner from 'components/LoadingSpinner'
import Markdown from 'components/MarkdownWrapper'
import SecondaryCard from 'components/SecondaryCard'
import TopContributors from 'components/TopContributors'
import TopContributorsList from 'components/TopContributorsList'
import UserCard from 'components/UserCard'

const leaders = {
Expand All @@ -54,8 +54,8 @@ const About = () => {
}
)

const [projectMetadata, setProjectMetadata] = useState<ProjectTypeGraphql | null>(null)
const [topContributors, setTopContributors] = useState<TopContributorsTypeGraphql[]>([])
const [projectMetadata, setProjectMetadata] = useState<Project | null>(null)
const [topContributors, setTopContributors] = useState<Contributor[]>([])

useEffect(() => {
if (projectMetadataResponse?.project) {
Expand Down Expand Up @@ -122,7 +122,7 @@ const About = () => {
</SecondaryCard>

{topContributors && (
<TopContributors
<TopContributorsList
icon={faUsers}
contributors={topContributors}
maxInitialDisplay={9}
Expand Down
Loading