strawberry-graphql 0.238.1__tar.gz → 0.239.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/PKG-INFO +1 -1
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/pyproject.toml +7 -1
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/aiohttp/views.py +35 -2
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/asgi/__init__.py +24 -1
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/channels/handlers/http_handler.py +49 -14
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/django/views.py +32 -8
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/fastapi/router.py +18 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/flask/views.py +8 -1
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/http/async_base_view.py +118 -5
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/http/base.py +11 -0
- strawberry_graphql-0.239.0/strawberry/http/parse_content_type.py +16 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/http/sync_base_view.py +13 -3
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/litestar/controller.py +26 -1
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/quart/views.py +17 -1
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/sanic/views.py +32 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/schema/base.py +6 -2
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/schema/execute.py +26 -13
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/schema/schema.py +2 -2
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/subscriptions/protocols/graphql_transport_ws/handlers.py +1 -1
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/__init__.py +3 -1
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/execution.py +17 -2
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/graphql.py +5 -1
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/LICENSE +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/README.md +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/__main__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/aiohttp/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/aiohttp/handlers/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/aiohttp/handlers/graphql_transport_ws_handler.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/aiohttp/handlers/graphql_ws_handler.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/aiohttp/test/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/aiohttp/test/client.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/annotation.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/asgi/handlers/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/asgi/handlers/graphql_transport_ws_handler.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/asgi/handlers/graphql_ws_handler.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/asgi/test/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/asgi/test/client.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/chalice/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/chalice/views.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/channels/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/channels/handlers/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/channels/handlers/base.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/channels/handlers/graphql_transport_ws_handler.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/channels/handlers/graphql_ws_handler.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/channels/handlers/ws_handler.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/channels/router.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/channels/testing.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/cli/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/cli/app.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/cli/commands/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/cli/commands/codegen.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/cli/commands/export_schema.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/cli/commands/schema_codegen.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/cli/commands/server.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/cli/commands/upgrade/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/cli/commands/upgrade/_fake_progress.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/cli/commands/upgrade/_run_codemod.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/cli/constants.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/cli/debug_server.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/cli/utils/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/cli/utils/load_schema.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/codegen/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/codegen/exceptions.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/codegen/plugins/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/codegen/plugins/print_operation.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/codegen/plugins/python.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/codegen/plugins/typescript.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/codegen/query_codegen.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/codegen/types.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/codemods/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/codemods/annotated_unions.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/codemods/update_imports.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/dataloader.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/directive.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/django/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/django/apps.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/django/context.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/django/test/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/django/test/client.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/conflicting_arguments.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/duplicated_type_name.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/exception.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/exception_source.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/handler.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/invalid_argument_type.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/invalid_union_type.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/missing_arguments_annotations.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/missing_dependencies.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/missing_field_annotation.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/missing_return_annotation.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/not_a_strawberry_enum.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/object_is_not_a_class.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/object_is_not_an_enum.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/permission_fail_silently_requires_optional.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/private_strawberry_field.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/scalar_already_registered.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/syntax.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/unresolved_field_type.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/utils/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/exceptions/utils/source_finder.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/experimental/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/experimental/pydantic/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/experimental/pydantic/_compat.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/experimental/pydantic/conversion.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/experimental/pydantic/conversion_types.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/experimental/pydantic/error_type.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/experimental/pydantic/exceptions.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/experimental/pydantic/fields.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/experimental/pydantic/object_type.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/experimental/pydantic/utils.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/ext/LICENSE +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/ext/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/ext/dataclasses/LICENSE +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/ext/dataclasses/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/ext/dataclasses/dataclasses.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/ext/mypy_plugin.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/add_validation_rules.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/base_extension.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/context.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/directives.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/disable_validation.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/field_extension.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/mask_errors.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/max_aliases.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/max_tokens.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/parser_cache.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/pyinstrument.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/query_depth_limiter.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/runner.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/tracing/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/tracing/apollo.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/tracing/datadog.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/tracing/opentelemetry.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/tracing/sentry.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/tracing/utils.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/utils.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/extensions/validation_cache.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/fastapi/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/fastapi/context.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/fastapi/handlers/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/fastapi/handlers/graphql_transport_ws_handler.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/fastapi/handlers/graphql_ws_handler.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/federation/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/federation/argument.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/federation/enum.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/federation/field.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/federation/mutation.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/federation/object_type.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/federation/scalar.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/federation/schema.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/federation/schema_directive.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/federation/schema_directives.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/federation/types.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/federation/union.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/field_extensions/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/field_extensions/input_mutation.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/file_uploads/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/file_uploads/scalars.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/file_uploads/utils.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/flask/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/http/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/http/exceptions.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/http/ides.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/http/temporal_response.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/http/types.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/http/typevars.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/litestar/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/litestar/handlers/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/litestar/handlers/graphql_transport_ws_handler.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/litestar/handlers/graphql_ws_handler.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/parent.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/permission.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/printer/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/printer/ast_from_value.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/printer/printer.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/py.typed +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/quart/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/relay/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/relay/exceptions.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/relay/fields.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/relay/types.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/relay/utils.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/resolvers.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/sanic/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/sanic/context.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/sanic/utils.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/scalars.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/schema/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/schema/compat.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/schema/config.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/schema/exceptions.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/schema/name_converter.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/schema/schema_converter.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/schema/types/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/schema/types/base_scalars.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/schema/types/concrete_type.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/schema/types/scalar.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/schema/validation_rules/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/schema/validation_rules/one_of.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/schema_codegen/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/schema_directive.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/schema_directives.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/static/apollo-sandbox.html +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/static/graphiql.html +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/static/pathfinder.html +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/subscriptions/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/subscriptions/protocols/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/subscriptions/protocols/graphql_transport_ws/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/subscriptions/protocols/graphql_transport_ws/types.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/subscriptions/protocols/graphql_ws/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/subscriptions/protocols/graphql_ws/handlers.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/subscriptions/protocols/graphql_ws/types.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/test/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/test/client.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/tools/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/tools/create_type.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/tools/merge_types.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/arguments.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/auto.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/base.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/enum.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/field.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/fields/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/fields/resolver.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/info.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/lazy_type.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/mutation.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/nodes.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/object_type.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/private.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/scalar.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/type_resolver.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/union.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/types/unset.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/utils/__init__.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/utils/aio.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/utils/await_maybe.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/utils/dataclasses.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/utils/debug.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/utils/deprecations.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/utils/graphql_lexer.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/utils/importer.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/utils/inspect.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/utils/logging.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/utils/operation.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/utils/str_converters.py +0 -0
- {strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/utils/typing.py +0 -0
@@ -1,7 +1,7 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "strawberry-graphql"
|
3
3
|
packages = [ { include = "strawberry" } ]
|
4
|
-
version = "0.
|
4
|
+
version = "0.239.0"
|
5
5
|
description = "A library for creating GraphQL APIs"
|
6
6
|
authors = ["Patrick Arminio <patrick.arminio@gmail.com>"]
|
7
7
|
license = "MIT"
|
@@ -173,6 +173,12 @@ filterwarnings = [
|
|
173
173
|
# ignoring the text instead of the whole warning because we'd
|
174
174
|
# get an error when django is not installed
|
175
175
|
"ignore:The default value of USE_TZ",
|
176
|
+
"ignore::DeprecationWarning:pydantic_openapi_schema.*",
|
177
|
+
"ignore::DeprecationWarning:graphql.*",
|
178
|
+
"ignore::DeprecationWarning:websockets.*",
|
179
|
+
"ignore::DeprecationWarning:pydantic.*",
|
180
|
+
"ignore::UserWarning:pydantic.*",
|
181
|
+
"ignore::DeprecationWarning:pkg_resources.*",
|
176
182
|
]
|
177
183
|
|
178
184
|
[tool.autopub]
|
@@ -7,10 +7,13 @@ from io import BytesIO
|
|
7
7
|
from typing import (
|
8
8
|
TYPE_CHECKING,
|
9
9
|
Any,
|
10
|
+
AsyncGenerator,
|
11
|
+
Callable,
|
10
12
|
Dict,
|
11
13
|
Iterable,
|
12
14
|
Mapping,
|
13
15
|
Optional,
|
16
|
+
Union,
|
14
17
|
cast,
|
15
18
|
)
|
16
19
|
|
@@ -73,11 +76,17 @@ class AioHTTPRequestAdapter(AsyncHTTPRequestAdapter):
|
|
73
76
|
|
74
77
|
@property
|
75
78
|
def content_type(self) -> Optional[str]:
|
76
|
-
return self.
|
79
|
+
return self.headers.get("content-type")
|
77
80
|
|
78
81
|
|
79
82
|
class GraphQLView(
|
80
|
-
AsyncBaseHTTPView[
|
83
|
+
AsyncBaseHTTPView[
|
84
|
+
web.Request,
|
85
|
+
Union[web.Response, web.StreamResponse],
|
86
|
+
web.Response,
|
87
|
+
Context,
|
88
|
+
RootValue,
|
89
|
+
]
|
81
90
|
):
|
82
91
|
# Mark the view as coroutine so that AIOHTTP does not confuse it with a deprecated
|
83
92
|
# bare handler function.
|
@@ -180,5 +189,29 @@ class GraphQLView(
|
|
180
189
|
|
181
190
|
return sub_response
|
182
191
|
|
192
|
+
async def create_multipart_response(
|
193
|
+
self,
|
194
|
+
request: web.Request,
|
195
|
+
stream: Callable[[], AsyncGenerator[str, None]],
|
196
|
+
sub_response: web.Response,
|
197
|
+
) -> web.StreamResponse:
|
198
|
+
response = web.StreamResponse(
|
199
|
+
status=sub_response.status,
|
200
|
+
headers={
|
201
|
+
**sub_response.headers,
|
202
|
+
"Transfer-Encoding": "chunked",
|
203
|
+
"Content-type": "multipart/mixed;boundary=graphql;subscriptionSpec=1.0,application/json",
|
204
|
+
},
|
205
|
+
)
|
206
|
+
|
207
|
+
await response.prepare(request)
|
208
|
+
|
209
|
+
async for data in stream():
|
210
|
+
await response.write(data.encode())
|
211
|
+
|
212
|
+
await response.write_eof()
|
213
|
+
|
214
|
+
return response
|
215
|
+
|
183
216
|
|
184
217
|
__all__ = ["GraphQLView"]
|
@@ -5,6 +5,8 @@ from datetime import timedelta
|
|
5
5
|
from typing import (
|
6
6
|
TYPE_CHECKING,
|
7
7
|
Any,
|
8
|
+
AsyncIterator,
|
9
|
+
Callable,
|
8
10
|
Mapping,
|
9
11
|
Optional,
|
10
12
|
Sequence,
|
@@ -14,7 +16,12 @@ from typing import (
|
|
14
16
|
|
15
17
|
from starlette import status
|
16
18
|
from starlette.requests import Request
|
17
|
-
from starlette.responses import
|
19
|
+
from starlette.responses import (
|
20
|
+
HTMLResponse,
|
21
|
+
PlainTextResponse,
|
22
|
+
Response,
|
23
|
+
StreamingResponse,
|
24
|
+
)
|
18
25
|
from starlette.websockets import WebSocket
|
19
26
|
|
20
27
|
from strawberry.asgi.handlers import (
|
@@ -213,3 +220,19 @@ class GraphQL(
|
|
213
220
|
response.status_code = sub_response.status_code
|
214
221
|
|
215
222
|
return response
|
223
|
+
|
224
|
+
async def create_multipart_response(
|
225
|
+
self,
|
226
|
+
request: Request | WebSocket,
|
227
|
+
stream: Callable[[], AsyncIterator[str]],
|
228
|
+
sub_response: Response,
|
229
|
+
) -> Response:
|
230
|
+
return StreamingResponse(
|
231
|
+
stream(),
|
232
|
+
status_code=sub_response.status_code,
|
233
|
+
headers={
|
234
|
+
**sub_response.headers,
|
235
|
+
"Transfer-Encoding": "chunked",
|
236
|
+
"Content-type": "multipart/mixed;boundary=graphql;subscriptionSpec=1.0,application/json",
|
237
|
+
},
|
238
|
+
)
|
@@ -1,8 +1,3 @@
|
|
1
|
-
"""GraphQLHTTPHandler.
|
2
|
-
|
3
|
-
A consumer to provide a graphql endpoint, and optionally graphiql.
|
4
|
-
"""
|
5
|
-
|
6
1
|
from __future__ import annotations
|
7
2
|
|
8
3
|
import dataclasses
|
@@ -10,7 +5,17 @@ import json
|
|
10
5
|
import warnings
|
11
6
|
from functools import cached_property
|
12
7
|
from io import BytesIO
|
13
|
-
from typing import
|
8
|
+
from typing import (
|
9
|
+
TYPE_CHECKING,
|
10
|
+
Any,
|
11
|
+
AsyncGenerator,
|
12
|
+
Callable,
|
13
|
+
Dict,
|
14
|
+
Mapping,
|
15
|
+
Optional,
|
16
|
+
Union,
|
17
|
+
)
|
18
|
+
from typing_extensions import assert_never
|
14
19
|
from urllib.parse import parse_qs
|
15
20
|
|
16
21
|
from django.conf import settings
|
@@ -44,6 +49,14 @@ class ChannelsResponse:
|
|
44
49
|
headers: Dict[bytes, bytes] = dataclasses.field(default_factory=dict)
|
45
50
|
|
46
51
|
|
52
|
+
@dataclasses.dataclass
|
53
|
+
class MultipartChannelsResponse:
|
54
|
+
stream: Callable[[], AsyncGenerator[str, None]]
|
55
|
+
status: int = 200
|
56
|
+
content_type: str = "multipart/mixed;boundary=graphql;subscriptionSpec=1.0"
|
57
|
+
headers: Dict[bytes, bytes] = dataclasses.field(default_factory=dict)
|
58
|
+
|
59
|
+
|
47
60
|
@dataclasses.dataclass
|
48
61
|
class ChannelsRequest:
|
49
62
|
consumer: ChannelsConsumer
|
@@ -186,16 +199,28 @@ class BaseGraphQLHTTPConsumer(ChannelsConsumer, AsyncHttpConsumer):
|
|
186
199
|
async def handle(self, body: bytes) -> None:
|
187
200
|
request = ChannelsRequest(consumer=self, body=body)
|
188
201
|
try:
|
189
|
-
response
|
202
|
+
response = await self.run(request)
|
190
203
|
|
191
204
|
if b"Content-Type" not in response.headers:
|
192
205
|
response.headers[b"Content-Type"] = response.content_type.encode()
|
193
206
|
|
194
|
-
|
195
|
-
response.
|
196
|
-
response.
|
197
|
-
|
198
|
-
|
207
|
+
if isinstance(response, MultipartChannelsResponse):
|
208
|
+
response.headers[b"Transfer-Encoding"] = b"chunked"
|
209
|
+
await self.send_headers(headers=response.headers)
|
210
|
+
|
211
|
+
async for chunk in response.stream():
|
212
|
+
await self.send_body(chunk.encode("utf-8"), more_body=True)
|
213
|
+
|
214
|
+
await self.send_body(b"", more_body=False)
|
215
|
+
|
216
|
+
elif isinstance(response, ChannelsResponse):
|
217
|
+
await self.send_response(
|
218
|
+
response.status,
|
219
|
+
response.content,
|
220
|
+
headers=response.headers,
|
221
|
+
)
|
222
|
+
else:
|
223
|
+
assert_never(response)
|
199
224
|
except HTTPException as e:
|
200
225
|
await self.send_response(e.status_code, e.reason.encode())
|
201
226
|
|
@@ -204,7 +229,7 @@ class GraphQLHTTPConsumer(
|
|
204
229
|
BaseGraphQLHTTPConsumer,
|
205
230
|
AsyncBaseHTTPView[
|
206
231
|
ChannelsRequest,
|
207
|
-
ChannelsResponse,
|
232
|
+
Union[ChannelsResponse, MultipartChannelsResponse],
|
208
233
|
TemporalResponse,
|
209
234
|
Context,
|
210
235
|
RootValue,
|
@@ -248,6 +273,16 @@ class GraphQLHTTPConsumer(
|
|
248
273
|
async def get_sub_response(self, request: ChannelsRequest) -> TemporalResponse:
|
249
274
|
return TemporalResponse()
|
250
275
|
|
276
|
+
async def create_multipart_response(
|
277
|
+
self,
|
278
|
+
request: ChannelsRequest,
|
279
|
+
stream: Callable[[], AsyncGenerator[str, None]],
|
280
|
+
sub_response: TemporalResponse,
|
281
|
+
) -> MultipartChannelsResponse:
|
282
|
+
status = sub_response.status_code or 200
|
283
|
+
headers = {k.encode(): v.encode() for k, v in sub_response.headers.items()}
|
284
|
+
return MultipartChannelsResponse(stream=stream, status=status, headers=headers)
|
285
|
+
|
251
286
|
async def render_graphql_ide(self, request: ChannelsRequest) -> ChannelsResponse:
|
252
287
|
return ChannelsResponse(
|
253
288
|
content=self.graphql_ide_html.encode(), content_type="text/html"
|
@@ -302,7 +337,7 @@ class SyncGraphQLHTTPConsumer(
|
|
302
337
|
request: ChannelsRequest,
|
303
338
|
context: Optional[Context] = UNSET,
|
304
339
|
root_value: Optional[RootValue] = UNSET,
|
305
|
-
) -> ChannelsResponse:
|
340
|
+
) -> ChannelsResponse | MultipartChannelsResponse:
|
306
341
|
return super().run(request, context, root_value)
|
307
342
|
|
308
343
|
|
@@ -5,6 +5,7 @@ import warnings
|
|
5
5
|
from typing import (
|
6
6
|
TYPE_CHECKING,
|
7
7
|
Any,
|
8
|
+
AsyncIterator,
|
8
9
|
Callable,
|
9
10
|
Mapping,
|
10
11
|
Optional,
|
@@ -14,8 +15,14 @@ from typing import (
|
|
14
15
|
|
15
16
|
from asgiref.sync import markcoroutinefunction
|
16
17
|
from django.core.serializers.json import DjangoJSONEncoder
|
17
|
-
from django.http import
|
18
|
-
|
18
|
+
from django.http import (
|
19
|
+
HttpRequest,
|
20
|
+
HttpResponse,
|
21
|
+
HttpResponseNotAllowed,
|
22
|
+
JsonResponse,
|
23
|
+
StreamingHttpResponse,
|
24
|
+
)
|
25
|
+
from django.http.response import HttpResponseBase
|
19
26
|
from django.template import RequestContext, Template
|
20
27
|
from django.template.exceptions import TemplateDoesNotExist
|
21
28
|
from django.template.loader import render_to_string
|
@@ -116,7 +123,7 @@ class AsyncDjangoHTTPRequestAdapter(AsyncHTTPRequestAdapter):
|
|
116
123
|
|
117
124
|
@property
|
118
125
|
def content_type(self) -> Optional[str]:
|
119
|
-
return self.
|
126
|
+
return self.headers.get("Content-type")
|
120
127
|
|
121
128
|
async def get_body(self) -> str:
|
122
129
|
return self.request.body.decode()
|
@@ -159,8 +166,9 @@ class BaseView:
|
|
159
166
|
|
160
167
|
def create_response(
|
161
168
|
self, response_data: GraphQLHTTPResponse, sub_response: HttpResponse
|
162
|
-
) ->
|
169
|
+
) -> HttpResponseBase:
|
163
170
|
data = self.encode_json(response_data)
|
171
|
+
|
164
172
|
response = HttpResponse(
|
165
173
|
data,
|
166
174
|
content_type="application/json",
|
@@ -177,6 +185,22 @@ class BaseView:
|
|
177
185
|
|
178
186
|
return response
|
179
187
|
|
188
|
+
async def create_multipart_response(
|
189
|
+
self,
|
190
|
+
request: HttpRequest,
|
191
|
+
stream: Callable[[], AsyncIterator[Any]],
|
192
|
+
sub_response: TemporalHttpResponse,
|
193
|
+
) -> HttpResponseBase:
|
194
|
+
return StreamingHttpResponse(
|
195
|
+
streaming_content=stream(),
|
196
|
+
status=sub_response.status_code,
|
197
|
+
headers={
|
198
|
+
**sub_response.headers,
|
199
|
+
"Transfer-Encoding": "chunked",
|
200
|
+
"Content-type": "multipart/mixed;boundary=graphql;subscriptionSpec=1.0,application/json",
|
201
|
+
},
|
202
|
+
)
|
203
|
+
|
180
204
|
def encode_json(self, response_data: GraphQLHTTPResponse) -> str:
|
181
205
|
return json.dumps(response_data, cls=DjangoJSONEncoder)
|
182
206
|
|
@@ -184,7 +208,7 @@ class BaseView:
|
|
184
208
|
class GraphQLView(
|
185
209
|
BaseView,
|
186
210
|
SyncBaseHTTPView[
|
187
|
-
HttpRequest,
|
211
|
+
HttpRequest, HttpResponseBase, TemporalHttpResponse, Context, RootValue
|
188
212
|
],
|
189
213
|
View,
|
190
214
|
):
|
@@ -207,7 +231,7 @@ class GraphQLView(
|
|
207
231
|
@method_decorator(csrf_exempt)
|
208
232
|
def dispatch(
|
209
233
|
self, request: HttpRequest, *args: Any, **kwargs: Any
|
210
|
-
) -> Union[HttpResponseNotAllowed, TemplateResponse,
|
234
|
+
) -> Union[HttpResponseNotAllowed, TemplateResponse, HttpResponseBase]:
|
211
235
|
try:
|
212
236
|
return self.run(request=request)
|
213
237
|
except HTTPException as e:
|
@@ -233,7 +257,7 @@ class GraphQLView(
|
|
233
257
|
class AsyncGraphQLView(
|
234
258
|
BaseView,
|
235
259
|
AsyncBaseHTTPView[
|
236
|
-
HttpRequest,
|
260
|
+
HttpRequest, HttpResponseBase, TemporalHttpResponse, Context, RootValue
|
237
261
|
],
|
238
262
|
View,
|
239
263
|
):
|
@@ -266,7 +290,7 @@ class AsyncGraphQLView(
|
|
266
290
|
@method_decorator(csrf_exempt)
|
267
291
|
async def dispatch( # pyright: ignore
|
268
292
|
self, request: HttpRequest, *args: Any, **kwargs: Any
|
269
|
-
) -> Union[HttpResponseNotAllowed, TemplateResponse,
|
293
|
+
) -> Union[HttpResponseNotAllowed, TemplateResponse, HttpResponseBase]:
|
270
294
|
try:
|
271
295
|
return await self.run(request=request)
|
272
296
|
except HTTPException as e:
|
@@ -6,6 +6,7 @@ from inspect import signature
|
|
6
6
|
from typing import (
|
7
7
|
TYPE_CHECKING,
|
8
8
|
Any,
|
9
|
+
AsyncIterator,
|
9
10
|
Awaitable,
|
10
11
|
Callable,
|
11
12
|
Dict,
|
@@ -25,6 +26,7 @@ from starlette.responses import (
|
|
25
26
|
JSONResponse,
|
26
27
|
PlainTextResponse,
|
27
28
|
Response,
|
29
|
+
StreamingResponse,
|
28
30
|
)
|
29
31
|
from starlette.websockets import WebSocket
|
30
32
|
|
@@ -330,5 +332,21 @@ class GraphQLRouter(
|
|
330
332
|
|
331
333
|
return response
|
332
334
|
|
335
|
+
async def create_multipart_response(
|
336
|
+
self,
|
337
|
+
request: Request,
|
338
|
+
stream: Callable[[], AsyncIterator[str]],
|
339
|
+
sub_response: Response,
|
340
|
+
) -> Response:
|
341
|
+
return StreamingResponse(
|
342
|
+
stream(),
|
343
|
+
status_code=sub_response.status_code,
|
344
|
+
headers={
|
345
|
+
**sub_response.headers,
|
346
|
+
"Transfer-Encoding": "chunked",
|
347
|
+
"Content-type": "multipart/mixed;boundary=graphql;subscriptionSpec=1.0,application/json",
|
348
|
+
},
|
349
|
+
)
|
350
|
+
|
333
351
|
|
334
352
|
__all__ = ["GraphQLRouter"]
|
@@ -1,7 +1,14 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import warnings
|
4
|
-
from typing import
|
4
|
+
from typing import (
|
5
|
+
TYPE_CHECKING,
|
6
|
+
Any,
|
7
|
+
Mapping,
|
8
|
+
Optional,
|
9
|
+
Union,
|
10
|
+
cast,
|
11
|
+
)
|
5
12
|
|
6
13
|
from flask import Request, Response, render_template_string, request
|
7
14
|
from flask.views import View
|
{strawberry_graphql-0.238.1 → strawberry_graphql-0.239.0}/strawberry/http/async_base_view.py
RENAMED
@@ -1,12 +1,17 @@
|
|
1
1
|
import abc
|
2
|
+
import asyncio
|
3
|
+
import contextlib
|
2
4
|
import json
|
3
5
|
from typing import (
|
6
|
+
Any,
|
7
|
+
AsyncGenerator,
|
4
8
|
Callable,
|
5
9
|
Dict,
|
6
10
|
Generic,
|
7
11
|
List,
|
8
12
|
Mapping,
|
9
13
|
Optional,
|
14
|
+
Tuple,
|
10
15
|
Union,
|
11
16
|
)
|
12
17
|
|
@@ -15,15 +20,20 @@ from graphql import GraphQLError
|
|
15
20
|
from strawberry import UNSET
|
16
21
|
from strawberry.exceptions import MissingQueryError
|
17
22
|
from strawberry.file_uploads.utils import replace_placeholders_with_files
|
18
|
-
from strawberry.http import
|
23
|
+
from strawberry.http import (
|
24
|
+
GraphQLHTTPResponse,
|
25
|
+
GraphQLRequestData,
|
26
|
+
process_result,
|
27
|
+
)
|
19
28
|
from strawberry.http.ides import GraphQL_IDE
|
20
29
|
from strawberry.schema.base import BaseSchema
|
21
30
|
from strawberry.schema.exceptions import InvalidOperationTypeError
|
22
|
-
from strawberry.types import ExecutionResult
|
31
|
+
from strawberry.types import ExecutionResult, SubscriptionExecutionResult
|
23
32
|
from strawberry.types.graphql import OperationType
|
24
33
|
|
25
34
|
from .base import BaseView
|
26
35
|
from .exceptions import HTTPException
|
36
|
+
from .parse_content_type import parse_content_type
|
27
37
|
from .types import FormData, HTTPMethod, QueryParams
|
28
38
|
from .typevars import Context, Request, Response, RootValue, SubResponse
|
29
39
|
|
@@ -82,9 +92,17 @@ class AsyncBaseHTTPView(
|
|
82
92
|
@abc.abstractmethod
|
83
93
|
async def render_graphql_ide(self, request: Request) -> Response: ...
|
84
94
|
|
95
|
+
async def create_multipart_response(
|
96
|
+
self,
|
97
|
+
request: Request,
|
98
|
+
stream: Callable[[], AsyncGenerator[str, None]],
|
99
|
+
sub_response: SubResponse,
|
100
|
+
) -> Response:
|
101
|
+
raise ValueError("Multipart responses are not supported")
|
102
|
+
|
85
103
|
async def execute_operation(
|
86
104
|
self, request: Request, context: Context, root_value: Optional[RootValue]
|
87
|
-
) -> ExecutionResult:
|
105
|
+
) -> Union[ExecutionResult, SubscriptionExecutionResult]:
|
88
106
|
request_adapter = self.request_adapter_class(request)
|
89
107
|
|
90
108
|
try:
|
@@ -178,6 +196,11 @@ class AsyncBaseHTTPView(
|
|
178
196
|
except MissingQueryError as e:
|
179
197
|
raise HTTPException(400, "No GraphQL query found in the request") from e
|
180
198
|
|
199
|
+
if isinstance(result, SubscriptionExecutionResult):
|
200
|
+
stream = self._get_stream(request, result)
|
201
|
+
|
202
|
+
return await self.create_multipart_response(request, stream, sub_response)
|
203
|
+
|
181
204
|
response_data = await self.process_result(request=request, result=result)
|
182
205
|
|
183
206
|
if result.errors:
|
@@ -187,17 +210,107 @@ class AsyncBaseHTTPView(
|
|
187
210
|
response_data=response_data, sub_response=sub_response
|
188
211
|
)
|
189
212
|
|
213
|
+
def encode_multipart_data(self, data: Any, separator: str) -> str:
|
214
|
+
return "".join(
|
215
|
+
[
|
216
|
+
f"\r\n--{separator}\r\n",
|
217
|
+
"Content-Type: application/json\r\n\r\n",
|
218
|
+
self.encode_json(data),
|
219
|
+
"\n",
|
220
|
+
]
|
221
|
+
)
|
222
|
+
|
223
|
+
def _stream_with_heartbeat(
|
224
|
+
self, stream: Callable[[], AsyncGenerator[str, None]]
|
225
|
+
) -> Callable[[], AsyncGenerator[str, None]]:
|
226
|
+
"""Adds a heartbeat to the stream, to prevent the connection from closing when there are no messages being sent."""
|
227
|
+
queue = asyncio.Queue[Tuple[bool, Any]](1)
|
228
|
+
|
229
|
+
cancelling = False
|
230
|
+
|
231
|
+
async def drain() -> None:
|
232
|
+
try:
|
233
|
+
async for item in stream():
|
234
|
+
await queue.put((False, item))
|
235
|
+
except Exception as e:
|
236
|
+
if not cancelling:
|
237
|
+
await queue.put((True, e))
|
238
|
+
else:
|
239
|
+
raise
|
240
|
+
|
241
|
+
async def heartbeat() -> None:
|
242
|
+
while True:
|
243
|
+
await queue.put((False, self.encode_multipart_data({}, "graphql")))
|
244
|
+
|
245
|
+
await asyncio.sleep(5)
|
246
|
+
|
247
|
+
async def merged() -> AsyncGenerator[str, None]:
|
248
|
+
heartbeat_task = asyncio.create_task(heartbeat())
|
249
|
+
task = asyncio.create_task(drain())
|
250
|
+
|
251
|
+
async def cancel_tasks() -> None:
|
252
|
+
nonlocal cancelling
|
253
|
+
cancelling = True
|
254
|
+
task.cancel()
|
255
|
+
|
256
|
+
with contextlib.suppress(asyncio.CancelledError):
|
257
|
+
await task
|
258
|
+
|
259
|
+
heartbeat_task.cancel()
|
260
|
+
|
261
|
+
with contextlib.suppress(asyncio.CancelledError):
|
262
|
+
await heartbeat_task
|
263
|
+
|
264
|
+
try:
|
265
|
+
while not task.done():
|
266
|
+
raised, data = await queue.get()
|
267
|
+
|
268
|
+
if raised:
|
269
|
+
await cancel_tasks()
|
270
|
+
raise data
|
271
|
+
|
272
|
+
yield data
|
273
|
+
finally:
|
274
|
+
await cancel_tasks()
|
275
|
+
|
276
|
+
return merged
|
277
|
+
|
278
|
+
def _get_stream(
|
279
|
+
self,
|
280
|
+
request: Request,
|
281
|
+
result: SubscriptionExecutionResult,
|
282
|
+
separator: str = "graphql",
|
283
|
+
) -> Callable[[], AsyncGenerator[str, None]]:
|
284
|
+
async def stream() -> AsyncGenerator[str, None]:
|
285
|
+
async for value in result:
|
286
|
+
response = await self.process_result(request, value)
|
287
|
+
yield self.encode_multipart_data({"payload": response}, separator)
|
288
|
+
|
289
|
+
yield f"\r\n--{separator}--\r\n"
|
290
|
+
|
291
|
+
return self._stream_with_heartbeat(stream)
|
292
|
+
|
293
|
+
async def parse_multipart_subscriptions(
|
294
|
+
self, request: AsyncHTTPRequestAdapter
|
295
|
+
) -> Dict[str, str]:
|
296
|
+
if request.method == "GET":
|
297
|
+
return self.parse_query_params(request.query_params)
|
298
|
+
|
299
|
+
return self.parse_json(await request.get_body())
|
300
|
+
|
190
301
|
async def parse_http_body(
|
191
302
|
self, request: AsyncHTTPRequestAdapter
|
192
303
|
) -> GraphQLRequestData:
|
193
|
-
content_type = request.content_type or ""
|
304
|
+
content_type, params = parse_content_type(request.content_type or "")
|
194
305
|
|
195
306
|
if request.method == "GET":
|
196
307
|
data = self.parse_query_params(request.query_params)
|
197
308
|
elif "application/json" in content_type:
|
198
309
|
data = self.parse_json(await request.get_body())
|
199
|
-
elif content_type
|
310
|
+
elif content_type == "multipart/form-data":
|
200
311
|
data = await self.parse_multipart(request)
|
312
|
+
elif self._is_multipart_subscriptions(content_type, params):
|
313
|
+
data = await self.parse_multipart_subscriptions(request)
|
201
314
|
else:
|
202
315
|
raise HTTPException(400, "Unsupported content type")
|
203
316
|
|
@@ -69,5 +69,16 @@ class BaseView(Generic[Request]):
|
|
69
69
|
graphql_ide=self.graphql_ide,
|
70
70
|
)
|
71
71
|
|
72
|
+
def _is_multipart_subscriptions(
|
73
|
+
self, content_type: str, params: Dict[str, str]
|
74
|
+
) -> bool:
|
75
|
+
if content_type != "multipart/mixed":
|
76
|
+
return False
|
77
|
+
|
78
|
+
if params.get("boundary") != "graphql":
|
79
|
+
return False
|
80
|
+
|
81
|
+
return params.get("subscriptionspec", "").startswith("1.0")
|
82
|
+
|
72
83
|
|
73
84
|
__all__ = ["BaseView"]
|
@@ -0,0 +1,16 @@
|
|
1
|
+
from email.message import Message
|
2
|
+
from typing import Dict, Tuple
|
3
|
+
|
4
|
+
|
5
|
+
def parse_content_type(content_type: str) -> Tuple[str, Dict[str, str]]:
|
6
|
+
"""Parse a content type header into a mime-type and a dictionary of parameters."""
|
7
|
+
email = Message()
|
8
|
+
email["content-type"] = content_type
|
9
|
+
|
10
|
+
params = email.get_params()
|
11
|
+
|
12
|
+
assert params
|
13
|
+
|
14
|
+
mime_type, _ = params.pop(0)
|
15
|
+
|
16
|
+
return mime_type, dict(params)
|
@@ -16,7 +16,11 @@ from graphql import GraphQLError
|
|
16
16
|
from strawberry import UNSET
|
17
17
|
from strawberry.exceptions import MissingQueryError
|
18
18
|
from strawberry.file_uploads.utils import replace_placeholders_with_files
|
19
|
-
from strawberry.http import
|
19
|
+
from strawberry.http import (
|
20
|
+
GraphQLHTTPResponse,
|
21
|
+
GraphQLRequestData,
|
22
|
+
process_result,
|
23
|
+
)
|
20
24
|
from strawberry.http.ides import GraphQL_IDE
|
21
25
|
from strawberry.schema import BaseSchema
|
22
26
|
from strawberry.schema.exceptions import InvalidOperationTypeError
|
@@ -25,6 +29,7 @@ from strawberry.types.graphql import OperationType
|
|
25
29
|
|
26
30
|
from .base import BaseView
|
27
31
|
from .exceptions import HTTPException
|
32
|
+
from .parse_content_type import parse_content_type
|
28
33
|
from .types import HTTPMethod, QueryParams
|
29
34
|
from .typevars import Context, Request, Response, RootValue, SubResponse
|
30
35
|
|
@@ -131,14 +136,19 @@ class SyncBaseHTTPView(
|
|
131
136
|
raise HTTPException(400, "File(s) missing in form data") from e
|
132
137
|
|
133
138
|
def parse_http_body(self, request: SyncHTTPRequestAdapter) -> GraphQLRequestData:
|
134
|
-
content_type = request.content_type or ""
|
139
|
+
content_type, params = parse_content_type(request.content_type or "")
|
135
140
|
|
136
141
|
if request.method == "GET":
|
137
142
|
data = self.parse_query_params(request.query_params)
|
138
143
|
elif "application/json" in content_type:
|
139
144
|
data = self.parse_json(request.body)
|
140
|
-
|
145
|
+
# TODO: multipart via get?
|
146
|
+
elif content_type == "multipart/form-data":
|
141
147
|
data = self.parse_multipart(request)
|
148
|
+
elif self._is_multipart_subscriptions(content_type, params):
|
149
|
+
raise HTTPException(
|
150
|
+
400, "Multipart subcriptions are not supported in sync mode"
|
151
|
+
)
|
142
152
|
else:
|
143
153
|
raise HTTPException(400, "Unsupported content type")
|
144
154
|
|