diff options
Diffstat (limited to 'api')
| -rw-r--r-- | api/admin.py | 6 | ||||
| -rw-r--r-- | api/migrations/0018_messagedeletioncontext.py | 2 | ||||
| -rw-r--r-- | api/migrations/0018_user_rename.py | 17 | ||||
| -rw-r--r-- | api/migrations/0019_deletedmessage.py | 2 | ||||
| -rw-r--r-- | api/migrations/0019_user_in_guild.py | 18 | ||||
| -rw-r--r-- | api/migrations/0020_add_snake_field_validators.py | 24 | ||||
| -rw-r--r-- | api/migrations/0021_merge_20181125_1015.py | 14 | ||||
| -rw-r--r-- | api/models.py | 18 | ||||
| -rw-r--r-- | api/serializers.py | 18 | ||||
| -rw-r--r-- | api/tests/test_models.py | 22 | ||||
| -rw-r--r-- | api/tests/test_users.py (renamed from api/tests/test_members.py) | 30 | ||||
| -rw-r--r-- | api/urls.py | 12 | ||||
| -rw-r--r-- | api/viewsets.py | 81 | 
13 files changed, 176 insertions, 88 deletions
| diff --git a/api/admin.py b/api/admin.py index af2cfbeb..bcd41a7e 100644 --- a/api/admin.py +++ b/api/admin.py @@ -2,17 +2,16 @@ from django.contrib import admin  from .models import (      DeletedMessage, DocumentationLink, -    Member, MessageDeletionContext, +    MessageDeletionContext,      OffTopicChannelName, Role,      SnakeFact, SnakeIdiom,      SnakeName, SpecialSnake, -    Tag +    Tag, User  )  admin.site.register(DeletedMessage)  admin.site.register(DocumentationLink) -admin.site.register(Member)  admin.site.register(MessageDeletionContext)  admin.site.register(OffTopicChannelName)  admin.site.register(Role) @@ -21,3 +20,4 @@ admin.site.register(SnakeIdiom)  admin.site.register(SnakeName)  admin.site.register(SpecialSnake)  admin.site.register(Tag) +admin.site.register(User) diff --git a/api/migrations/0018_messagedeletioncontext.py b/api/migrations/0018_messagedeletioncontext.py index 39a4fb87..88cbab28 100644 --- a/api/migrations/0018_messagedeletioncontext.py +++ b/api/migrations/0018_messagedeletioncontext.py @@ -17,7 +17,7 @@ class Migration(migrations.Migration):              fields=[                  ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),                  ('creation', models.DateTimeField(help_text='When this deletion took place.')), -                ('actor', models.ForeignKey(help_text='The original actor causing this deletion. Could be the author of a manual clean command invocation, the bot when executing automatic actions, or nothing to indicate that the bulk deletion was not issued by us.', null=True, on_delete=django.db.models.deletion.CASCADE, to='api.Member')), +                ('actor', models.ForeignKey(help_text='The original actor causing this deletion. Could be the author of a manual clean command invocation, the bot when executing automatic actions, or nothing to indicate that the bulk deletion was not issued by us.', null=True, on_delete=django.db.models.deletion.CASCADE, to='api.User')),              ],              bases=(api.models.ModelReprMixin, models.Model),          ), diff --git a/api/migrations/0018_user_rename.py b/api/migrations/0018_user_rename.py new file mode 100644 index 00000000..f88eb5bc --- /dev/null +++ b/api/migrations/0018_user_rename.py @@ -0,0 +1,17 @@ +# Generated by Django 2.1.3 on 2018-11-19 20:09 + +from django.db import migrations + + +class Migration(migrations.Migration): + +    dependencies = [ +        ('api', '0017_auto_20181029_1921'), +    ] + +    operations = [ +        migrations.RenameModel( +            old_name='Member', +            new_name='User', +        ), +    ] diff --git a/api/migrations/0019_deletedmessage.py b/api/migrations/0019_deletedmessage.py index b119c3ef..fbd94949 100644 --- a/api/migrations/0019_deletedmessage.py +++ b/api/migrations/0019_deletedmessage.py @@ -23,7 +23,7 @@ class Migration(migrations.Migration):                  ('channel_id', models.BigIntegerField(help_text='The channel ID that this message was sent in, taken from Discord.', validators=[django.core.validators.MinValueValidator(limit_value=0, message='Channel IDs cannot be negative.')])),                  ('content', models.CharField(help_text='The content of this message, taken from Discord.', max_length=2000)),                  ('embeds', django.contrib.postgres.fields.ArrayField(base_field=django.contrib.postgres.fields.jsonb.JSONField(validators=[api.validators.validate_tag_embed]), help_text='Embeds attached to this message.', size=None)), -                ('author', models.ForeignKey(help_text='The author of this message.', on_delete=django.db.models.deletion.CASCADE, to='api.Member')), +                ('author', models.ForeignKey(help_text='The author of this message.', on_delete=django.db.models.deletion.CASCADE, to='api.User')),                  ('deletion_context', models.ForeignKey(help_text='The deletion context this message is part of.', on_delete=django.db.models.deletion.CASCADE, to='api.MessageDeletionContext')),              ],              options={ diff --git a/api/migrations/0019_user_in_guild.py b/api/migrations/0019_user_in_guild.py new file mode 100644 index 00000000..fda008c4 --- /dev/null +++ b/api/migrations/0019_user_in_guild.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.3 on 2018-11-19 20:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + +    dependencies = [ +        ('api', '0018_user_rename'), +    ] + +    operations = [ +        migrations.AddField( +            model_name='user', +            name='in_guild', +            field=models.BooleanField(default=True, help_text='Whether this user is in our server.'), +        ), +    ] diff --git a/api/migrations/0020_add_snake_field_validators.py b/api/migrations/0020_add_snake_field_validators.py new file mode 100644 index 00000000..3b625f9b --- /dev/null +++ b/api/migrations/0020_add_snake_field_validators.py @@ -0,0 +1,24 @@ +# Generated by Django 2.1.2 on 2018-11-24 17:11 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + +    dependencies = [ +        ('api', '0019_user_in_guild'), +    ] + +    operations = [ +        migrations.AlterField( +            model_name='snakename', +            name='name', +            field=models.CharField(help_text="The regular name for this snake, e.g. 'Python'.", max_length=100, primary_key=True, serialize=False, validators=[django.core.validators.RegexValidator(regex='^([^0-9])+$')]), +        ), +        migrations.AlterField( +            model_name='snakename', +            name='scientific', +            field=models.CharField(help_text="The scientific name for this snake, e.g. 'Python bivittatus'.", max_length=150, validators=[django.core.validators.RegexValidator(regex='^([^0-9])+$')]), +        ), +    ] diff --git a/api/migrations/0021_merge_20181125_1015.py b/api/migrations/0021_merge_20181125_1015.py new file mode 100644 index 00000000..d8eaa510 --- /dev/null +++ b/api/migrations/0021_merge_20181125_1015.py @@ -0,0 +1,14 @@ +# Generated by Django 2.1.1 on 2018-11-25 10:15 + +from django.db import migrations + + +class Migration(migrations.Migration): + +    dependencies = [ +        ('api', '0020_add_snake_field_validators'), +        ('api', '0019_deletedmessage'), +    ] + +    operations = [ +    ] diff --git a/api/models.py b/api/models.py index ded9ebeb..68833328 100644 --- a/api/models.py +++ b/api/models.py @@ -92,11 +92,13 @@ class SnakeName(ModelReprMixin, models.Model):      name = models.CharField(          primary_key=True,          max_length=100, -        help_text="The regular name for this snake, e.g. 'Python'." +        help_text="The regular name for this snake, e.g. 'Python'.", +        validators=[RegexValidator(regex=r'^([^0-9])+$')]      )      scientific = models.CharField(          max_length=150, -        help_text="The scientific name for this snake, e.g. 'Python bivittatus'." +        help_text="The scientific name for this snake, e.g. 'Python bivittatus'.", +        validators=[RegexValidator(regex=r'^([^0-9])+$')]      )      def __str__(self): @@ -167,8 +169,8 @@ class Role(ModelReprMixin, models.Model):          return self.name -class Member(ModelReprMixin, models.Model): -    """A member of our Discord server.""" +class User(ModelReprMixin, models.Model): +    """A Discord user."""      id = models.BigIntegerField(  # noqa          primary_key=True, @@ -205,6 +207,10 @@ class Member(ModelReprMixin, models.Model):          Role,          help_text="Any roles this user has on our server."      ) +    in_guild = models.BooleanField( +        default=True, +        help_text="Whether this user is in our server." +    )      def __str__(self):          return f"{self.name}#{self.discriminator}" @@ -222,7 +228,7 @@ class Message(ModelReprMixin, models.Model):          )      )      author = models.ForeignKey( -        Member, +        User,          on_delete=models.CASCADE,          help_text="The author of this message."      ) @@ -255,7 +261,7 @@ class Message(ModelReprMixin, models.Model):  class MessageDeletionContext(ModelReprMixin, models.Model):      actor = models.ForeignKey( -        Member, +        User,          on_delete=models.CASCADE,          help_text=(              "The original actor causing this deletion. Could be the author " diff --git a/api/serializers.py b/api/serializers.py index e39cd4a3..8091ac63 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -3,11 +3,11 @@ from rest_framework_bulk import BulkSerializerMixin  from .models import (      DeletedMessage, DocumentationLink, -    Member, MessageDeletionContext, -    OffTopicChannelName, Role, -    SnakeFact, SnakeIdiom, -    SnakeName, SpecialSnake, -    Tag +    MessageDeletionContext, OffTopicChannelName, +    Role, SnakeFact, +    SnakeIdiom, SnakeName, +    SpecialSnake, Tag, +    User  ) @@ -74,10 +74,10 @@ class TagSerializer(ModelSerializer):          fields = ('title', 'embed') -class MemberSerializer(BulkSerializerMixin, ModelSerializer): -    roles = PrimaryKeyRelatedField(many=True, queryset=Role.objects.all()) +class UserSerializer(BulkSerializerMixin, ModelSerializer): +    roles = PrimaryKeyRelatedField(many=True, queryset=Role.objects.all(), required=False)      class Meta: -        model = Member -        fields = ('id', 'avatar_hash', 'name', 'discriminator', 'roles') +        model = User +        fields = ('id', 'avatar_hash', 'name', 'discriminator', 'roles', 'in_guild')          depth = 1 diff --git a/api/tests/test_models.py b/api/tests/test_models.py index 8d41c23e..968f003e 100644 --- a/api/tests/test_models.py +++ b/api/tests/test_models.py @@ -4,12 +4,12 @@ from django.test import SimpleTestCase  from ..models import (      DeletedMessage, DocumentationLink, -    Member, Message, -    MessageDeletionContext, ModelReprMixin, -    OffTopicChannelName, Role, -    SnakeFact, SnakeIdiom, -    SnakeName, SpecialSnake, -    Tag +    Message, MessageDeletionContext, +    ModelReprMixin, OffTopicChannelName, +    Role, SnakeFact, +    SnakeIdiom, SnakeName, +    SpecialSnake, Tag, +    User  ) @@ -32,14 +32,14 @@ class StringDunderMethodTests(SimpleTestCase):          self.objects = (              DeletedMessage(                  id=45, -                author=Member( +                author=User(                      id=444, name='bill',                      discriminator=5, avatar_hash=None                  ),                  channel_id=666,                  content="wooey",                  deletion_context=MessageDeletionContext( -                    actor=Member( +                    actor=User(                          id=5555, name='shawn',                          discriminator=555, avatar_hash=None                      ), @@ -64,7 +64,7 @@ class StringDunderMethodTests(SimpleTestCase):              ),              Message(                  id=45, -                author=Member( +                author=User(                      id=444, name='bill',                      discriminator=5, avatar_hash=None                  ), @@ -73,13 +73,13 @@ class StringDunderMethodTests(SimpleTestCase):                  embeds=[]              ),              MessageDeletionContext( -                actor=Member( +                actor=User(                      id=5555, name='shawn',                      discriminator=555, avatar_hash=None                  ),                  creation=datetime.utcnow()              ), -            Member( +            User(                  id=5, name='bob',                  discriminator=1, avatar_hash=None              ), diff --git a/api/tests/test_members.py b/api/tests/test_users.py index 47466b62..8dadcbdb 100644 --- a/api/tests/test_members.py +++ b/api/tests/test_users.py @@ -1,7 +1,7 @@  from django_hosts.resolvers import reverse  from .base import APISubdomainTestCase -from ..models import Member, Role +from ..models import Role, User  class UnauthedDocumentationLinkAPITests(APISubdomainTestCase): @@ -10,25 +10,25 @@ class UnauthedDocumentationLinkAPITests(APISubdomainTestCase):          self.client.force_authenticate(user=None)      def test_detail_lookup_returns_401(self): -        url = reverse('bot:member-detail', args=('whatever',), host='api') +        url = reverse('bot:user-detail', args=('whatever',), host='api')          response = self.client.get(url)          self.assertEqual(response.status_code, 401)      def test_list_returns_401(self): -        url = reverse('bot:member-list', host='api') +        url = reverse('bot:user-list', host='api')          response = self.client.get(url)          self.assertEqual(response.status_code, 401)      def test_create_returns_401(self): -        url = reverse('bot:member-list', host='api') +        url = reverse('bot:user-list', host='api')          response = self.client.post(url, data={'hi': 'there'})          self.assertEqual(response.status_code, 401)      def test_delete_returns_401(self): -        url = reverse('bot:member-detail', args=('whatever',), host='api') +        url = reverse('bot:user-detail', args=('whatever',), host='api')          response = self.client.delete(url)          self.assertEqual(response.status_code, 401) @@ -45,7 +45,7 @@ class CreationTests(APISubdomainTestCase):          )      def test_accepts_valid_data(self): -        url = reverse('bot:member-list', host='api') +        url = reverse('bot:user-list', host='api')          data = {              'id': 42,              'avatar_hash': "validavatarhashiswear", @@ -53,20 +53,22 @@ class CreationTests(APISubdomainTestCase):              'discriminator': 42,              'roles': [                  self.role.id -            ] +            ], +            'in_guild': True          }          response = self.client.post(url, data=data)          self.assertEqual(response.status_code, 201)          self.assertEqual(response.json(), data) -        user = Member.objects.get(id=42) +        user = User.objects.get(id=42)          self.assertEqual(user.avatar_hash, data['avatar_hash'])          self.assertEqual(user.name, data['name'])          self.assertEqual(user.discriminator, data['discriminator']) +        self.assertEqual(user.in_guild, data['in_guild'])      def test_supports_multi_creation(self): -        url = reverse('bot:member-list', host='api') +        url = reverse('bot:user-list', host='api')          data = [              {                  'id': 5, @@ -75,14 +77,16 @@ class CreationTests(APISubdomainTestCase):                  'discriminator': 42,                  'roles': [                      self.role.id -                ] +                ], +                'in_guild': True              },              {                  'id': 8,                  'avatar_hash': "maybenot",                  'name': "another test man",                  'discriminator': 555, -                'roles': [] +                'roles': [], +                'in_guild': False              }          ] @@ -91,7 +95,7 @@ class CreationTests(APISubdomainTestCase):          self.assertEqual(response.json(), data)      def test_returns_400_for_unknown_role_id(self): -        url = reverse('bot:member-list', host='api') +        url = reverse('bot:user-list', host='api')          data = {              'id': 5,              'avatar_hash': "hahayes", @@ -106,7 +110,7 @@ class CreationTests(APISubdomainTestCase):          self.assertEqual(response.status_code, 400)      def test_returns_400_for_bad_data(self): -        url = reverse('bot:member-list', host='api') +        url = reverse('bot:user-list', host='api')          data = {              'id': True,              'avatar_hash': 1902831, diff --git a/api/urls.py b/api/urls.py index 2bca5689..dca208d8 100644 --- a/api/urls.py +++ b/api/urls.py @@ -4,10 +4,10 @@ from rest_framework.routers import DefaultRouter  from .views import HealthcheckView  from .viewsets import (      DeletedMessageViewSet, DocumentationLinkViewSet, -    MemberViewSet, OffTopicChannelNameViewSet, -    RoleViewSet, SnakeFactViewSet, -    SnakeIdiomViewSet, SnakeNameViewSet, -    SpecialSnakeViewSet, TagViewSet +    OffTopicChannelNameViewSet, RoleViewSet, +    SnakeFactViewSet, SnakeIdiomViewSet, +    SnakeNameViewSet, SpecialSnakeViewSet, +    TagViewSet, UserViewSet  ) @@ -27,8 +27,8 @@ bot_router.register(      base_name='offtopicchannelname'  )  bot_router.register( -    'members', -    MemberViewSet +    'users', +    UserViewSet  )  bot_router.register(      'roles', diff --git a/api/viewsets.py b/api/viewsets.py index 8dca5b2a..e406e8c3 100644 --- a/api/viewsets.py +++ b/api/viewsets.py @@ -10,18 +10,18 @@ from rest_framework.viewsets import GenericViewSet, ModelViewSet, ViewSet  from rest_framework_bulk import BulkCreateModelMixin  from .models import ( -    DocumentationLink, Member, -    MessageDeletionContext, OffTopicChannelName, -    Role, SnakeFact, -    SnakeIdiom, SnakeName, -    SpecialSnake, Tag +    DocumentationLink, MessageDeletionContext, +    OffTopicChannelName, Role, +    SnakeFact, SnakeIdiom, +    SnakeName, SpecialSnake, +    Tag, User  )  from .serializers import ( -    DocumentationLinkSerializer, MemberSerializer, -    MessageDeletionContextSerializer, OffTopicChannelNameSerializer, -    RoleSerializer, SnakeFactSerializer, -    SnakeIdiomSerializer, SnakeNameSerializer, -    SpecialSnakeSerializer, TagSerializer +    DocumentationLinkSerializer, MessageDeletionContextSerializer, +    OffTopicChannelNameSerializer, RoleSerializer, +    SnakeFactSerializer, SnakeIdiomSerializer, +    SnakeNameSerializer, SpecialSnakeSerializer, +    TagSerializer, UserSerializer  ) @@ -539,13 +539,13 @@ class TagViewSet(ModelViewSet):      queryset = Tag.objects.all() -class MemberViewSet(BulkCreateModelMixin, ModelViewSet): +class UserViewSet(BulkCreateModelMixin, ModelViewSet):      """ -    View providing CRUD operations on our Discord server's members through the bot. +    View providing CRUD operations on Discord users through the bot.      ## Routes -    ### GET /bot/members -    Returns all members currently known. +    ### GET /bot/users +    Returns all users currently known.      #### Response format      >>> [ @@ -559,15 +559,16 @@ class MemberViewSet(BulkCreateModelMixin, ModelViewSet):      ...             270988689419665409,      ...             277546923144249364,      ...             458226699344019457 -    ...         ] +    ...         ], +    ...         'in_guild': True      ...     }      ... ]      #### Status codes      - 200: returned on success -    ### GET /bot/members/<snowflake:int> -    Gets a single member by ID. +    ### GET /bot/users/<snowflake:int> +    Gets a single user by ID.      #### Response format      >>> { @@ -580,16 +581,17 @@ class MemberViewSet(BulkCreateModelMixin, ModelViewSet):      ...         270988689419665409,      ...         277546923144249364,      ...         458226699344019457 -    ...     ] +    ...     ], +    ...     'in_guild': True      ... }      #### Status codes      - 200: returned on success -    - 404: if a member with the given `snowflake` could not be found +    - 404: if a user with the given `snowflake` could not be found -    ### POST /bot/members -    Adds a single or multiple new members. -    The roles attached to the member(s) must be roles known by the site. +    ### POST /bot/users +    Adds a single or multiple new users. +    The roles attached to the user(s) must be roles known by the site.      #### Request body      >>> { @@ -597,18 +599,19 @@ class MemberViewSet(BulkCreateModelMixin, ModelViewSet):      ...     'avatar': str,      ...     'name': str,      ...     'discriminator': int, -    ...     'roles': List[int] +    ...     'roles': List[int], +    ...     'in_guild': bool      ... } -    Alternatively, request members can be POSTed as a list of above objects, -    in which case multiple members will be created at once. +    Alternatively, request users can be POSTed as a list of above objects, +    in which case multiple users will be created at once.      #### Status codes      - 201: returned on success      - 400: if one of the given roles does not exist, or one of the given fields is invalid -    ### PUT /bot/members/<snowflake:int> -    Update the member with the given `snowflake`. +    ### PUT /bot/users/<snowflake:int> +    Update the user with the given `snowflake`.      All fields in the request body are required.      #### Request body @@ -617,16 +620,17 @@ class MemberViewSet(BulkCreateModelMixin, ModelViewSet):      ...     'avatar': str,      ...     'name': str,      ...     'discriminator': int, -    ...     'roles': List[int] +    ...     'roles': List[int], +    ...     'in_guild': bool      ... }      #### Status codes      - 200: returned on success      - 400: if the request body was invalid, see response body for details -    - 404: if the member with the given `snowflake` could not be found +    - 404: if the user with the given `snowflake` could not be found -    ### PATCH /bot/members/<snowflake:int> -    Update the member with the given `snowflake`. +    ### PATCH /bot/users/<snowflake:int> +    Update the user with the given `snowflake`.      All fields in the request body are optional.      #### Request body @@ -635,21 +639,22 @@ class MemberViewSet(BulkCreateModelMixin, ModelViewSet):      ...     'avatar': str,      ...     'name': str,      ...     'discriminator': int, -    ...     'roles': List[int] +    ...     'roles': List[int], +    ...     'in_guild': bool      ... }      #### Status codes      - 200: returned on success      - 400: if the request body was invalid, see response body for details -    - 404: if the member with the given `snowflake` could not be found +    - 404: if the user with the given `snowflake` could not be found -    ### DELETE /bot/members/<snowflake:int> -    Deletes the member with the given `snowflake`. +    ### DELETE /bot/users/<snowflake:int> +    Deletes the user with the given `snowflake`.      #### Status codes      - 204: returned on success -    - 404: if a member with the given `snowflake` does not exist +    - 404: if a user with the given `snowflake` does not exist      """ -    serializer_class = MemberSerializer -    queryset = Member.objects.all() +    serializer_class = UserSerializer +    queryset = User.objects.all() | 
