# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['strawberry_django_plus',
 'strawberry_django_plus.gql',
 'strawberry_django_plus.integrations',
 'strawberry_django_plus.management.commands',
 'strawberry_django_plus.middlewares',
 'strawberry_django_plus.utils']

package_data = \
{'': ['*'], 'strawberry_django_plus': ['templates/strawberry_django_plus/*']}

install_requires = \
['django>=3.2',
 'strawberry-graphql-django>=0.2.5',
 'strawberry-graphql>=0.93.4',
 'typing-extensions>=4.0.1']

setup_kwargs = {
    'name': 'strawberry-django-plus',
    'version': '0.4.2',
    'description': 'Enhanced Strawberry GraphQL integration with Django',
    'long_description': '# strawberry-django-plus\n\n[![build status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fblb-ventures%2Fstrawberry-django-plus%2Fbadge%3Fref%3Dmaster&style=flat)](https://actions-badge.atrox.dev/blb-ventures/straw/goto?ref=master)\n[![coverage](https://img.shields.io/codecov/c/github/blb-ventures/strawberry-django-plus.svg)](https://codecov.io/gh/blb-ventures/strawberry-django-plus)\n[![PyPI version](https://img.shields.io/pypi/v/strawberry-django-plus.svg)](https://pypi.org/project/strawberry-django-plus/)\n![python version](https://img.shields.io/pypi/pyversions/strawberry-django-plus.svg)\n![django version](https://img.shields.io/pypi/djversions/strawberry-django-plus.svg)\n\nEnhanced Strawberry integration with Django.\n\nBuilt on top of [strawberry-django](https://github.com/strawberry-graphql/strawberry-graphql-django)\nintegration, enhancing its overall functionality.\n\n## Features\n\n- All of supported features by `strawberry-django`.\n- Extension that [automatically optimize queries](#automatic-queryset-optimization) (using\n  `only`/`select_related`/`prefetch_related`) to solve graphql `N+1` problems, with support for\n  fragment spread, inline fragments, `@include`/`@skip` directives, prefetch merging, etc\n- Improved sync/async resolver that priorizes the model\'s cache to avoid have to use\n  [sync_to_async](https://docs.djangoproject.com/en/4.0/topics/async/#asgiref.sync.sync_to_async)\n  when not needed.\n- Support for [Django choices enums using](#django-choices-enums) (requires\n  [django-choices-field](https://github.com/bellini666/django-choices-field))\n- [Permissioning](#permissioning) using schema directives with the default\n  [django authentication system](https://docs.djangoproject.com/en/4.0/topics/auth/default/),\n  with support for both direct and per-object permission checking for backends that\n  support those (e.g. [django-guardian](https://django-guardian.readthedocs.io/en/stable])).\n- [Relay support](#relay-support) for queries, connections and input mutations.\n- [Django Debug Toolbar integration](#django-debug-toolbar-integration) with graphiql to\n  display metrics like SQL queries\n- (Coming Soon...) Improved Django mutations with automatic validation errors integration.\n- A well typed and documented API.\n\n## Installation\n\nInstall it with pip:\n\n```shell\npip install strawberry-django-plus\n```\n\n## Introduction\n\nSince this lib has a long name, it does provide a shortcut called `gql` where all of\nstrawberry\'s API and ours can be accessed.\n\n```python\nfrom strawberry_django_plus import gql\n\n# All strawberry\'s base api can be found directly on gql, like:\ngql.type  # same as strawberry.type\ngql.field  # same as strawberry.field\n...\n\n# The strawberry-django API and our custom implementation can be found on gql.django, like:\ngql.django.type\ngql.django.field\n...\n\n# We also have a custom relay implementation in here:\ngql.relay\n```\n\n## How To\n\n### Automatic QuerySet Optimization\n\nThe automatic optimization is enabled by adding the `DjangoOptimizerExtension` to your\nstrawberry\'s schema config.\n\n```python\nimport strawberry\nfrom strawberry_django_plus.optimizer import DjangoOptimizerExtension\n\nschema = strawberry.Schema(\n    Query,\n    extensions=[\n        # other extensions...\n        DjangoOptimizerExtension,\n    ]\n)\n```\n\nNow consider the following:\n\n```python\n# models.py\n\nclass Artist(models.Model):\n    name = models.CharField()\n\nclass Album(models.Moodel):\n    name = models.CharField()\n    release_date = models.DateTimeField()\n    artist = models.ForeignKey("Artist", related_name="albuns")\n\nclass Song(models.Model):\n    name = model.CharField()\n    duration = models.DecimalField()\n    album = models.ForeignKey("Album", related_name="songs")\n\n# schema.py\nfrom strawberry_django_plus import gql\n\n@gql.django.type(Artist)\nclass ArtistType:\n    name: auto\n    albums: "List[AlbumType]"\n\n@gql.django.type(Album)\nclass AlbumType:\n    name: auto\n    release_date: auto\n    artist: ArtistType\n    songs: "List[SongType]"\n\n@gql.django.type(Song)\nclass SongType:\n    name: auto\n    duration: auto\n    album_type: AlbumType\n\n@gql.type\nclass Query:\n    artist: Artist = gql.django.field()\n    songs: List[SongType] = gql.django.field()\n```\n\nQuerying the artist field:\n\n```gql\n{\n  artist {\n    id\n    name\n    albums {\n      id\n      name\n      songs {\n        id\n        name\n      }\n    }\n  }\n}\n```\n\n```python\n# This will generate a query like:\nArtist.objects.all().only("id", "name").prefetch_related(\n    Prefetch(\n        "albums",\n        queryset=Album.objects.all().only("id", "name").prefetch_related(\n            "songs",\n            Song.objects.all().only("id", "name"),\n        )\n    ),\n)\n```\n\nAnd querying the songs list:\n\n```gql\n{\n  song {\n    id\n    album\n    id\n    name\n    artist {\n      id\n      name\n      albums {\n        id\n        name\n        release_date\n      }\n    }\n  }\n}\n```\n\n```python\n# This will generate a query like:\nSong.objects.all().only(\n    "id",\n    "album",\n    "album__id",\n    "album__name",\n    "album__release_date",  # Note about this below\n    "album__artist",\n    "album__artist__id",\n).select_related(\n    "album",\n    "album__artist",\n).prefetch_related(\n    "album__artist__albums",\n    Prefetch(\n        "albums",\n        Album.objects.all().only("id", "name", "release_date"),\n    )\n)\n```\n\nNote that even though `album__release_date` field was not selected here, it got selected\nin the prefetch query later. Since Django caches known objects, we have to select it here or\nelse it would trigger extra queries latter.\n\nIt is also possible to include hints for non-model fields using the field api or even our\n`@model_property` (or its cached variation, `@cached_model_property`) decorator on the model\nitself, for people who likes to keep all the business logic at the model.\n\nFor example, the following will automatically optimize `only` and `select_related` if that\nfield gets selected:\n\n```python\nfrom strawberry_django_plus import gql\n\nclass Song(models.Model):\n    name = models.CharField()\n\n    @gql.model_property(only=["name", "album__name"], select_related=["album"])\n    def name_with_album(self) -> List[str]:\n        return f"{self.album.name}: {self.name}"\n\n@gql.django.type(Song)\nclass SongType:\n    name: auto\n    name_with_album: str\n```\n\nAnother option would be to define that on the field itself:\n\n```python\n@gql.django.type(Song)\nclass SongType:\n    name: auto\n    name_with_album: str = gql.django.field(\n        only=["name", "album__name"],\n        select_related=["album"],\n    )\n```\n\n### Django Choices Enums\n\nConvert choices fields into GraphQL enums by using\n[Django Choices Field](https://github.com/bellini666/django-choices-field) extension.\n\n```python\nfrom django_choices_field import TexChoicesField\n\nclass Song(models.Model):\n    class Genre(models.TextChoices):\n        ROCK = "rock", "Rock\'n\'Roll"\n        METAL = "metal", "Metal"\n        OTHERS = "others", "Who Cares?"\n\n    genre = TextChoicesField(choices_enum=Genre)\n```\n\nIn that example, a new enum called `Genre` will be created and be used for queries\nand mutations.\n\nIf you want to name it differently, decorate the class with `@gql.enum` with your preferred\nname so that strawberry-django-plus will not try to register it again.\n\n### Permissioning\n\nPermissioning is done using schema directives by applying them to the fields that requires\npermission checking.\n\nFor example:\n\n```python\n@strawberry.type\nclass SomeType:\n    login_required_field: RetType = strawberry.field(\n        directives=[IsAuthenticated()],\n    )\n    perm_required_field: OtherType = strawberry.field(\n        directives=[HasPerm("some_app.some_perm")],\n    )\n    obj_perm_required_field: OtherType = strawberry.field(\n        directives=[HasObjPerm("some_app.some_perm")],\n    )\n```\n\n- `login_required_field` will check if the user is authenticated\n- `perm_required_field` will check if the user has `"some_app.some_perm"` permission\n- `obj_perm_required_field` will check the permission for the resolved value\n\nAvailable options are:\n\n- `IsAuthenticated`: Checks if the user is authenticated (`user.is_autenticated`)\n- `IsStaff`: Checks if the user is a staff member (`user.is_staff`)\n- `IsSuperuser`: Checks if the user is a superuser (`user.is_superuser`)\n- `HasPerm(perms: str, list[str], any: bool = True)`: Checks if the user has any or all of\n  the given permissions (`user.has_perm(perm)`)\n- `HasRootPerm(perms: str | list[str], any: bool = True)`: Checks if the user has any or all\n  of the given permissions for the root of that field (`user.has_perm(perm, root)`)\n- `HasObjPerm(perms: str | list[str], any: bool = True)`: Resolves the retval and then\n  checks if the user has any or all of the given permissions for that specific value\n  (`user.has_perm(perm, retval)`). Note that if the return value is a list, this directive\n  will filter the return value, removing objects that fails the check (check below for more\n  information regarding other possibilities).\n\nThere are some important notes regarding how the directives handle the return value:\n\n- If the user passes the check, the retval is returned normally\n- If the user fails the check:\n  - If the return type was `Optional`, it returns `None`\n  - If the return type was a `List`, it returns an empty list\n  - If the return type was a relay `Connection`, it returns an empty `Connection`\n  - Otherwise, it raises a `PermissionError` for that resolver\n\nNote that since `strawberry` doesn\'t support resolvers for schema directives, it is necessary\nto use this lib\'s custom extension that handles the resolution of those and any other custom\ndefined schema directive inherited from `strawberry_django_plus.directives.SchemaDirectiveResolver`:\n\n```python\nimport strawberry\nfrom strawberry_django_plus.directives import SchemaDirectiveExtension\n\nschema = strawberry.Schema(\n    Query,\n    extensions=[\n        SchemaDirectiveExtension,\n        # other extensions...\n    ]\n)\n```\n\n### Relay Support\n\nWe have a custom [relay spec](https://relay.dev/docs/guides/graphql-server-specification/)\nimplementation. It is not tied to Django at all to allow its usage with other types.\n\nIt provides types and fields for node and connection querying. For example:\n\n```python\n# schema.py\nfrom strawberry_django_plus import gql\nfrom strawberry_django_plus.gql import relay\n\n@gql.type\nclass Fruit(relay.Node):\n    name: str\n\n    def resolve_node(cls, node_id, info, required=False):\n        ...\n\n    def resolve_nodes(cls, node_id, info, node_ids=False):\n        ...\n\n\n@gql.type\nclass Query:\n    fruit: Optional[Fruit] = relay.node()\n    fruits_connection: relay.Connection[Fruit] = relay.connection()\n\n    @relay.connection\n    def fruits_connection_filtered(self, name_startswith: str) -> Iterable[Fruit]:\n        # Note that this resolver is special. It should not resolve the connection, but\n        # the iterable of nodes itself. Thus, any arguments defined here will be appended\n        # to the query, and the pagination of the iterable returned here will be\n        # automatically handled.\n        ...\n```\n\nThis will generate a schema like this:\n\n```gql\ninterface Node {\n  id: GlobalID!\n}\n\ntype Fruit implements Node {\n  id: GlobalID!\n  name: String!\n}\n\ntype FruitEdge implements Node {\n  cursor: String!\n  node: Fruit\n}\n\ntype FruitConnection {\n  edges: [ShipEdge!]!\n  pageInfo: PageInfo!\n}\n\ntype PageInfo {\n  hasNextPage: Boolean!\n  hasPreviousPage: Boolean!\n  startCursor: String\n  endCursor: String\n}\n\ntype Query {\n  fruit(id: GlobalID!): Fruit\n  fruits_connection(\n    before: String\n    after: String\n    first: Int\n    last: Int\n  ): FruitConnection\n  fruits_connection_filtered(\n    before: String\n    after: String\n    first: Int\n    last: Int\n    nameStartswith: String!\n  ): FruitConnection\n}\n```\n\nIt is expected that types implementing the `Node` interface define some methods, like\n`resolve_nodes` and `resolve_node`. Take a look at\n[the documentation](.strawberry_django_plus.relay.py) for more information.\n\nAlso note that Django fields created with `@gql.django.type` automatically implements\nall of the required methods when the type inherits from `Node`.\n\nThis module also exposes a mutation that converts all of its arguments to a single input.\nFor example:\n\n```python\n@gql.type\nclass Mutation:\n    @relay.input_mutation\n    def create_fruit(name: str) -> Fruit:\n        ....\n```\n\nWill generate those types:\n\n```gql\ninput CreateFruitInput {\n  name: String!\n}\n\ntype Mutation {\n  createFruit(input: CreateFruitInput!): Fruit\n}\n```\n\n### Django Debug Toolbar Integration\n\nInstall [Django Debug Toolbar](https://github.com/jazzband/django-debug-toolbar)\nand change its middleware from:\n\n```python\nMIDDLEWARE = [\n    ...\n    "debug_toolbar.middleware.DebugToolbarMiddleware",\n    ...\n]\n```\n\nTo:\n\n```python\nMIDDLEWARE = [\n    ...\n    "strawberry_django_plus.middlewares.debug_toolbar.DebugToolbarMiddleware",\n    ...\n]\n```\n\n## Contributing\n\nWe use [poetry](https://github.com/sdispater/poetry) to manage dependencies, to\nget started follow these steps:\n\n```shell\ngit clone https://github.com/blb-ventures/strawberry-django-plus\ncd strawberry\npoetry install\npoetry run pytest\n```\n\nThis will install all the dependencies (including dev ones) and run the tests.\n\n### Pre commit\n\nWe have a configuration for\n[pre-commit](https://github.com/pre-commit/pre-commit), to add the hook run the\nfollowing command:\n\n```shell\npre-commit install\n```\n\n## Licensing\n\nThe code in this project is licensed under MIT license. See [LICENSE](./LICENSE)\nfor more information.\n',
    'author': 'Thiago Bellini Ribeiro',
    'author_email': 'thiago@bellini.dev',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/blb-ventures/strawberry-django-plus',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.8,<4.0',
}


setup(**setup_kwargs)
