Skip to content

Commit 6908ca6

Browse files
authored
Merge pull request #25 from mapswipe/feature/tutorial-mutations
Feature/tutorial mutations and also includes validate project
2 parents 083d99f + 86bf782 commit 6908ca6

File tree

115 files changed

+4138
-739
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

115 files changed

+4138
-739
lines changed

.dockerignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,5 @@ deploy/eb/
155155
# Generated by docker on up (only for mypy)
156156
.mypy-dep/
157157
.mypy_cache/
158+
159+
rest-media-temp/

.github/workflows/ci.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ jobs:
3333

3434
steps:
3535
- uses: actions/checkout@main
36+
- uses: actions/setup-python@v5
37+
with:
38+
python-version-file: '.python-version'
39+
3640
- uses: astral-sh/setup-uv@v5
3741
with:
3842
enable-cache: true

.pre-commit-config.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ repos:
4646
- id: ruff-format
4747
types_or: [python, pyi, jupyter, toml]
4848

49+
- repo: https://github.com/gitleaks/gitleaks
50+
rev: v8.24.2
51+
hooks:
52+
- id: gitleaks
53+
4954
- repo: https://github.com/RobertCraigie/pyright-python
5055
rev: v1.1.398
5156
hooks:

TODO.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# TODO
2+
3+
## Project and Tutorial
4+
5+
- [ ] Add field validations when attaching tutorial to project
6+
- [ ] Define relationship between project and tutorial (do we avoid graphs)
7+
- Can tutorial.reference_project be equal to project.tutorial
8+
9+
## Base Tutorial
10+
11+
- [ ] Implement validation for state transition
12+
- [ ] Update Tutorial
13+
- [ ] What fields can be updated depending on the state and referenced project?
14+
- [ ] Archive Tutorial
15+
- [ ] Add validation when attaching tutorial that it's published
16+
- [ ] Add validation for state transitions
17+
- [ ] Publish Tutorial
18+
19+
# Base Project
20+
- [ ] Implement validation for state transition
21+
- [ ] Update Project
22+
- [ ] What fields can be updated depending on the state and attached project?
23+
24+
## Project Types
25+
- [ ] Implement Validate
26+
- [ ] Update Completeness to support vector tiles and rendering

apps/common/graphql/dataloaders.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
import typing
2-
31
from django.db import models
42

5-
DjangoModel = typing.TypeVar("DjangoModel", bound=models.Model)
6-
73

84
# -- Helper
9-
def load_model_objects(
5+
def load_model_objects[DjangoModel: models.Model](
106
Model: type[DjangoModel],
117
keys: list[int],
128
) -> list[DjangoModel]:

apps/common/graphql/inputs.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import strawberry
2+
3+
4+
@strawberry.input
5+
class UserResourceCreateInputMixin:
6+
client_id: str
7+
8+
9+
@strawberry.input
10+
class UserResourceUpdateInputMixin:
11+
id: strawberry.ID
12+
client_id: str
13+
14+
15+
@strawberry.input
16+
class UserResourceTopLevelUpdateInputMixin:
17+
client_id: str

apps/common/graphql/types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
# -- Interfaces
99
@strawberry.interface
1010
class UserResourceTypeMixin:
11+
client_id: str
1112
created_at: datetime.datetime
1213
modified_at: datetime.datetime
1314

apps/common/models.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,39 @@
11
# pyright: reportUninitializedInstanceVariable=false
22
import typing
33

4+
from django.core.exceptions import ValidationError
45
from django.db import models
6+
from django.utils.translation import gettext
57
from django_stubs_ext.db.models import TypedModelMeta
8+
from ulid import ULID
69

710
from apps.user.models import User
811
from main.db import Model
912

1013

14+
def validate_ulid(val: str):
15+
if val == "":
16+
raise ValidationError(
17+
gettext("Empty string is not a valid ULID value"),
18+
)
19+
try:
20+
ULID.from_str(val)
21+
except (ValueError, TypeError) as e:
22+
raise ValidationError(
23+
gettext("Not a valid ULID value '%s'") % (val),
24+
) from e
25+
26+
1127
# -- Abstracts
1228
class UserResource(Model):
29+
# FIXME(tnagorra): Should users be able to edit this?
30+
client_id = models.CharField(
31+
null=False,
32+
blank=False,
33+
unique=True,
34+
max_length=26,
35+
validators=[validate_ulid],
36+
)
1337
created_at = models.DateTimeField(auto_now_add=True)
1438
modified_at = models.DateTimeField(auto_now=True)
1539
created_by = models.ForeignKey(

apps/common/serializers.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,46 @@
11
import typing
22

3+
from django.http.request import HttpRequest
4+
from django.utils.translation import gettext
35
from rest_framework import serializers
6+
from ulid import ULID
47

58
from apps.common.models import UserResource
69

7-
ModelType = typing.TypeVar("ModelType", bound=UserResource)
810

11+
class DrfContextType(typing.TypedDict):
12+
request: HttpRequest
913

10-
class UserResourceSerializer(serializers.ModelSerializer[ModelType]):
14+
15+
# FIXME(tnagorra): Add support for DrfContextType in __init__
16+
# Reference: https://github.com/locustio/locust/blob/master/locust/clients.py#L144
17+
class UserResourceSerializer[ModelType: UserResource, ContextType: DrfContextType = DrfContextType](
18+
serializers.ModelSerializer[ModelType],
19+
):
1120
modified_at = serializers.DateTimeField(read_only=True)
1221
modified_by = serializers.PrimaryKeyRelatedField(read_only=True)
22+
client_id = serializers.CharField()
1323

1424
instance: ModelType | None # type: ignore[override]
1525

26+
def validate_client_id(self, new_client_id: str | None):
27+
if new_client_id is None:
28+
return None
29+
30+
try:
31+
ULID.from_str(new_client_id)
32+
return new_client_id
33+
except (ValueError, TypeError) as err:
34+
raise serializers.ValidationError(
35+
gettext("Not a valid ULID value '%s'") % (new_client_id),
36+
) from err
37+
38+
@property
39+
def context(self) -> ContextType: # type: ignore[override]
40+
context = super().context
41+
assert context is not None, f"Always pass context when using {type(self)}"
42+
return typing.cast("ContextType", context)
43+
1644
@typing.override
1745
def create(self, validated_data: dict[str, typing.Any]) -> ModelType:
1846
if "created_by" in self.Meta.model._meta._forward_fields_map: # type: ignore[reportAttributeAccessIssue]

apps/common/tasks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ def clear_expired_django_sessions():
1414
if not acquired:
1515
logger.warning("Clear expired django sessions")
1616
return
17-
management.call_command("clearsessions", verbosity=0, interactive=False)
17+
management.call_command("clearsessions", verbosity=0)

0 commit comments

Comments
 (0)