• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DemocracyClub / aggregator-api / 93f28176-d12b-44aa-8582-a03ba3be77a2

05 Dec 2023 03:31PM UTC coverage: 77.419% (-0.8%) from 78.236%
93f28176-d12b-44aa-8582-a03ba3be77a2

push

circleci

web-flow
Merge pull request #441 from DemocracyClub/self-serve-api-keys

Self-serve API keys

315 of 422 new or added lines in 17 files covered. (74.64%)

1 existing line in 1 file now uncovered.

864 of 1116 relevant lines covered (77.42%)

0.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.73
/frontend/apps/api_users/forms.py
1
from common.settings import API_PLANS
1✔
2
from django import forms
1✔
3
from django.contrib.auth import get_user_model
1✔
4
from django.core.exceptions import ValidationError
1✔
5

6
from api_users.models import APIKey, CustomUser
1✔
7

8
User = get_user_model()
1✔
9

10
__all__ = ["LoginForm"]
1✔
11

12

13
class LoginForm(forms.Form):
1✔
14
    """
15
    Login form for a User.
16
    """
17

18
    email = forms.EmailField(required=True)
1✔
19

20
    def clean_email(self):
1✔
21
        """
22
        Normalize the entered email
23
        """
NEW
24
        email = self.cleaned_data["email"]
×
NEW
25
        return User.objects.normalize_email(email)
×
26

27

28
class UserProfileForm(forms.ModelForm):
1✔
29
    class Meta:
1✔
30
        model = CustomUser
1✔
31
        fields = ("name",)
1✔
32

33

34
class APIKeyForm(forms.ModelForm):
1✔
35
    class Meta:
1✔
36
        model = APIKey
1✔
37
        fields = ["name", "usage_reason", "key_type"]
1✔
38
        widgets = {"usage_reason": forms.Textarea(attrs={"style": ""})}
1✔
39

40
    def __init__(self, **kwargs):
1✔
41
        self.user: CustomUser = kwargs.pop("user")
1✔
42
        super().__init__(**kwargs)
1✔
43

44
        choices = self.get_allowed_key_types()
1✔
45
        if not choices:
1✔
46
            del self.fields["key_type"]
1✔
47
            return
1✔
48
        self.fields["key_type"].choices = choices
1✔
49

50
    def get_allowed_key_types(self):
1✔
51
        """
52
        There are three cases:
53

54
        1. The user is a hobbyist. This means they can't pick their key type
55
        2. The user is a standard user. They can make one production key and multiple development keys
56
        3. The user is an enterprise user, they can make multiple of any sort of keys
57
        """
58

59
        if self.user.api_plan == "hobbyists":
1✔
60
            return None
1✔
61

62
        choices = [
1✔
63
            ("development", "Development"),
64
        ]
65
        if (
1✔
66
            self.user.api_plan == API_PLANS["enterprise"].value
67
            or not self.user.api_keys.filter(key_type="production").exists()
68
        ):
69
            choices.append(("production", "Production key"))
1✔
70
            return choices
1✔
71

72
        return None
1✔
73

74
    def clean(self):
1✔
75
        cleaned_data = super().clean()
1✔
76

77
        api_plan = self.user.api_plan
1✔
78

79
        if api_plan == "enterprise":
1✔
80
            # For "enterprise" we don't need to validate because any combo is valid.
81
            # That's what you get for giving us the big bucks
NEW
82
            return cleaned_data
×
83

84
        if api_plan == API_PLANS["hobbyists"].value:
1✔
85
            has_existing_keys = self.user.api_keys.exists()
1✔
86
            if has_existing_keys:
1✔
87
                raise ValidationError("Can't make more than one hobbyist key")
1✔
88
            return cleaned_data
1✔
89

90
        # Standard is the only remaining option
91
        has_prod_key = self.user.api_keys.filter(key_type="production").exists()
1✔
92
        if has_prod_key and cleaned_data.get("key_type") == "production":
1✔
93
            # We should never get here, because the option is removed from the
94
            # form in self.__init__
NEW
95
            raise ValidationError("Only one production key allowed")
×
96

97
        return cleaned_data
1✔
98

99
    def save(self, commit=True):
1✔
100
        model: CustomUser = super().save(commit=False)
1✔
101
        model.user = self.user
1✔
102
        return super().save(commit=True)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc