Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix disambiguate_id to correctly handle UUIDs passed as strings #121

Merged
merged 1 commit into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion graphene_django_cud/tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@
from django.db import models
from factory.django import DjangoModelFactory

from graphene_django_cud.tests.models import User, Cat, Dog, Mouse, DogRegistration
from graphene_django_cud.tests.models import (
User,
Cat,
Dog,
Mouse,
DogRegistration,
Fish,
)


class UserFactory(DjangoModelFactory):
Expand Down Expand Up @@ -104,3 +111,10 @@ class Meta:
model = Mouse

name = "mouse"


class FishFactory(DjangoModelFactory):
class Meta:
model = Fish

name = "Koi"
26 changes: 26 additions & 0 deletions graphene_django_cud/tests/migrations/0009_auto_20231228_1921.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Generated by Django 3.2.20 on 2023-12-28 19:21

from django.db import migrations, models
import uuid


class Migration(migrations.Migration):

dependencies = [
('tests', '0008_auto_20220313_1242'),
]

operations = [
migrations.CreateModel(
name='Fish',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=40)),
],
),
migrations.AlterField(
model_name='user',
name='first_name',
field=models.CharField(blank=True, max_length=150, verbose_name='first name'),
),
]
6 changes: 6 additions & 0 deletions graphene_django_cud/tests/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import uuid
from django.contrib.auth.models import AbstractUser
from django.db import models

Expand Down Expand Up @@ -59,3 +60,8 @@ class CatUserRelation(models.Model):

class Meta:
unique_together = (("cat", "user"),)


class Fish(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
name = models.CharField(max_length=40, blank=False, null=False)
28 changes: 28 additions & 0 deletions graphene_django_cud/tests/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
Mouse,
DogRegistration,
CatUserRelation,
Fish,
)


Expand Down Expand Up @@ -57,18 +58,26 @@ class Meta:
interfaces = (Node,)


class FishNode(DjangoObjectType):
class Meta:
model = Fish
interfaces = (Node,)


class Query(graphene.ObjectType):
user = Node.Field(UserNode)
cat = Node.Field(CatNode)
dog = Node.Field(DogNode)
mice = Node.Field(MouseNode)
cat_user_relation = Node.Field(CatUserRelationNode)
fish = Node.Field(FishNode)

all_users = DjangoConnectionField(UserNode)
all_cats = DjangoConnectionField(CatNode)
all_dogs = DjangoConnectionField(DogNode)
all_mice = DjangoConnectionField(MouseNode)
all_cat_user_relations = DjangoConnectionField(CatUserRelationNode)
all_fish = DjangoConnectionField(FishNode)


class CreateUserMutation(DjangoCreateMutation):
Expand Down Expand Up @@ -251,6 +260,21 @@ class Meta:
filter_fields = ("name", "name__startswith")


class CreateFishMutation(DjangoCreateMutation):
class Meta:
model = Fish


class UpdateFishMutation(DjangoUpdateMutation):
class Meta:
model = Fish


class DeleteFishMutation(DjangoDeleteMutation):
class Meta:
model = Fish


class Mutations(graphene.ObjectType):

create_user = CreateUserMutation.Field()
Expand Down Expand Up @@ -282,5 +306,9 @@ class Mutations(graphene.ObjectType):
delete_mouse = DeleteMouseMutation.Field()
batch_delete_mouse = FilterDeleteMouseMutation.Field()

create_fish = CreateFishMutation.Field()
update_fish = UpdateFishMutation.Field()
delete_fish = DeleteFishMutation.Field(0)


schema = Schema(query=Query, mutation=Mutations)
47 changes: 46 additions & 1 deletion graphene_django_cud/tests/test_create_mutation.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
UserFactory,
CatFactory,
DogFactory,
FishFactory,
)
from graphene_django_cud.tests.dummy_query import DummyQuery
from graphene_django_cud.tests.models import User, Cat, Dog, DogRegistration
from graphene_django_cud.tests.models import User, Cat, Dog, DogRegistration, Fish
from graphene_django_cud.util import disambiguate_id


Expand Down Expand Up @@ -736,3 +737,47 @@ class Mutations(graphene.ObjectType):
cat["catUserRelations"]["edges"][0]["node"]["user"]["id"],
to_global_id("UserNode", other_user.id),
)


class TestCreateUuidPk(TestCase):
def test__creating_a_record_with_uuid_pk(self):
# This register the FishNode type
from .schema import FishNode # noqa: F401

class CreateFishMutation(DjangoCreateMutation):
class Meta:
model = Fish

class Mutations(graphene.ObjectType):
create_fish = CreateFishMutation.Field()

user = UserFactory.create()
fish = FishFactory.build()

schema = Schema(query=DummyQuery, mutation=Mutations)
mutation = """
mutation CreateFish(
$input: CreateFishInput!
){
createFish(input: $input) {
fish {
id
name
}
}
}
"""

result = schema.execute(
mutation,
variables={
"input": {
"name": fish.name
}
},
context=Dict(user=user),
)
self.assertIsNone(result.errors)

data = Dict(result.data)
self.assertEqual(data.createFish.fish.name, fish.name)
41 changes: 40 additions & 1 deletion graphene_django_cud/tests/test_delete_mutation.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
UserWithPermissionsFactory,
CatFactory,
UserFactory,
FishFactory,
)
from graphene_django_cud.tests.dummy_query import DummyQuery
from graphene_django_cud.tests.models import Cat
from graphene_django_cud.tests.models import Cat, Fish
from graphene_django_cud.util import disambiguate_id


Expand Down Expand Up @@ -128,3 +129,41 @@ class Mutations(graphene.ObjectType):
)
self.assertIsNotNone(result.errors)
self.assertIn("Not permitted", str(result.errors))

def test__deleting_a_record_with_uuid_pk__with_pk_as_str(self):
# This register the FishNode type
from .schema import FishNode # noqa: F401

class DeleteFishMutation(DjangoDeleteMutation):
class Meta:
model = Fish

class Mutations(graphene.ObjectType):
delete_fish = DeleteFishMutation.Field()

user = UserFactory.create()
fish = FishFactory.create()

schema = Schema(query=DummyQuery, mutation=Mutations)
mutation = """
mutation DeleteFish(
$id: ID!
){
deleteFish(id: $id) {
found
deletedId
}
}
"""

# Excluded use of `to_global_id` and cast UUID to str to match some
# real-world mutation scenarios.
result = schema.execute(
mutation,
variables={
"id": str(fish.id)
},
context=Dict(user=user),
)
self.assertIsNone(result.errors)
self.assertEqual(Fish.objects.count(), 0)
50 changes: 49 additions & 1 deletion graphene_django_cud/tests/test_update_mutation.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
UserWithPermissionsFactory,
DogFactory,
MouseFactory,
FishFactory,
)
from graphene_django_cud.tests.models import User, Cat, Dog
from graphene_django_cud.tests.models import User, Cat, Dog, Fish
from graphene_django_cud.util import disambiguate_id
from graphene_django_cud.tests.dummy_query import DummyQuery

Expand Down Expand Up @@ -484,6 +485,53 @@ class Mutations(graphene.ObjectType):
)
self.assertIsNone(result.errors)

def test__updating_a_record_with_uuid_pk__with_pk_as_str(self):
# This register the FishNode type
from .schema import FishNode # noqa: F401

class UpdateFishMutation(DjangoUpdateMutation):
class Meta:
model = Fish

class Mutations(graphene.ObjectType):
update_fish = UpdateFishMutation.Field()

user = UserFactory.create()
fish = FishFactory.create()

schema = Schema(query=DummyQuery, mutation=Mutations)
mutation = """
mutation UpdateFish(
$id: ID!
$input: UpdateFishInput!
){
updateFish(id: $id, input: $input) {
fish {
id
name
}
}
}
"""

# Excluded use of `to_global_id` and cast UUID to str to match some
# real-world mutation scenarios.
result = schema.execute(
mutation,
variables={
"id": str(fish.id),
"input": {
"name": "Fugu"
}
},
context=Dict(user=user),
)
self.assertIsNone(result.errors)

data = Dict(result.data)
self.assertNotEqual(data.updateFish.fish.name, fish.name)
self.assertEqual(data.updateFish.fish.name, "Fugu")


class TestUpdateMutationManyToManyOnReverseField(TestCase):
def test_default_setup__adding_resource_by_id__adds_resource(self):
Expand Down
5 changes: 4 additions & 1 deletion graphene_django_cud/util/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ def disambiguate_id(ambiguous_id: Union[int, float, str, uuid.UUID]):

if isinstance(ambiguous_id, str):
try:
return from_global_id(ambiguous_id)[1]
_id = from_global_id(ambiguous_id)[1]

if _id:
return _id
except (ValueError, TypeError, binascii.Error):
pass

Expand Down
Loading