From 3e39f94a4fda553336c6e3f8711ebb17a2660536 Mon Sep 17 00:00:00 2001 From: Andrew Tavis McAllister Date: Thu, 7 Nov 2024 01:26:40 +0100 Subject: [PATCH 01/10] Updates outputs from the backend to camelCase for easy frontend ingestion --- CONTRIBUTING.md | 2 +- backend/authentication/serializers.py | 4 ++ backend/authentication/views.py | 2 + backend/backend/settings.py | 54 ++++++++++++++----- backend/entities/factories.py | 1 + backend/entities/serializers.py | 2 +- backend/requirements-dev.in | 8 +-- backend/requirements-dev.txt | 2 + backend/requirements.in | 8 +-- backend/requirements.txt | 2 + frontend/components/EmptyState.vue | 2 +- frontend/components/card/CardConnect.vue | 14 ++--- frontend/components/card/CardDocsEntry.vue | 6 +-- .../card/CardOrgApplicationVote.vue | 4 +- .../components/card/about/CardAboutEvent.vue | 2 +- .../components/card/about/CardAboutGroup.vue | 2 +- .../card/about/CardAboutOrganization.vue | 2 +- .../get-involved/CardGetInvolvedGroup.vue | 4 +- .../CardGetInvolvedOrganization.vue | 10 ++-- .../card/search-result/CardSearchResult.vue | 50 ++++++++--------- .../components/dropdown/DropdownCreate.vue | 16 +++--- frontend/components/dropdown/DropdownInfo.vue | 12 ++--- .../dropdown/DropdownItemsLayout.vue | 4 +- .../dropdown/DropdownUserOptions.vue | 32 +++++------ frontend/components/feed/Feed.vue | 8 +-- frontend/components/feed/FeedItem.vue | 4 +- frontend/components/grid/GridDocEntries.vue | 2 +- .../grid/GridGitHubContributors.vue | 10 ++-- frontend/components/header/HeaderAppPage.vue | 6 +-- frontend/components/image/ImageEvent.vue | 6 +-- .../components/image/ImageOrganization.vue | 4 +- .../components/landing/LandingContent.vue | 30 +++++------ frontend/components/menu/MenuItemLabel.vue | 4 +- .../components/menu/MenuSubPageSelector.vue | 4 +- .../menu/mobile/MenuMobileNavBar.vue | 10 ++-- .../mobile/MenuMobileNavigationDropdown.vue | 6 +-- .../menu/mobile/MenuMobileSelector.vue | 10 ++-- frontend/components/modal/ModalSharePage.vue | 16 +++--- .../modal/edit/about/ModalEditAboutEvent.vue | 6 +-- .../modal/edit/about/ModalEditAboutGroup.vue | 6 +-- .../edit/about/ModalEditAboutOrganization.vue | 8 +-- .../components/modal/image/ModalImage.vue | 6 +-- .../components/modal/image/ModalImageBtn.vue | 6 +-- .../components/modal/qr-code/ModalQRCode.vue | 10 ++-- frontend/components/page/PageBreadcrumbs.vue | 14 ++--- frontend/components/page/PageContent.vue | 8 +-- frontend/components/page/PageDocs.vue | 4 +- .../page/preview/PagePreviewAffiliates.vue | 2 +- .../page/preview/PagePreviewDiscussion.vue | 2 +- .../page/preview/PagePreviewHome.vue | 2 +- .../page/preview/PagePreviewSettings.vue | 2 +- .../page/preview/PagePreviewTasks.vue | 2 +- .../page/preview/PagePreviewTeam.vue | 2 +- .../sidebar/left/SidebarLeftIndex.vue | 6 +-- .../left/SidebarLeftMainSectionSelectors.vue | 10 ++-- .../sidebar/left/SidebarLeftSelector.vue | 8 +-- frontend/composables/useLinkURL.ts | 8 +-- frontend/composables/useMenuEntriesState.ts | 10 ++-- frontend/pages/about/activist.vue | 2 +- frontend/pages/about/imprint.vue | 2 +- frontend/pages/about/index.vue | 6 +-- frontend/pages/about/roadmap.vue | 2 +- frontend/pages/docs/get-active.vue | 4 +- frontend/pages/docs/get-organized.vue | 4 +- frontend/pages/docs/grow-organization.vue | 4 +- frontend/pages/docs/index.vue | 6 +-- frontend/pages/events/[id]/about.vue | 6 +-- frontend/pages/events/[id]/discussion.vue | 2 +- frontend/pages/events/[id]/index.vue | 10 ++-- frontend/pages/events/[id]/resources.vue | 2 +- frontend/pages/events/[id]/settings.vue | 2 +- frontend/pages/events/[id]/tasks.vue | 2 +- frontend/pages/events/[id]/team.vue | 2 +- frontend/pages/help/contact.vue | 2 +- frontend/pages/help/faq.vue | 2 +- frontend/pages/help/index.vue | 4 +- frontend/pages/index.vue | 20 +++---- frontend/pages/legal/index.vue | 4 +- frontend/pages/legal/privacy-policy.vue | 2 +- frontend/pages/legal/trademark-policy.vue | 2 +- frontend/pages/organizations/[id]/about.vue | 6 +-- .../pages/organizations/[id]/affiliates.vue | 2 +- .../organizations/[id]/discussions/index.vue | 2 +- frontend/pages/organizations/[id]/events.vue | 2 +- frontend/pages/organizations/[id]/faq.vue | 2 +- .../organizations/[id]/groups/[id]/about.vue | 4 +- .../organizations/[id]/groups/[id]/events.vue | 2 +- .../organizations/[id]/groups/[id]/faq.vue | 2 +- .../[id]/groups/[id]/resources.vue | 2 +- .../pages/organizations/[id]/groups/index.vue | 2 +- frontend/pages/organizations/[id]/index.vue | 10 ++-- .../pages/organizations/[id]/resources.vue | 2 +- .../pages/organizations/[id]/settings.vue | 2 +- frontend/pages/organizations/[id]/tasks.vue | 2 +- frontend/pages/organizations/[id]/team.vue | 2 +- frontend/pages/organizations/create.vue | 6 +-- frontend/pages/supporters/index.vue | 4 +- frontend/pages/supporters/join.vue | 2 +- frontend/stores/event.ts | 18 +++---- frontend/stores/group.ts | 8 +-- frontend/stores/organization.ts | 42 ++++++++------- frontend/tests/page-objects/BasePage.ts | 10 ++-- frontend/types/auth/user.d.ts | 16 +++--- frontend/types/content/discussion.d.ts | 6 +-- frontend/types/content/resource.d.ts | 12 ++--- frontend/types/docs-entry.ts | 2 +- frontend/types/entities/group.d.ts | 26 ++++----- frontend/types/entities/organization.d.ts | 36 ++++++------- frontend/types/events/event.d.ts | 42 +++++++-------- frontend/types/feed/feed-item.d.ts | 2 +- frontend/types/menu/menu-entry.ts | 4 +- frontend/types/menu/menu-selector.d.ts | 4 +- frontend/types/sub-page-selector.ts | 2 +- frontend/utils/groupSubPages.ts | 8 +-- frontend/utils/navMenuItems.ts | 12 ++--- 115 files changed, 464 insertions(+), 421 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2142b0e13..43a2b65ad 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -191,7 +191,7 @@ git remote add upstream https://github.com/activist-org/activist.git 4. Start your docker images with the following: ```bash - # --build only necessary with new dependencies or backend model changes + # --build only necessary with new dependencies or backend model changes. docker compose --env-file .env.dev up --build # And to stop the containers when you're done working: diff --git a/backend/authentication/serializers.py b/backend/authentication/serializers.py index 8c0b45f56..8b6930479 100644 --- a/backend/authentication/serializers.py +++ b/backend/authentication/serializers.py @@ -106,6 +106,10 @@ class Meta: # MARK: Methods +class DeleteUserResponseSerializer(serializers.Serializer): + message = serializers.CharField(max_length=200) + + class SignupSerializer(serializers.ModelSerializer[UserModel]): password_confirmed = serializers.CharField(write_only=True) diff --git a/backend/authentication/views.py b/backend/authentication/views.py index 32969b555..a0dbee6cc 100644 --- a/backend/authentication/views.py +++ b/backend/authentication/views.py @@ -26,6 +26,7 @@ UserTopic, ) from .serializers import ( + DeleteUserResponseSerializer, LoginSerializer, PasswordResetSerializer, SignupSerializer, @@ -239,6 +240,7 @@ def post(self, request: Request) -> Response: class DeleteUserView(APIView): queryset = UserModel.objects.all() permission_classes = (IsAuthenticated,) + serializer_class = DeleteUserResponseSerializer def delete(self, request: Request, pk: UUID | str) -> Response: user = UserModel.objects.get(pk=pk) diff --git a/backend/backend/settings.py b/backend/backend/settings.py index 94c45627a..90da29251 100644 --- a/backend/backend/settings.py +++ b/backend/backend/settings.py @@ -19,6 +19,7 @@ django_stubs_ext.monkeypatch(extra_classes=(viewsets.ModelViewSet,)) dotenv.load_dotenv() +# MARK: DB DATABASE_HOST = os.getenv("DATABASE_HOST") DATABASE_NAME = os.getenv("DATABASE_NAME") @@ -45,26 +46,29 @@ "DJANGO_CSRF_TRUSTED_ORIGINS", "http://localhost" ).split(" ") -# Application definition +# MARK: Apps INSTALLED_APPS = [ - "rest_framework", - "rest_framework.authtoken", + "corsheaders", "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", + "djangorestframework_camel_case", + "drf_spectacular", + "rest_framework", + "rest_framework.authtoken", "backend", "authentication", "entities", "content", "events", - "drf_spectacular", - "corsheaders", ] +# MARK: Middleware + MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", @@ -75,14 +79,19 @@ "django.middleware.clickjacking.XFrameOptionsMiddleware", "corsheaders.middleware.CorsMiddleware", "django.middleware.common.CommonMiddleware", + "djangorestframework_camel_case.middleware.CamelCaseMiddleWare", ] +# MARK: URLs + CORS_ALLOWED_ORIGINS = [ "http://localhost:3000", ] ROOT_URLCONF = "backend.urls" +# MARK: Templates + TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", @@ -100,7 +109,7 @@ ] -# Database +# MARK: Database # https://docs.djangoproject.com/en/4.2/ref/settings/#databases DATABASES = { @@ -115,7 +124,7 @@ } -# Password validation +# MARK: Pass Validation # https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ @@ -135,7 +144,7 @@ AUTH_USER_MODEL = "authentication.UserModel" -# Internationalization +# MARK: I18n # https://docs.djangoproject.com/en/4.2/topics/i18n/ LANGUAGE_CODE = "en-us" @@ -147,18 +156,18 @@ USE_TZ = True -# Static files (CSS, JavaScript, Images) +# MARK: Static Files # https://docs.djangoproject.com/en/4.2/howto/static-files/ STATIC_ROOT = BASE_DIR / "static/" STATIC_URL = "static/" -# Default primary key field type +# MARK: Primary Key # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" -# Email Settings +# MARK: Email # https://docs.djangoproject.com/en/5.0/topics/email/ EMAIL_HOST = os.getenv("EMAIL_HOST") @@ -169,6 +178,8 @@ # DEVELOPMENT ONLY EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" +# MARK: REST Framework + REST_FRAMEWORK = { "DEFAULT_THROTTLE_CLASSES": [ "rest_framework.throttling.AnonRateThrottle", @@ -183,19 +194,36 @@ "rest_framework.authentication.TokenAuthentication", ], "EXCEPTION_HANDLER": "backend.exception_handler.bad_request_logger", + "DEFAULT_RENDERER_CLASSES": ( + "djangorestframework_camel_case.render.CamelCaseJSONRenderer", + "djangorestframework_camel_case.render.CamelCaseBrowsableAPIRenderer", + ), + "DEFAULT_PARSER_CLASSES": ( + "djangorestframework_camel_case.parser.CamelCaseFormParser", + "djangorestframework_camel_case.parser.CamelCaseMultiPartParser", + "djangorestframework_camel_case.parser.CamelCaseJSONParser", + ), } +# MARK: Spectacular + SPECTACULAR_SETTINGS = { "TITLE": "activist.org API", - "DESCRIPTION": "Open-source, nonprofit activism platform", + "DESCRIPTION": "An open-source activism platform", "VERSION": "0.1.0", "SERVE_INCLUDE_SCHEMA": False, + "CAMELIZE_NAMES": False, + "POSTPROCESSING_HOOKS": [ + "drf_spectacular.hooks.postprocess_schema_enums", + "drf_spectacular.contrib.djangorestframework_camel_case.camelize_serializer_fields", + ], + "SWAGGER_UI_FAVICON_HREF": "https://github.com/activist-org/activist/blob/main/backend/static/swagger_favicon.png?raw=true", } # Workaround #471 / monkeypatch() is overriding the REST_FRAMEWORK dict. api_settings.reload() -# Logging Configuration +# MARK: Logging # https://docs.djangoproject.com/en/4.2/topics/logging/ LOGGING = { diff --git a/backend/entities/factories.py b/backend/entities/factories.py index 418645f72..9aa15fdbd 100644 --- a/backend/entities/factories.py +++ b/backend/entities/factories.py @@ -33,6 +33,7 @@ class Meta: name = factory.Faker("word") tagline = factory.Faker("word") social_links = ["https://www.instagram.com/activist_org/"] + get_involved_url = "https://activist.org/" created_by = factory.SubFactory("authentication.factories.UserFactory") terms_checked = factory.Faker("boolean") status = factory.SubFactory("entities.factories.StatusTypeFactory", name="Active") diff --git a/backend/entities/serializers.py b/backend/entities/serializers.py index bff553d89..9d482e56c 100644 --- a/backend/entities/serializers.py +++ b/backend/entities/serializers.py @@ -40,7 +40,7 @@ class Meta: class OrganizationTextSerializer(serializers.ModelSerializer[OrganizationText]): - orgID = serializers.StringRelatedField(source="org_id.id") + org_id = serializers.StringRelatedField(source="org_id.id") class Meta: model = OrganizationText diff --git a/backend/requirements-dev.in b/backend/requirements-dev.in index ded40e4a2..eae79888f 100644 --- a/backend/requirements-dev.in +++ b/backend/requirements-dev.in @@ -1,13 +1,13 @@ # In order to add, delete or modify a dependency, please update -# the reference here and then run: +# the reference here and then the following in ./backend: # -# - `pip-compile --rebuild requirements.in` -# - `pip-compile --rebuild requirements-dev.in` +# - pip-compile --rebuild requirements.in +# - pip-compile --rebuild requirements-dev.in # # Make sure we use production deps for constraining installed dev packages. This # is important as otherwise we could be running tests with different versions # than production. -# + -r requirements.txt factory-boy diff --git a/backend/requirements-dev.txt b/backend/requirements-dev.txt index f4b84e324..b6d74f671 100644 --- a/backend/requirements-dev.txt +++ b/backend/requirements-dev.txt @@ -53,6 +53,8 @@ djangorestframework==3.15.2 # via # -r requirements.txt # drf-spectacular +djangorestframework-camel-case==1.4.2 + # via -r requirements.txt djangorestframework-stubs==3.14.4 # via -r requirements.txt drf-spectacular==0.26.5 diff --git a/backend/requirements.in b/backend/requirements.in index 21db9d92b..b9e0e8e27 100644 --- a/backend/requirements.in +++ b/backend/requirements.in @@ -1,15 +1,15 @@ # In order to add, delete or modify a dependency, please update -# the reference here and then run: -# -# - `pip-compile --rebuild requirements.in` -# - `pip-compile --rebuild requirements-dev.in` +# the reference here and then run the following in ./backend: # +# - pip-compile --rebuild requirements.in +# - pip-compile --rebuild requirements-dev.in Django django-cors-headers django-stubs django-stubs-ext djangorestframework +djangorestframework-camel-case djangorestframework-stubs drf-spectacular gunicorn diff --git a/backend/requirements.txt b/backend/requirements.txt index d156f5ed7..8f85ff399 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -36,6 +36,8 @@ djangorestframework==3.15.2 # via # -r requirements.in # drf-spectacular +djangorestframework-camel-case==1.4.2 + # via -r requirements.in djangorestframework-stubs==3.14.4 # via -r requirements.in drf-spectacular==0.26.5 diff --git a/frontend/components/EmptyState.vue b/frontend/components/EmptyState.vue index f975a8e53..bf54adc44 100644 --- a/frontend/components/EmptyState.vue +++ b/frontend/components/EmptyState.vue @@ -3,7 +3,7 @@ class="flex w-full flex-col items-center bg-light-layer-0 text-light-text dark:bg-dark-layer-0 dark:text-dark-text" >
diff --git a/frontend/components/card/CardConnect.vue b/frontend/components/card/CardConnect.vue index 89649d9dc..35f886d0b 100644 --- a/frontend/components/card/CardConnect.vue +++ b/frontend/components/card/CardConnect.vue @@ -120,11 +120,11 @@ const props = defineProps<{ }>(); const { userIsSignedIn } = useUser(); -const paramsID = useRoute().params.id; -const paramsIDGroup = useRoute().params.groupID; +const paramsId = useRoute().params.id; +const paramsIdGroup = useRoute().params.groupId; -const id = typeof paramsID === "string" ? paramsID : undefined; -const idGroup = typeof paramsIDGroup === "string" ? paramsIDGroup : undefined; +const id = typeof paramsId === "string" ? paramsId : undefined; +const idGroup = typeof paramsIdGroup === "string" ? paramsIdGroup : undefined; const organizationStore = useOrganizationStore(); const groupStore = useGroupStore(); @@ -135,13 +135,13 @@ let group: Group; let event: Event; if (props.pageType == "organization") { - await organizationStore.fetchByID(id); + await organizationStore.fetchById(id); organization = organizationStore.organization; } else if (props.pageType == "group") { - await groupStore.fetchByID(idGroup); + await groupStore.fetchById(idGroup); group = groupStore.group; } else if (props.pageType == "event") { - await eventStore.fetchByID(id); + await eventStore.fetchById(id); event = eventStore.event; } diff --git a/frontend/components/card/CardDocsEntry.vue b/frontend/components/card/CardDocsEntry.vue index 45e4b581c..c54f483ee 100644 --- a/frontend/components/card/CardDocsEntry.vue +++ b/frontend/components/card/CardDocsEntry.vue @@ -10,13 +10,13 @@
@@ -34,7 +34,7 @@ const localPath = useLocalePath(); defineProps<{ url: string; - imgURL: string; + imgUrl: string; imgDimensions: string; imgAltText: string; title: string; diff --git a/frontend/components/card/CardOrgApplicationVote.vue b/frontend/components/card/CardOrgApplicationVote.vue index fcae4242d..3e447e23e 100644 --- a/frontend/components/card/CardOrgApplicationVote.vue +++ b/frontend/components/card/CardOrgApplicationVote.vue @@ -7,7 +7,7 @@ class="mr-5 fill-light-text dark:fill-dark-text" > @@ -16,7 +16,7 @@ class="rounded border border-light-section-div dark:border-dark-section-div" >
@@ -49,7 +49,7 @@

-
+

{{ organization.getInvolved }}

@@ -90,7 +90,7 @@ const idParam = useRoute().params.id; const id = typeof idParam === "string" ? idParam : undefined; const organizationStore = useOrganizationStore(); -await organizationStore.fetchByID(id); +await organizationStore.fetchById(id); const { organization } = organizationStore; diff --git a/frontend/components/card/search-result/CardSearchResult.vue b/frontend/components/card/search-result/CardSearchResult.vue index 6b35c6bad..38c34e29b 100644 --- a/frontend/components/card/search-result/CardSearchResult.vue +++ b/frontend/components/card/search-result/CardSearchResult.vue @@ -6,7 +6,7 @@
@@ -111,7 +111,7 @@

@@ -120,7 +120,7 @@ >

@@ -205,8 +205,8 @@

-->
-

@{{ organization.org_name }}

-

@{{ group.group_name }}

+

@{{ organization.orgName }}

+

@{{ group.groupName }}

diff --git a/frontend/components/image/ImageOrganization.vue b/frontend/components/image/ImageOrganization.vue index ca67b34ce..51884825f 100644 --- a/frontend/components/image/ImageOrganization.vue +++ b/frontend/components/image/ImageOrganization.vue @@ -2,7 +2,7 @@

- +
(); diff --git a/frontend/components/landing/LandingContent.vue b/frontend/components/landing/LandingContent.vue index e7e5243e0..ec8eb728c 100644 --- a/frontend/components/landing/LandingContent.vue +++ b/frontend/components/landing/LandingContent.vue @@ -10,13 +10,13 @@

@@ -47,12 +47,12 @@ @@ -65,12 +65,12 @@ @@ -81,13 +81,13 @@

@@ -141,7 +141,7 @@ class="w-full" :cta="true" :label="`${btnText1}`" - :linkTo="`${btnURL1}`" + :linkTo="`${btnUrl1}`" fontSize="xl" :ariaLabel="btnAriaLabel1 ? btnAriaLabel1 : ''" /> @@ -150,7 +150,7 @@ class="w-full" :cta="false" :label="`${btnText2}`" - :linkTo="`${btnURL2}`" + :linkTo="`${btnUrl2}`" fontSize="xl" :ariaLabel="btnAriaLabel2 ? btnAriaLabel2 : ''" /> @@ -174,15 +174,15 @@ defineProps<{ header: string; tagline: string; text: string; - imageURL?: string; + imgUrl?: string; imageAltText: string; btnText1: string; - btnURL1: string; + btnUrl1: string; btnAriaLabel1?: string; btnId1?: string; btnId2?: string; btnText2?: string; - btnURL2?: string; + btnUrl2?: string; btnAriaLabel2?: string; subText?: string; }>(); diff --git a/frontend/components/menu/MenuItemLabel.vue b/frontend/components/menu/MenuItemLabel.vue index 3d8570f23..fe32fa0aa 100644 --- a/frontend/components/menu/MenuItemLabel.vue +++ b/frontend/components/menu/MenuItemLabel.vue @@ -1,7 +1,7 @@ diff --git a/frontend/components/menu/mobile/MenuMobileNavigationDropdown.vue b/frontend/components/menu/mobile/MenuMobileNavigationDropdown.vue index 6eca14fb9..fc5f31e98 100644 --- a/frontend/components/menu/mobile/MenuMobileNavigationDropdown.vue +++ b/frontend/components/menu/mobile/MenuMobileNavigationDropdown.vue @@ -8,7 +8,7 @@ class="elem-shadow-sm focus-brand relative flex w-full items-center fill-light-layer-1 py-2 pl-5 text-left align-middle text-light-layer-1 dark:fill-dark-layer-1 dark:text-dark-layer-1" >