diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 937affe0..a8486602 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -31,6 +31,15 @@ test: - poetry install --no-interaction --no-ansi - ./manage.py test +lint: + image: python:3.9-slim + script: + - apt-get update && apt-get install -y gcc + - pip install poetry + - poetry install --no-interaction --no-ansi + - isort --check glitchtip + - pylint --load-plugins=pylint_django --django-settings-module=glitchtip.settings --disable=R glitchtip + build: image: docker:20 rules: diff --git a/.pylintrc b/.pylintrc index 277362f5..79e83ed9 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,3 +1,7 @@ [MASTER] disable= C0114, # missing-module-docstring + missing-class-docstring, + missing-function-docstring, + abstract-method, + unused-argument diff --git a/.vscode/settings.json b/.vscode/settings.json index 9553089d..0ef4fac2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,10 +2,12 @@ "python.formatting.provider": "black", "python.linting.pylintArgs": [ "--enable=W0614", + "--django-settings-module=glitchtip.settings", "--load-plugins", "pylint_django" ], "python.linting.enabled": true, "python.linting.pylintEnabled": true, - "editor.formatOnSave": true + "editor.formatOnSave": true, + "files.trimTrailingWhitespace": true } diff --git a/glitchtip/authentication.py b/glitchtip/authentication.py index cd5931a1..f6a883dd 100644 --- a/glitchtip/authentication.py +++ b/glitchtip/authentication.py @@ -1,6 +1,7 @@ from django.utils.translation import gettext_lazy as _ -from rest_framework.authentication import TokenAuthentication from rest_framework import exceptions +from rest_framework.authentication import TokenAuthentication + from api_tokens.models import APIToken diff --git a/glitchtip/celery.py b/glitchtip/celery.py index 1bb5b806..24b39a40 100644 --- a/glitchtip/celery.py +++ b/glitchtip/celery.py @@ -19,4 +19,5 @@ app.autodiscover_tasks() @app.task(bind=True) def debug_task(self): + # pylint: disable=consider-using-f-string print("Request: {0!r}".format(self.request)) diff --git a/glitchtip/debounced_celery_task.py b/glitchtip/debounced_celery_task.py index 15859878..173ba92d 100644 --- a/glitchtip/debounced_celery_task.py +++ b/glitchtip/debounced_celery_task.py @@ -1,5 +1,6 @@ # Credit https://gist.github.com/pardo/19d672235bbef6fa793a import functools + from django.core.cache import cache @@ -32,9 +33,7 @@ def debounced_task(key_generator): def wrapper(**kwargs): func_args = kwargs.get("args", []) func_kwargs = kwargs.get("kwargs", {}) - key = "{}.{}.{}".format( - func.__module__, func.__name__, key_generator(*func_args, **func_kwargs) - ) + key = f"{func.__module__}.{func.__name__}.{key_generator(*func_args, **func_kwargs)}" cache.add(key, 0) call_count = cache.incr(key) func_kwargs.update({"key": key, "call_count": call_count}) diff --git a/glitchtip/email.py b/glitchtip/email.py index 66652d5e..f9aedec7 100644 --- a/glitchtip/email.py +++ b/glitchtip/email.py @@ -1,9 +1,9 @@ from django.conf import settings from django.contrib.auth import get_user_model from django.core.mail import EmailMultiAlternatives +from django.template.loader import render_to_string from django.views.generic.base import ContextMixin from django.views.generic.detail import SingleObjectMixin -from django.template.loader import render_to_string User = get_user_model() diff --git a/glitchtip/exceptions.py b/glitchtip/exceptions.py index a63e9b26..f8a2b98e 100644 --- a/glitchtip/exceptions.py +++ b/glitchtip/exceptions.py @@ -6,4 +6,3 @@ class ConflictException(exceptions.APIException): status_code = status.HTTP_409_CONFLICT default_detail = _("Already present!") default_code = "already_present" - diff --git a/glitchtip/pagination.py b/glitchtip/pagination.py index 95000910..89c4c7e8 100644 --- a/glitchtip/pagination.py +++ b/glitchtip/pagination.py @@ -1,5 +1,6 @@ import urllib.parse as urlparse from urllib.parse import parse_qs + from rest_framework.pagination import CursorPagination from rest_framework.response import Response diff --git a/glitchtip/permissions.py b/glitchtip/permissions.py index dc94b461..e8194aab 100644 --- a/glitchtip/permissions.py +++ b/glitchtip/permissions.py @@ -8,6 +8,8 @@ class ScopedPermission(BasePermission): Fall back to checking for user authentication """ + scope_map = {} + def get_allowed_scopes(self, request, view): try: return self.scope_map[request.method] @@ -22,7 +24,7 @@ class ScopedPermission(BasePermission): return bool(request.user and request.user.is_authenticated) def get_user_scopes(self, obj, user): - pass + return set() def has_object_permission(self, request, view, obj): allowed_scopes = self.get_allowed_scopes(request, view) diff --git a/glitchtip/serializers.py b/glitchtip/serializers.py index a449a678..466169fd 100644 --- a/glitchtip/serializers.py +++ b/glitchtip/serializers.py @@ -1,4 +1,5 @@ from datetime import datetime + from django.utils.timezone import make_aware from rest_framework import serializers diff --git a/glitchtip/settings.py b/glitchtip/settings.py index dff8e9dd..5eef008a 100644 --- a/glitchtip/settings.py +++ b/glitchtip/settings.py @@ -14,12 +14,13 @@ import logging import os import sys import warnings +from datetime import timedelta + import environ import sentry_sdk -from datetime import timedelta +from celery.schedules import crontab from django.core.exceptions import ImproperlyConfigured from sentry_sdk.integrations.django import DjangoIntegration -from celery.schedules import crontab env = environ.Env( ALLOWED_HOSTS=(list, ["*"]), @@ -46,7 +47,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # See https://docs.djangoproject.com/en/dev/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = env("SECRET_KEY") +SECRET_KEY = env.str("SECRET_KEY", "change_me") # SECURITY WARNING: don't run with debug turned on in production! DEBUG = env("DEBUG") diff --git a/glitchtip/social.py b/glitchtip/social.py index eefd7a88..4737e681 100644 --- a/glitchtip/social.py +++ b/glitchtip/social.py @@ -1,20 +1,19 @@ -from django.conf import settings -from django.contrib.auth import get_backends -from rest_framework import serializers -from rest_framework.response import Response -from dj_rest_auth.registration.views import SocialConnectView, SocialLoginView -from dj_rest_auth.registration.serializers import ( - SocialLoginSerializer as BaseSocialLoginSerializer, -) -from django_rest_mfa.helpers import has_mfa from allauth.account.adapter import DefaultAccountAdapter from allauth.account.auth_backends import AuthenticationBackend +from allauth.socialaccount.providers.github.views import GitHubOAuth2Adapter from allauth.socialaccount.providers.gitlab.views import GitLabOAuth2Adapter from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter from allauth.socialaccount.providers.microsoft.views import MicrosoftGraphOAuth2Adapter -from allauth.socialaccount.providers.github.views import GitHubOAuth2Adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Client - +from dj_rest_auth.registration.serializers import ( + SocialLoginSerializer as BaseSocialLoginSerializer, +) +from dj_rest_auth.registration.views import SocialConnectView, SocialLoginView +from django.conf import settings +from django.contrib.auth import get_backends +from django_rest_mfa.helpers import has_mfa +from rest_framework import serializers +from rest_framework.response import Response DOMAIN = settings.GLITCHTIP_URL.geturl() @@ -29,7 +28,7 @@ class MFAAccountAdapter(DefaultAccountAdapter): if not hasattr(user, "backend"): backends = get_backends() backend = None - for b in backends: + for b in backends: # pylint: disable=invalid-name if isinstance(b, AuthenticationBackend): # prefer our own backend backend = b diff --git a/glitchtip/test_utils/generators.py b/glitchtip/test_utils/generators.py index 2b5c64ca..2bf0fcee 100644 --- a/glitchtip/test_utils/generators.py +++ b/glitchtip/test_utils/generators.py @@ -1,5 +1,5 @@ from model_bakery import baker -from model_bakery.random_gen import gen_slug, gen_datetime, gen_integer, gen_related +from model_bakery.random_gen import gen_datetime, gen_integer, gen_related, gen_slug def currency_code(): diff --git a/glitchtip/test_utils/test_case.py b/glitchtip/test_utils/test_case.py index 1ad89dff..2807a835 100644 --- a/glitchtip/test_utils/test_case.py +++ b/glitchtip/test_utils/test_case.py @@ -1,5 +1,7 @@ -from rest_framework.test import APITestCase +# pylint: disable=attribute-defined-outside-init,invalid-name from model_bakery import baker +from rest_framework.test import APITestCase + from organizations_ext.models import OrganizationUserRole diff --git a/glitchtip/tests.py b/glitchtip/tests.py index 1a1cfaac..2ed7ec99 100644 --- a/glitchtip/tests.py +++ b/glitchtip/tests.py @@ -1,5 +1,5 @@ -from django.test import TestCase from django.shortcuts import reverse +from django.test import TestCase class DocsTestCase(TestCase): diff --git a/glitchtip/uptime/admin.py b/glitchtip/uptime/admin.py index c050c00d..9d2fd8e9 100644 --- a/glitchtip/uptime/admin.py +++ b/glitchtip/uptime/admin.py @@ -3,13 +3,15 @@ from django.contrib import admin from django.forms.models import BaseInlineFormSet from django.urls import reverse from django.utils import timezone + from .models import Monitor, MonitorCheck class MonitorCheckInlineFormSet(BaseInlineFormSet): def get_queryset(self): if not hasattr(self, "_queryset"): - self._queryset = super().get_queryset()[:50] + # pylint: disable=attribute-defined-outside-init + self._queryset = super().get_queryset()[:50] # noqa return self._queryset diff --git a/glitchtip/uptime/constants.py b/glitchtip/uptime/constants.py index 7a01ad56..43b0b1cf 100644 --- a/glitchtip/uptime/constants.py +++ b/glitchtip/uptime/constants.py @@ -17,4 +17,3 @@ class MonitorCheckReason(models.IntegerChoices): BODY = 3, _("Expected response not found") SSL = 4, _("SSL error") NETWORK = 5, _("Network error") - diff --git a/glitchtip/uptime/email.py b/glitchtip/uptime/email.py index 1917a37c..8b865557 100644 --- a/glitchtip/uptime/email.py +++ b/glitchtip/uptime/email.py @@ -2,10 +2,11 @@ from django.contrib.auth import get_user_model from django.db.models import Q from django.utils import timezone from django.utils.translation import gettext_lazy as _ -from users.models import ProjectAlertStatus, User -from glitchtip.email import DetailEmail -from .models import MonitorCheck +from glitchtip.email import DetailEmail +from users.models import ProjectAlertStatus, User + +from .models import MonitorCheck User = get_user_model() @@ -39,4 +40,3 @@ class MonitorEmail(DetailEmail): ) | Q(subscribe_by_default=False, userprojectalert=None), ) - diff --git a/glitchtip/uptime/migrations/0001_initial.py b/glitchtip/uptime/migrations/0001_initial.py index 982589ad..c0e2e19a 100644 --- a/glitchtip/uptime/migrations/0001_initial.py +++ b/glitchtip/uptime/migrations/0001_initial.py @@ -1,8 +1,9 @@ # Generated by Django 3.2.5 on 2021-08-04 17:27 import datetime -from django.db import migrations, models + import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): @@ -10,50 +11,128 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('organizations_ext', '0001_squashed_0009_organization_scrub_ip_addresses'), - ('environments', '0002_auto_20201229_1643'), - ('projects', '0008_alter_projectkey_created'), + ("organizations_ext", "0001_squashed_0009_organization_scrub_ip_addresses"), + ("environments", "0002_auto_20201229_1643"), + ("projects", "0008_alter_projectkey_created"), ] operations = [ migrations.CreateModel( - name='Monitor', + name="Monitor", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', models.DateTimeField(auto_now_add=True, db_index=True)), - ('monitor_type', models.CharField(choices=[('ping', 'Ping'), ('get', 'GET'), ('post', 'POST'), ('ssl', 'SSL'), ('heartbeat', 'Heartbeat')], default='ping', max_length=12)), - ('name', models.CharField(max_length=200)), - ('url', models.URLField(blank=True)), - ('expected_status', models.PositiveSmallIntegerField(default=200)), - ('expected_body', models.CharField(blank=True, max_length=2000)), - ('ip_address', models.GenericIPAddressField(blank=True, null=True)), - ('interval', models.DurationField(default=datetime.timedelta(seconds=60))), - ('environment', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='environments.environment')), - ('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organizations_ext.organization')), - ('project', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='projects.project')), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created", models.DateTimeField(auto_now_add=True, db_index=True)), + ( + "monitor_type", + models.CharField( + choices=[ + ("ping", "Ping"), + ("get", "GET"), + ("post", "POST"), + ("ssl", "SSL"), + ("heartbeat", "Heartbeat"), + ], + default="ping", + max_length=12, + ), + ), + ("name", models.CharField(max_length=200)), + ("url", models.URLField(blank=True)), + ("expected_status", models.PositiveSmallIntegerField(default=200)), + ("expected_body", models.CharField(blank=True, max_length=2000)), + ("ip_address", models.GenericIPAddressField(blank=True, null=True)), + ( + "interval", + models.DurationField(default=datetime.timedelta(seconds=60)), + ), + ( + "environment", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="environments.environment", + ), + ), + ( + "organization", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="organizations_ext.organization", + ), + ), + ( + "project", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="projects.project", + ), + ), ], - options={ - 'abstract': False, - }, + options={"abstract": False,}, ), migrations.CreateModel( - name='MonitorCheck', + name="MonitorCheck", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', models.DateTimeField(auto_now_add=True, db_index=True)), - ('is_up', models.BooleanField()), - ('start_check', models.DateTimeField(help_text='Time when the start of this check was performed')), - ('reason', models.PositiveSmallIntegerField(blank=True, choices=[(0, 'Unknown'), (1, 'Timeout'), (2, 'Wrong status code'), (3, 'Expected response not found'), (4, 'SSL Error')], default=0, null=True)), - ('response_time', models.DurationField(blank=True, null=True)), - ('data', models.JSONField(blank=True, null=True)), - ('monitor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='checks', to='uptime.monitor')), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created", models.DateTimeField(auto_now_add=True, db_index=True)), + ("is_up", models.BooleanField()), + ( + "start_check", + models.DateTimeField( + help_text="Time when the start of this check was performed" + ), + ), + ( + "reason", + models.PositiveSmallIntegerField( + blank=True, + choices=[ + (0, "Unknown"), + (1, "Timeout"), + (2, "Wrong status code"), + (3, "Expected response not found"), + (4, "SSL Error"), + ], + default=0, + null=True, + ), + ), + ("response_time", models.DurationField(blank=True, null=True)), + ("data", models.JSONField(blank=True, null=True)), + ( + "monitor", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="checks", + to="uptime.monitor", + ), + ), ], - options={ - 'ordering': ('-created',), - }, + options={"ordering": ("-created",),}, ), migrations.AddIndex( - model_name='monitorcheck', - index=models.Index(fields=['start_check', 'monitor'], name='uptime_moni_start_c_6434f7_idx'), + model_name="monitorcheck", + index=models.Index( + fields=["start_check", "monitor"], name="uptime_moni_start_c_6434f7_idx" + ), ), ] diff --git a/glitchtip/uptime/migrations/0002_auto_20210822_1617.py b/glitchtip/uptime/migrations/0002_auto_20210822_1617.py index c389551d..e58020ec 100644 --- a/glitchtip/uptime/migrations/0002_auto_20210822_1617.py +++ b/glitchtip/uptime/migrations/0002_auto_20210822_1617.py @@ -6,18 +6,35 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('uptime', '0001_initial'), + ("uptime", "0001_initial"), ] operations = [ migrations.AddField( - model_name='monitor', - name='endpoint_id', - field=models.UUIDField(blank=True, editable=False, help_text='Used for referencing heartbeat endpoint', null=True), + model_name="monitor", + name="endpoint_id", + field=models.UUIDField( + blank=True, + editable=False, + help_text="Used for referencing heartbeat endpoint", + null=True, + ), ), migrations.AlterField( - model_name='monitorcheck', - name='reason', - field=models.PositiveSmallIntegerField(blank=True, choices=[(0, 'Unknown'), (1, 'Timeout'), (2, 'Wrong status code'), (3, 'Expected response not found'), (4, 'SSL error'), (5, 'Network error')], default=0, null=True), + model_name="monitorcheck", + name="reason", + field=models.PositiveSmallIntegerField( + blank=True, + choices=[ + (0, "Unknown"), + (1, "Timeout"), + (2, "Wrong status code"), + (3, "Expected response not found"), + (4, "SSL error"), + (5, "Network error"), + ], + default=0, + null=True, + ), ), ] diff --git a/glitchtip/uptime/models.py b/glitchtip/uptime/models.py index cee087e8..350f3723 100644 --- a/glitchtip/uptime/models.py +++ b/glitchtip/uptime/models.py @@ -1,9 +1,12 @@ import uuid from datetime import timedelta + from django.db import models -from django.db.models import Subquery, OuterRef +from django.db.models import OuterRef, Subquery + from glitchtip.base_models import CreatedModel -from .constants import MonitorType, MonitorCheckReason + +from .constants import MonitorCheckReason, MonitorType class MonitorManager(models.Manager): @@ -64,6 +67,7 @@ class Monitor(CreatedModel): if self.monitor_type == MonitorType.HEARTBEAT and not self.endpoint_id: self.endpoint_id = uuid.uuid4() super().save(*args, **kwargs) + # pylint: disable=import-outside-toplevel from glitchtip.uptime.tasks import perform_checks if self.monitor_type != MonitorType.HEARTBEAT: diff --git a/glitchtip/uptime/serializers.py b/glitchtip/uptime/serializers.py index 3d9f1d97..1ed6f4a8 100644 --- a/glitchtip/uptime/serializers.py +++ b/glitchtip/uptime/serializers.py @@ -1,5 +1,6 @@ from django.utils import timezone from rest_framework import serializers + from .models import Monitor, MonitorCheck diff --git a/glitchtip/uptime/tasks.py b/glitchtip/uptime/tasks.py index 598cc3a4..1541d0a7 100644 --- a/glitchtip/uptime/tasks.py +++ b/glitchtip/uptime/tasks.py @@ -1,16 +1,17 @@ import asyncio from datetime import timedelta from typing import List -from django.db import transaction -from django.db.models import F, Q, ExpressionWrapper, DateTimeField, Subquery, OuterRef + +from celery import shared_task from django.conf import settings +from django.db import transaction +from django.db.models import DateTimeField, ExpressionWrapper, F, OuterRef, Q, Subquery from django.utils import timezone from django.utils.dateparse import parse_datetime -from celery import shared_task +from .email import MonitorEmail from .models import Monitor, MonitorCheck from .utils import fetch_all -from .email import MonitorEmail @shared_task @@ -103,4 +104,5 @@ def cleanup_old_monitor_checks(): """ Delete older checks and associated data """ days = settings.GLITCHTIP_MAX_EVENT_LIFE_DAYS qs = MonitorCheck.objects.filter(created__lt=timezone.now() - timedelta(days=days)) - qs._raw_delete(qs.db) + # pylint: disable=protected-access + qs._raw_delete(qs.db) # noqa diff --git a/glitchtip/uptime/tests/test_api.py b/glitchtip/uptime/tests/test_api.py index 09cb3060..c2108581 100644 --- a/glitchtip/uptime/tests/test_api.py +++ b/glitchtip/uptime/tests/test_api.py @@ -1,5 +1,6 @@ from django.shortcuts import reverse from model_bakery import baker + from glitchtip.test_utils.test_case import GlitchTipTestCase diff --git a/glitchtip/uptime/tests/test_tasks.py b/glitchtip/uptime/tests/test_tasks.py index 588bf642..0cc25611 100644 --- a/glitchtip/uptime/tests/test_tasks.py +++ b/glitchtip/uptime/tests/test_tasks.py @@ -1,17 +1,20 @@ from datetime import timedelta from unittest import mock + from django.conf import settings from django.utils.timezone import now -from model_bakery import baker from freezegun import freeze_time +from model_bakery import baker + from glitchtip.test_utils.test_case import GlitchTipTestCase + from ..models import MonitorCheck from ..tasks import cleanup_old_monitor_checks class TasksTestCase(GlitchTipTestCase): @mock.patch("glitchtip.uptime.tasks.perform_checks.run") - def test_cleanup_old_monitor_checks(self, mock): + def test_cleanup_old_monitor_checks(self, _): baker.make(MonitorCheck, _quantity=2) cleanup_old_monitor_checks() self.assertEqual(MonitorCheck.objects.count(), 2) diff --git a/glitchtip/uptime/tests/tests.py b/glitchtip/uptime/tests/tests.py index 9023f364..81816f7f 100644 --- a/glitchtip/uptime/tests/tests.py +++ b/glitchtip/uptime/tests/tests.py @@ -1,15 +1,18 @@ import asyncio from unittest import mock + from aioresponses import aioresponses from django.core import mail from django.urls import reverse from freezegun import freeze_time from model_bakery import baker + from glitchtip.test_utils.test_case import GlitchTipTestCase + +from ..constants import MonitorType +from ..models import Monitor, MonitorCheck from ..tasks import dispatch_checks from ..utils import fetch_all -from ..models import Monitor, MonitorCheck -from ..constants import MonitorType class UptimeTestCase(GlitchTipTestCase): diff --git a/glitchtip/uptime/urls.py b/glitchtip/uptime/urls.py index 8cd300ee..501800a6 100644 --- a/glitchtip/uptime/urls.py +++ b/glitchtip/uptime/urls.py @@ -1,6 +1,6 @@ from django.urls import path -from .views import HeartBeatCheckView +from .views import HeartBeatCheckView urlpatterns = [ path( @@ -9,4 +9,3 @@ urlpatterns = [ name="heartbeat-check", ), ] - diff --git a/glitchtip/uptime/utils.py b/glitchtip/uptime/utils.py index 5e9d230d..ffd2f125 100644 --- a/glitchtip/uptime/utils.py +++ b/glitchtip/uptime/utils.py @@ -5,8 +5,8 @@ from ssl import SSLError import aiohttp from aiohttp.client_exceptions import ClientConnectorError -from .constants import MonitorType, MonitorCheckReason +from .constants import MonitorCheckReason, MonitorType DEFAULT_TIMEOUT = 10 DEFAULT_PING_TIMEOUT = 1 diff --git a/glitchtip/uptime/views.py b/glitchtip/uptime/views.py index 8063ff0d..55181c15 100644 --- a/glitchtip/uptime/views.py +++ b/glitchtip/uptime/views.py @@ -1,6 +1,7 @@ from django.shortcuts import get_object_or_404 from rest_framework import permissions, viewsets from rest_framework.generics import CreateAPIView + from .models import Monitor from .serializers import HeartBeatCheckSerializer, MonitorSerializer from .tasks import send_monitor_notification diff --git a/glitchtip/urls.py b/glitchtip/urls.py index a7544fbf..148250b4 100644 --- a/glitchtip/urls.py +++ b/glitchtip/urls.py @@ -1,27 +1,28 @@ from django.conf import settings from django.conf.urls.static import static from django.contrib import admin -from django.urls import path, include, re_path +from django.urls import include, path, re_path from django.views.generic import TemplateView from django.views.generic.base import RedirectView +from django_rest_mfa.rest_auth_helpers.views import MFALoginView +from drf_yasg import openapi +from drf_yasg.views import get_schema_view +from organizations.backends import invitation_backend from rest_framework import permissions from rest_framework_nested import routers -from django_rest_mfa.rest_auth_helpers.views import MFALoginView -from organizations.backends import invitation_backend -from drf_yasg.views import get_schema_view -from drf_yasg import openapi + +from api_tokens.urls import router as apiTokensRouter from issues.urls import router as issuesRouter from issues.views import EventJsonView +from organizations_ext.urls import router as organizationsRouter from projects.urls import router as projectsRouter from teams.urls import router as teamsRouter -from organizations_ext.urls import router as organizationsRouter from users.urls import router as usersRouter -from api_tokens.urls import router as apiTokensRouter from users.views import SocialAccountDisconnectView -from . import social -from .yasg import CustomOpenAPISchemaGenerator -from .views import SettingsView, health, APIRootView +from . import social +from .views import APIRootView, SettingsView, health +from .yasg import CustomOpenAPISchemaGenerator router = routers.DefaultRouter() router.registry.extend(projectsRouter.registry) diff --git a/glitchtip/views.py b/glitchtip/views.py index 6a88200e..d700ed72 100644 --- a/glitchtip/views.py +++ b/glitchtip/views.py @@ -1,12 +1,13 @@ +from allauth.socialaccount.models import SocialApp from django.conf import settings from django.http import HttpResponse +from rest_framework.permissions import AllowAny from rest_framework.response import Response from rest_framework.views import APIView -from rest_framework.permissions import AllowAny -from allauth.socialaccount.models import SocialApp -from users.utils import is_user_registration_open -from users.serializers import SocialAppSerializer, UserSerializer + from api_tokens.serializers import APITokenAuthScopesSerializer +from users.serializers import SocialAppSerializer, UserSerializer +from users.utils import is_user_registration_open try: from djstripe.settings import STRIPE_PUBLIC_KEY @@ -19,7 +20,7 @@ class SettingsView(APIView): permission_classes = [AllowAny] - def get(self, request, format=None): + def get(self, request, *args, **kwargs): billing_enabled = settings.BILLING_ENABLED enable_user_registration = is_user_registration_open() stripe_public_key = None @@ -49,7 +50,7 @@ class SettingsView(APIView): class APIRootView(APIView): """ /api/0/ gives information about the server and current user """ - def get(self, request, format=None): + def get(self, request, *args, **kwargs): user_data = None auth_data = None if request.user.is_authenticated: diff --git a/glitchtip/wizard/serializers.py b/glitchtip/wizard/serializers.py index 59383185..81c312ba 100644 --- a/glitchtip/wizard/serializers.py +++ b/glitchtip/wizard/serializers.py @@ -1,4 +1,5 @@ from rest_framework import serializers + from api_tokens.serializers import APITokenSerializer from projects.serializers.serializers import ProjectWithKeysSerializer diff --git a/glitchtip/wizard/tests.py b/glitchtip/wizard/tests.py index 68a32584..6686ad80 100644 --- a/glitchtip/wizard/tests.py +++ b/glitchtip/wizard/tests.py @@ -1,7 +1,9 @@ -from django.urls import reverse from django.core.cache import cache +from django.urls import reverse + from glitchtip.test_utils.test_case import GlitchTipTestCase -from .views import SETUP_WIZARD_CACHE_KEY, SETUP_WIZARD_CACHE_EMPTY + +from .views import SETUP_WIZARD_CACHE_EMPTY, SETUP_WIZARD_CACHE_KEY class WizardTestCase(GlitchTipTestCase): diff --git a/glitchtip/wizard/urls.py b/glitchtip/wizard/urls.py index 30ee4399..332d244b 100644 --- a/glitchtip/wizard/urls.py +++ b/glitchtip/wizard/urls.py @@ -1,5 +1,6 @@ from django.urls import path, re_path -from .views import SetupWizardView, SetupWizardSetTokenView + +from .views import SetupWizardSetTokenView, SetupWizardView urlpatterns = [ path("wizard/", SetupWizardView.as_view(), name="setup-wizard"), diff --git a/glitchtip/wizard/views.py b/glitchtip/wizard/views.py index 2c904666..0dd2cab8 100644 --- a/glitchtip/wizard/views.py +++ b/glitchtip/wizard/views.py @@ -1,13 +1,16 @@ import string from collections import namedtuple + from django.core.cache import cache from django.utils.crypto import get_random_string -from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.throttling import AnonRateThrottle +from rest_framework.views import APIView + from api_tokens.models import APIToken from projects.models import Project -from .serializers import SetupWizardSerializer, SetupWizardResultSerializer + +from .serializers import SetupWizardResultSerializer, SetupWizardSerializer SETUP_WIZARD_CACHE_KEY = "setup-wizard-keys:v1:" SETUP_WIZARD_CACHE_TIMEOUT = 600 diff --git a/glitchtip/yasg.py b/glitchtip/yasg.py index 24e4f7ac..ad15b0f5 100644 --- a/glitchtip/yasg.py +++ b/glitchtip/yasg.py @@ -1,5 +1,5 @@ from django.conf import settings -from drf_yasg.generators import OpenAPISchemaGenerator, EndpointEnumerator +from drf_yasg.generators import EndpointEnumerator, OpenAPISchemaGenerator from drf_yasg.inspectors import SwaggerAutoSchema diff --git a/poetry.lock b/poetry.lock index 6ba181eb..36e095c0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1990,7 +1990,7 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "8aa85cb1a478cfd174566ba7ab0d300449757a23fd049186b9bb05391c69c561" +content-hash = "77d2b89d318bc2ee71d285daa4a8016094dd25b2c71eb0139aee0d7f4701af7b" [metadata.files] aiohttp = [ diff --git a/pyproject.toml b/pyproject.toml index 114815c0..238dd49f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,6 +57,10 @@ mypy = "~0.910" django-stubs = "^1.9.0" django-sslserver = "^0.22" aioresponses = "^0.7.2" +isort = "^5.9.3" + +[tool.isort] +profile = "black" [build-system] requires = ["poetry>=0.12"]