Skip to content
This repository has been archived by the owner on May 30, 2024. It is now read-only.

Commit

Permalink
feat: porting files API
Browse files Browse the repository at this point in the history
feat: file ownership basics

feat: gaps in test compatibility
  • Loading branch information
mcataford committed Nov 19, 2023
1 parent 205f661 commit c7e49ab
Show file tree
Hide file tree
Showing 17 changed files with 340 additions and 40 deletions.
6 changes: 6 additions & 0 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ dependencies = [
"argon2-cffi",
"psycopg2",
"django",
"djangorestframework",
]

[project.optional-dependencies]
dev = [
"anyio",
"black",
"pylint",
"pylint_django",
"pytest",
"httpx",
]
Expand All @@ -31,3 +33,7 @@ pythonpath=[
".",
"./rotini",
]

[tool.pylint.'MASTER']
load-plugins="pylint_django"

6 changes: 6 additions & 0 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ cffi==1.15.1
click==8.1.6
# via uvicorn
django==4.2.7
# via
# djangorestframework
# rotini (pyproject.toml)
djangorestframework==3.14.0
# via rotini (pyproject.toml)
exceptiongroup==1.1.2
# via anyio
Expand Down Expand Up @@ -42,6 +46,8 @@ python-dotenv==1.0.0
# via uvicorn
python-multipart==0.0.6
# via rotini (pyproject.toml)
pytz==2023.3.post1
# via djangorestframework
pyyaml==6.0.1
# via uvicorn
sniffio==1.3.0
Expand Down
16 changes: 16 additions & 0 deletions backend/requirements_dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ click==8.1.6
dill==0.3.7
# via pylint
django==4.2.7
# via
# -c requirements.txt
# djangorestframework
# rotini (pyproject.toml)
djangorestframework==3.14.0
# via
# -c requirements.txt
# rotini (pyproject.toml)
Expand Down Expand Up @@ -115,7 +120,14 @@ pyjwt==2.8.0
# -c requirements.txt
# rotini (pyproject.toml)
pylint==2.17.5
# via
# pylint-django
# pylint-plugin-utils
# rotini (pyproject.toml)
pylint-django==2.5.5
# via rotini (pyproject.toml)
pylint-plugin-utils==0.8.2
# via pylint-django
pytest==7.4.0
# via rotini (pyproject.toml)
python-dotenv==1.0.0
Expand All @@ -126,6 +138,10 @@ python-multipart==0.0.6
# via
# -c requirements.txt
# rotini (pyproject.toml)
pytz==2023.3.post1
# via
# -c requirements.txt
# djangorestframework
pyyaml==6.0.1
# via
# -c requirements.txt
Expand Down
16 changes: 16 additions & 0 deletions backend/rotini_django/base/env_settings/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""
Settings overrides for test environments.
"""

DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql_psycopg2",
"NAME": "postgres",
"USER": "postgres",
"PASSWORD": "test",
"HOST": "localhost",
"PORT": "5432",
}
}

USER_UPLOAD_ROOT = "/tmp"
68 changes: 30 additions & 38 deletions backend/rotini_django/base/settings.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,22 @@
# pylint: disable=wildcard-import,unused-wildcard-import
"""
Django settings for rotini2 project.
Base settings for all environments.
Generated by 'django-admin startproject' using Django 4.2.7.
For more information on this file, see
https://docs.djangoproject.com/en/4.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.2/ref/settings/
These values can be overridden by base.env_settings.
"""

from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

import sys
import os

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
BASE_DIR = Path(__file__).resolve().parent.parent

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure-ia%*ioce3mw$s5$y2@976@qv*3p@e+qis61h6d%5&o(!okdx&*"

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []

ALLOWED_HOSTS = ["*"]

# Application definition

INSTALLED_APPS = [
"django.contrib.admin",
Expand All @@ -37,6 +25,8 @@
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"rest_framework",
"files",
]

MIDDLEWARE = [
Expand Down Expand Up @@ -69,20 +59,26 @@

WSGI_APPLICATION = "base.wsgi.application"


# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases

DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
"ENGINE": "django.db.backends.postgresql_psycopg2",
"NAME": "postgres",
"USER": "postgres",
"PASSWORD": "test",
"HOST": "docker.host.internal",
"PORT": "5432",
}
}


# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
REST_FRAMEWORK = {
"DEFAULT_RENDERER_CLASSES": [
"rest_framework.renderers.JSONRenderer",
],
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.BasicAuthentication",
"rest_framework.authentication.SessionAuthentication",
],
}

AUTH_PASSWORD_VALIDATORS = [
{
Expand All @@ -99,10 +95,6 @@
},
]


# Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/

LANGUAGE_CODE = "en-us"

TIME_ZONE = "UTC"
Expand All @@ -111,13 +103,13 @@

USE_TZ = True

STATIC_URL = "static/"

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

STATIC_URL = "static/"
USER_UPLOAD_ROOT = os.environ.get("ROTINI_UPLOAD_ROOT", "/tmp")

# Default primary key field type
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
# Importing overrides for environment.

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
if "test" in sys.argv[0]:
from base.env_settings.test import *
7 changes: 5 additions & 2 deletions backend/rotini_django/base/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
import django.urls as django_urls

import files.urls as files_urls

urlpatterns = [
path("admin/", admin.site.urls),
django_urls.path("admin/", admin.site.urls),
django_urls.path("", django_urls.include(files_urls.urlpatterns)),
]
Binary file added backend/rotini_django/db.sqlite3
Binary file not shown.
Empty file.
6 changes: 6 additions & 0 deletions backend/rotini_django/files/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class FilesConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "files"
29 changes: 29 additions & 0 deletions backend/rotini_django/files/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Generated by Django 4.2.7 on 2023-11-17 06:15

from django.db import migrations, models
import uuid


class Migration(migrations.Migration):
initial = True

dependencies = []

operations = [
migrations.CreateModel(
name="File",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
("path", models.CharField(max_length=4096)),
("size", models.IntegerField()),
],
),
]
24 changes: 24 additions & 0 deletions backend/rotini_django/files/migrations/0002_file_owner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 4.2.7 on 2023-11-18 06:02

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("files", "0001_initial"),
]

operations = [
migrations.AddField(
model_name="file",
name="owner",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to=settings.AUTH_USER_MODEL,
),
),
]
Empty file.
17 changes: 17 additions & 0 deletions backend/rotini_django/files/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import uuid

from django.db import models
from django.conf import settings


class File(models.Model):
"""
Represents a file tracked by the system.
"""

id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
path = models.CharField(max_length=4096, null=False)
size = models.IntegerField(null=False)
owner = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True
)
39 changes: 39 additions & 0 deletions backend/rotini_django/files/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import typing
import pathlib

import rest_framework.serializers as drf_serializers

import files.models as files_models


class FileDict(typing.TypedDict):
id: str
path: str
size: int
filename: str
owner_id: int


class FileSerializer(drf_serializers.ModelSerializer):
def validate_path(self, value: str) -> typing.Union[typing.NoReturn, str]:
if not value:
raise drf_serializers.ValidationError("Path must not be empty.")
return value

def validate_owner(self, value: int) -> typing.Union[typing.NoReturn, int]:
if not value:
raise drf_serializers.ValidationError("File must have an owner.")
return value

def to_representation(self, instance: files_models.File) -> FileDict:
return {
"id": instance.id,
"path": instance.path,
"size": instance.size,
"owner_id": instance.owner.id,
"filename": pathlib.Path(instance.path).name,
}

class Meta:
model = files_models.File
fields = "__all__"
3 changes: 3 additions & 0 deletions backend/rotini_django/files/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.test import TestCase

# Create your tests here.
15 changes: 15 additions & 0 deletions backend/rotini_django/files/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import django.urls as dj_urls
import rest_framework.routers as drf_routers

import files.views as file_views

router = drf_routers.DefaultRouter()
router.register("files", file_views.FileViewSet, basename="files")

urlpatterns = router.urls + [
dj_urls.path(
"files/<str:file_id>/content/",
file_views.FileDataView.as_view(),
name="files-detail-data",
),
]
Loading

0 comments on commit c7e49ab

Please sign in to comment.