├── .alexignore ├── .alexrc ├── .codecov.yml ├── .coveragerc ├── .devcontainer ├── Dockerfile ├── devcontainer.json └── post-install.sh ├── .dockerignore ├── .editorconfig ├── .github ├── bot-action │ ├── Dockerfile │ ├── action.yml │ └── main.py ├── dependabot.yml ├── logo.png ├── pyproject.toml ├── release-check-action │ ├── Dockerfile │ ├── check.py │ ├── config.py │ └── release.py └── workflows │ ├── codeflash.yaml │ ├── codeql-analysis.yml │ ├── federation-compatibility.yml │ ├── invite-contributors.yml │ ├── issue-manager.yml │ ├── ok-to-preview.yml │ ├── pre-release-pr.yml │ ├── release-check.yml │ ├── release.yml │ ├── slash-commands.yml │ └── test.yml ├── .gitignore ├── .gitpod.yml ├── .pre-commit-config.yaml ├── .prettierrc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs ├── README.md ├── _test.md ├── breaking-changes.md ├── breaking-changes │ ├── 0.146.0.md │ ├── 0.159.0.md │ ├── 0.169.0.md │ ├── 0.180.0.md │ ├── 0.213.0.md │ ├── 0.217.0.md │ ├── 0.233.0.md │ ├── 0.236.0.md │ ├── 0.240.0.md │ ├── 0.243.0.md │ ├── 0.249.0.md │ ├── 0.251.0.md │ └── 0.268.0.md ├── codegen │ ├── query-codegen.md │ └── schema-codegen.md ├── concepts │ ├── async.md │ └── typings.md ├── editors │ ├── mypy.md │ ├── pylance.png │ └── vscode.md ├── errors.md ├── errors │ ├── _template.md │ ├── conflicting-arguments.md │ ├── duplicated-type-name.md │ ├── invalid-argument-type.md │ ├── invalid-type-for-union-merge.md │ ├── invalid-union-type.md │ ├── missing-arguments-annotations.md │ ├── missing-field-annotation.md │ ├── missing-return-annotation.md │ ├── node-id-annotation.md │ ├── object-is-not-an-enum.md │ ├── object-is-not-class.md │ ├── permission-fail-silently-requires-optional.md │ ├── private-strawberry-field.md │ ├── relay-wrong-annotation.md │ ├── relay-wrong-resolver-annotation.md │ ├── scalar-already-registered.md │ ├── unresolved-field-type.md │ └── unsupported-type.md ├── extensions.md ├── extensions │ ├── _template.md │ ├── add-validation-rules.md │ ├── apollo-tracing.md │ ├── datadog.md │ ├── disable-validation.md │ ├── mask-errors.md │ ├── max-aliases-limiter.md │ ├── max-tokens-limiter.md │ ├── opentelemetry.md │ ├── parser-cache.md │ ├── pyinstrument.md │ ├── query-depth-limiter.md │ ├── sentry-tracing.md │ └── validation-cache.md ├── faq.md ├── federation │ ├── custom_directives.md │ ├── entities.md │ ├── entity-interfaces.md │ └── introduction.md ├── general │ ├── multipart-subscriptions.md │ ├── mutations.md │ ├── queries.md │ ├── schema-basics.md │ ├── subscriptions.md │ ├── upgrades.md │ └── why.md ├── guides │ ├── accessing-parent-data.md │ ├── authentication.md │ ├── convert-to-dictionary.md │ ├── custom-extensions.md │ ├── dataloaders.md │ ├── errors.md │ ├── federation-v1.md │ ├── federation.md │ ├── field-extensions.md │ ├── file-upload.md │ ├── pagination │ │ ├── connections.md │ │ ├── cursor-based.md │ │ ├── offset-based.md │ │ └── overview.md │ ├── permissions.md │ ├── relay.md │ ├── schema-export.md │ ├── server.md │ └── tools.md ├── images │ ├── index-query-example.png │ ├── index-server.png │ └── subscriptions-count-websocket.png ├── index.md ├── integrations │ ├── aiohttp.md │ ├── asgi.md │ ├── chalice.md │ ├── channels.md │ ├── creating-an-integration.md │ ├── django.md │ ├── fastapi.md │ ├── flask.md │ ├── index.md │ ├── litestar.md │ ├── pydantic.md │ ├── quart.md │ ├── sanic.md │ └── starlette.md ├── operations │ ├── deployment.md │ ├── testing.md │ └── tracing.md └── types │ ├── enums.md │ ├── exceptions.md │ ├── generics.md │ ├── input-types.md │ ├── interfaces.md │ ├── lazy.md │ ├── object-types.md │ ├── operation-directives.md │ ├── private.md │ ├── resolvers.md │ ├── scalars.md │ ├── schema-configurations.md │ ├── schema-directives.md │ ├── schema.md │ └── union.md ├── federation-compatibility ├── Dockerfile ├── docker-compose.yml └── schema.py ├── mypy.ini ├── noxfile.py ├── poetry.lock ├── pyproject.toml ├── setup.py ├── strawberry ├── __init__.py ├── __main__.py ├── aiohttp │ ├── __init__.py │ ├── test │ │ ├── __init__.py │ │ └── client.py │ └── views.py ├── annotation.py ├── asgi │ ├── __init__.py │ └── test │ │ ├── __init__.py │ │ └── client.py ├── chalice │ ├── __init__.py │ └── views.py ├── channels │ ├── __init__.py │ ├── handlers │ │ ├── __init__.py │ │ ├── base.py │ │ ├── http_handler.py │ │ └── ws_handler.py │ ├── router.py │ └── testing.py ├── cli │ ├── __init__.py │ ├── app.py │ ├── commands │ │ ├── __init__.py │ │ ├── codegen.py │ │ ├── export_schema.py │ │ ├── schema_codegen.py │ │ ├── server.py │ │ └── upgrade │ │ │ ├── __init__.py │ │ │ ├── _fake_progress.py │ │ │ └── _run_codemod.py │ ├── constants.py │ ├── debug_server.py │ └── utils │ │ ├── __init__.py │ │ └── load_schema.py ├── codegen │ ├── __init__.py │ ├── exceptions.py │ ├── plugins │ │ ├── __init__.py │ │ ├── print_operation.py │ │ ├── python.py │ │ └── typescript.py │ ├── query_codegen.py │ └── types.py ├── codemods │ ├── __init__.py │ ├── annotated_unions.py │ └── update_imports.py ├── dataloader.py ├── directive.py ├── django │ ├── __init__.py │ ├── apps.py │ ├── context.py │ ├── test │ │ ├── __init__.py │ │ └── client.py │ └── views.py ├── exceptions │ ├── __init__.py │ ├── conflicting_arguments.py │ ├── duplicated_type_name.py │ ├── exception.py │ ├── exception_source.py │ ├── handler.py │ ├── invalid_argument_type.py │ ├── invalid_union_type.py │ ├── missing_arguments_annotations.py │ ├── missing_dependencies.py │ ├── missing_field_annotation.py │ ├── missing_return_annotation.py │ ├── object_is_not_a_class.py │ ├── object_is_not_an_enum.py │ ├── permission_fail_silently_requires_optional.py │ ├── private_strawberry_field.py │ ├── scalar_already_registered.py │ ├── syntax.py │ ├── unresolved_field_type.py │ └── utils │ │ ├── __init__.py │ │ └── source_finder.py ├── experimental │ ├── __init__.py │ └── pydantic │ │ ├── __init__.py │ │ ├── _compat.py │ │ ├── conversion.py │ │ ├── conversion_types.py │ │ ├── error_type.py │ │ ├── exceptions.py │ │ ├── fields.py │ │ ├── object_type.py │ │ └── utils.py ├── ext │ ├── LICENSE │ ├── __init__.py │ ├── dataclasses │ │ ├── LICENSE │ │ ├── README.md │ │ ├── __init__.py │ │ └── dataclasses.py │ └── mypy_plugin.py ├── extensions │ ├── __init__.py │ ├── add_validation_rules.py │ ├── base_extension.py │ ├── context.py │ ├── directives.py │ ├── disable_validation.py │ ├── field_extension.py │ ├── mask_errors.py │ ├── max_aliases.py │ ├── max_tokens.py │ ├── parser_cache.py │ ├── pyinstrument.py │ ├── query_depth_limiter.py │ ├── runner.py │ ├── tracing │ │ ├── __init__.py │ │ ├── apollo.py │ │ ├── datadog.py │ │ ├── opentelemetry.py │ │ └── utils.py │ ├── utils.py │ └── validation_cache.py ├── fastapi │ ├── __init__.py │ ├── context.py │ └── router.py ├── federation │ ├── __init__.py │ ├── argument.py │ ├── enum.py │ ├── field.py │ ├── mutation.py │ ├── object_type.py │ ├── scalar.py │ ├── schema.py │ ├── schema_directive.py │ ├── schema_directives.py │ ├── types.py │ └── union.py ├── field_extensions │ ├── __init__.py │ └── input_mutation.py ├── file_uploads │ ├── __init__.py │ ├── scalars.py │ └── utils.py ├── flask │ ├── __init__.py │ └── views.py ├── http │ ├── __init__.py │ ├── async_base_view.py │ ├── base.py │ ├── exceptions.py │ ├── ides.py │ ├── parse_content_type.py │ ├── sync_base_view.py │ ├── temporal_response.py │ ├── types.py │ └── typevars.py ├── litestar │ ├── __init__.py │ └── controller.py ├── parent.py ├── permission.py ├── printer │ ├── __init__.py │ ├── ast_from_value.py │ └── printer.py ├── py.typed ├── quart │ ├── __init__.py │ └── views.py ├── relay │ ├── __init__.py │ ├── exceptions.py │ ├── fields.py │ ├── types.py │ └── utils.py ├── resolvers.py ├── sanic │ ├── __init__.py │ ├── context.py │ ├── utils.py │ └── views.py ├── scalars.py ├── schema │ ├── __init__.py │ ├── base.py │ ├── compat.py │ ├── config.py │ ├── exceptions.py │ ├── name_converter.py │ ├── schema.py │ ├── schema_converter.py │ ├── types │ │ ├── __init__.py │ │ ├── base_scalars.py │ │ ├── concrete_type.py │ │ └── scalar.py │ └── validation_rules │ │ ├── __init__.py │ │ └── one_of.py ├── schema_codegen │ └── __init__.py ├── schema_directive.py ├── schema_directives.py ├── static │ ├── apollo-sandbox.html │ ├── graphiql.html │ └── pathfinder.html ├── subscriptions │ ├── __init__.py │ └── protocols │ │ ├── __init__.py │ │ ├── graphql_transport_ws │ │ ├── __init__.py │ │ ├── handlers.py │ │ └── types.py │ │ └── graphql_ws │ │ ├── __init__.py │ │ ├── handlers.py │ │ └── types.py ├── test │ ├── __init__.py │ └── client.py ├── tools │ ├── __init__.py │ ├── create_type.py │ └── merge_types.py ├── types │ ├── __init__.py │ ├── arguments.py │ ├── auto.py │ ├── base.py │ ├── cast.py │ ├── enum.py │ ├── execution.py │ ├── field.py │ ├── fields │ │ ├── __init__.py │ │ └── resolver.py │ ├── graphql.py │ ├── info.py │ ├── lazy_type.py │ ├── maybe.py │ ├── mutation.py │ ├── nodes.py │ ├── object_type.py │ ├── private.py │ ├── scalar.py │ ├── type_resolver.py │ ├── union.py │ └── unset.py └── utils │ ├── __init__.py │ ├── aio.py │ ├── await_maybe.py │ ├── dataclasses.py │ ├── debug.py │ ├── deprecations.py │ ├── graphql_lexer.py │ ├── importer.py │ ├── inspect.py │ ├── logging.py │ ├── operation.py │ ├── str_converters.py │ └── typing.py └── tests ├── __init__.py ├── a.py ├── asgi ├── __init__.py └── test_async.py ├── b.py ├── benchmarks ├── __init__.py ├── api.py ├── queries │ ├── items.graphql │ ├── many_fields.graphql │ ├── many_fields_directives.graphql │ └── simple.graphql ├── schema.py ├── test_complex_schema.py ├── test_execute.py ├── test_execute_sync.py ├── test_execute_with_extensions.py ├── test_generic_input.py └── test_subscriptions.py ├── c.py ├── channels ├── __init__.py ├── test_layers.py ├── test_router.py └── test_testing.py ├── cli ├── __init__.py ├── conftest.py ├── fixtures │ ├── __init__.py │ └── unions.py ├── snapshots │ ├── unions.py │ ├── unions_py38.py │ └── unions_typing_extension.py ├── test_codegen.py ├── test_export_schema.py ├── test_schema_codegen.py ├── test_server.py └── test_upgrade.py ├── codegen ├── __init__.py ├── conftest.py ├── lazy_type.py ├── queries │ ├── alias.graphql │ ├── basic.graphql │ ├── custom_scalar.graphql │ ├── enum.graphql │ ├── fragment.graphql │ ├── generic_types.graphql │ ├── interface.graphql │ ├── interface_fragments.graphql │ ├── interface_fragments_with_spread.graphql │ ├── interface_single_fragment.graphql │ ├── multiple_types.graphql │ ├── multiple_types_optional.graphql │ ├── mutation-fragment.graphql │ ├── mutation.graphql │ ├── mutation_with_object.graphql │ ├── nullable_list_of_non_scalars.graphql │ ├── optional_and_lists.graphql │ ├── union.graphql │ ├── union_return.graphql │ ├── union_with_one_type.graphql │ ├── union_with_typename.graphql │ ├── union_with_typename_and_fragment.graphql │ ├── variables.graphql │ └── with_directives.graphql ├── snapshots │ ├── python │ │ ├── alias.py │ │ ├── basic.py │ │ ├── custom_scalar.py │ │ ├── enum.py │ │ ├── fragment.py │ │ ├── generic_types.py │ │ ├── interface.py │ │ ├── interface_fragments.py │ │ ├── interface_fragments_with_spread.py │ │ ├── interface_single_fragment.py │ │ ├── multiple_types.py │ │ ├── multiple_types_optional.py │ │ ├── mutation-fragment.py │ │ ├── mutation.py │ │ ├── mutation_with_object.py │ │ ├── nullable_list_of_non_scalars.py │ │ ├── optional_and_lists.py │ │ ├── union.py │ │ ├── union_return.py │ │ ├── union_with_one_type.py │ │ ├── union_with_typename.py │ │ ├── union_with_typename_and_fragment.py │ │ ├── variables.py │ │ └── with_directives.py │ └── typescript │ │ ├── alias.ts │ │ ├── basic.ts │ │ ├── custom_scalar.ts │ │ ├── enum.ts │ │ ├── fragment.ts │ │ ├── generic_types.ts │ │ ├── interface.ts │ │ ├── interface_fragments.ts │ │ ├── interface_fragments_with_spread.ts │ │ ├── interface_single_fragment.ts │ │ ├── multiple_types.ts │ │ ├── multiple_types_optional.ts │ │ ├── mutation-fragment.ts │ │ ├── mutation.ts │ │ ├── mutation_with_object.ts │ │ ├── nullable_list_of_non_scalars.ts │ │ ├── optional_and_lists.ts │ │ ├── union.ts │ │ ├── union_return.ts │ │ ├── union_with_one_type.ts │ │ ├── union_with_typename.ts │ │ ├── union_with_typename_and_fragment.ts │ │ ├── variables.ts │ │ └── with_directives.ts ├── test_print_operation.py └── test_query_codegen.py ├── codemods ├── __init__.py ├── test_annotated_unions.py ├── test_annotated_unions_pipe.py └── test_imports.py ├── conftest.py ├── d.py ├── django ├── __init__.py ├── app │ ├── __init__.py │ ├── models.py │ └── urls.py ├── conftest.py ├── django_settings.py ├── test_dataloaders.py └── test_extensions.py ├── enums ├── __init__.py └── test_enum.py ├── exceptions ├── __init__.py ├── classes │ ├── __init__.py │ └── test_exception_class_missing_optional_dependencies_error.py ├── test_exception_handler.py ├── test_exception_source.py └── test_threading_exception_handler.py ├── experimental ├── __init__.py └── pydantic │ ├── __init__.py │ ├── schema │ ├── __init__.py │ ├── test_1_and_2.py │ ├── test_basic.py │ ├── test_computed.py │ ├── test_defaults.py │ ├── test_federation.py │ ├── test_forward_reference.py │ └── test_mutation.py │ ├── test_basic.py │ ├── test_conversion.py │ ├── test_error_type.py │ ├── test_fields.py │ └── utils.py ├── extensions ├── __init__.py ├── test_custom_objects_for_setting_attribute.py ├── test_pyinstrument.py └── tracing │ ├── __init__.py │ └── test_opentelemetry.py ├── fastapi ├── __init__.py ├── app.py ├── test_async.py ├── test_context.py ├── test_openapi.py └── test_router.py ├── federation ├── __init__.py ├── printer │ ├── __init__.py │ ├── test_additional_directives.py │ ├── test_authenticated.py │ ├── test_compose_directive.py │ ├── test_entities.py │ ├── test_inaccessible.py │ ├── test_interface.py │ ├── test_interface_object.py │ ├── test_keys.py │ ├── test_link.py │ ├── test_one_of.py │ ├── test_override.py │ ├── test_policy.py │ ├── test_provides.py │ ├── test_requires.py │ ├── test_requires_scopes.py │ ├── test_shareable.py │ └── test_tag.py ├── test_entities.py ├── test_schema.py └── test_types.py ├── fields ├── __init__.py ├── test_arguments.py ├── test_field_basics.py ├── test_field_defaults.py ├── test_field_descriptions.py ├── test_field_exceptions.py ├── test_field_names.py ├── test_permissions.py └── test_resolvers.py ├── file_uploads ├── __init__.py └── test_utils.py ├── fixtures ├── __init__.py └── sample_package │ ├── __init__.py │ └── sample_module.py ├── http ├── __init__.py ├── clients │ ├── __init__.py │ ├── aiohttp.py │ ├── asgi.py │ ├── async_django.py │ ├── async_flask.py │ ├── base.py │ ├── chalice.py │ ├── channels.py │ ├── django.py │ ├── fastapi.py │ ├── flask.py │ ├── litestar.py │ ├── quart.py │ └── sanic.py ├── conftest.py ├── context.py ├── test_async_base_view.py ├── test_graphql_ide.py ├── test_http.py ├── test_multipart_subscription.py ├── test_mutation.py ├── test_parse_content_type.py ├── test_process_result.py ├── test_query.py ├── test_query_via_get.py └── test_upload.py ├── litestar ├── __init__.py ├── app.py ├── conftest.py ├── test_context.py ├── test_response_headers.py └── test_response_status.py ├── objects ├── __init__.py ├── generics │ ├── __init__.py │ ├── test_generic_objects.py │ └── test_names.py ├── test_inheritance.py ├── test_interfaces.py ├── test_object_instantiation.py └── test_types.py ├── plugins ├── __init__.py └── strawberry_exceptions.py ├── python_312 ├── __init__.py ├── test_generic_objects.py ├── test_generics_schema.py ├── test_inspect.py └── test_python_generics.py ├── relay ├── __init__.py ├── schema.py ├── schema_future_annotations.py ├── snapshots │ ├── schema.gql │ └── schema_future_annotations.gql ├── test_connection.py ├── test_custom_edge.py ├── test_exceptions.py ├── test_fields.py ├── test_id.py ├── test_schema.py ├── test_types.py └── test_utils.py ├── sanic ├── __init__.py └── test_file_upload.py ├── schema ├── __init__.py ├── extensions │ ├── __init__.py │ ├── schema_extensions │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_extensions.py │ │ └── test_subscription.py │ ├── test_apollo.py │ ├── test_datadog.py │ ├── test_field_extensions.py │ ├── test_imports.py │ ├── test_input_mutation.py │ ├── test_input_mutation_federation.py │ ├── test_input_mutation_future.py │ ├── test_mask_errors.py │ ├── test_max_aliases.py │ ├── test_max_tokens.py │ ├── test_opentelemetry.py │ ├── test_parser_cache.py │ ├── test_query_depth_limiter.py │ └── test_validation_cache.py ├── test_annotated │ ├── __init__.py │ ├── type_a.py │ └── type_b.py ├── test_arguments.py ├── test_basic.py ├── test_camel_casing.py ├── test_config.py ├── test_custom_scalar.py ├── test_dataloaders.py ├── test_directives.py ├── test_duplicated_types.py ├── test_enum.py ├── test_execution.py ├── test_execution_errors.py ├── test_extensions.py ├── test_fields.py ├── test_generics.py ├── test_generics_nested.py ├── test_get_extensions.py ├── test_info.py ├── test_input.py ├── test_interface.py ├── test_lazy │ ├── __init__.py │ ├── schema.py │ ├── test_lazy.py │ ├── test_lazy_generic.py │ ├── type_a.py │ ├── type_b.py │ ├── type_c.py │ └── type_d.py ├── test_lazy_types │ ├── __init__.py │ ├── test_cyclic.py │ ├── test_lazy_enums.py │ ├── type_a.py │ └── type_b.py ├── test_list.py ├── test_maybe.py ├── test_mutation.py ├── test_name_converter.py ├── test_one_of.py ├── test_permission.py ├── test_private_field.py ├── test_pydantic.py ├── test_resolvers.py ├── test_scalars.py ├── test_schema_generation.py ├── test_schema_hooks.py ├── test_subscription.py ├── test_union.py ├── test_union_deprecated.py ├── test_unresolved_fields.py └── types │ ├── __init__.py │ ├── test_date.py │ ├── test_datetime.py │ ├── test_decimal.py │ ├── test_time.py │ └── test_uuid.py ├── schema_codegen ├── __init__.py ├── snapshots │ └── long_descriptions.py ├── test_descriptions.py ├── test_enum.py ├── test_federation.py ├── test_input_types.py ├── test_names.py ├── test_order.py ├── test_root_types_extend.py ├── test_scalar.py ├── test_schema.py ├── test_types.py └── test_union.py ├── test ├── __init__.py ├── conftest.py └── test_client.py ├── test_aio.py ├── test_auto.py ├── test_dataloaders.py ├── test_deprecations.py ├── test_forward_references.py ├── test_info.py ├── test_inspect.py ├── test_printer ├── __init__.py ├── test_basic.py ├── test_one_of.py └── test_schema_directives.py ├── test_repr.py ├── test_type.py ├── tools ├── __init__.py ├── test_create_type.py └── test_merge_types.py ├── typecheckers ├── __init__.py ├── test_auto.py ├── test_directives.py ├── test_enum.py ├── test_fastapi.py ├── test_federation.py ├── test_federation_fields.py ├── test_federation_params.py ├── test_fields.py ├── test_fields_input.py ├── test_fields_keyword.py ├── test_fields_resolver.py ├── test_fields_resolver_async.py ├── test_info.py ├── test_interface.py ├── test_maybe.py ├── test_params.py ├── test_private.py ├── test_relay.py ├── test_scalars.py ├── test_type.py ├── test_union.py └── utils │ ├── __init__.py │ ├── marks.py │ ├── mypy.py │ ├── pyright.py │ ├── result.py │ └── typecheck.py ├── types ├── __init__.py ├── cross_module_resolvers │ ├── README.md │ ├── __init__.py │ ├── a_mod.py │ ├── b_mod.py │ ├── c_mod.py │ ├── test_cross_module_resolvers.py │ └── x_mod.py ├── resolving │ ├── __init__.py │ ├── test_enums.py │ ├── test_forward_references.py │ ├── test_generics.py │ ├── test_lists.py │ ├── test_literals.py │ ├── test_optionals.py │ ├── test_string_annotations.py │ ├── test_union_pipe.py │ ├── test_unions.py │ └── test_unions_deprecated.py ├── test_annotation.py ├── test_argument_types.py ├── test_cast.py ├── test_convert_to_dictionary.py ├── test_deferred_annotations.py ├── test_execution.py ├── test_field_types.py ├── test_lazy_types.py ├── test_lazy_types_future_annotations.py ├── test_object_types.py ├── test_parent_type.py ├── test_parent_type_future_annotations.py └── test_resolver_types.py ├── utils ├── __init__.py ├── test_arguments_converter.py ├── test_get_first_operation.py ├── test_importer.py ├── test_inspect.py ├── test_logging.py ├── test_pretty_print.py ├── test_typing.py └── test_typing_forward_refs.py ├── views ├── __init__.py └── schema.py └── websockets ├── __init__.py ├── conftest.py ├── test_graphql_transport_ws.py ├── test_graphql_ws.py ├── test_websockets.py └── views.py /.alexignore: -------------------------------------------------------------------------------- 1 | CHANGELOG.md 2 | TWEET.md 3 | -------------------------------------------------------------------------------- /.alexrc: -------------------------------------------------------------------------------- 1 | { 2 | "allow": [ 3 | "black", 4 | "hook", 5 | "hooks", 6 | "failure", 7 | "period", 8 | "execute", 9 | "executed", 10 | "executes", 11 | "execution", 12 | "reject", 13 | "special", 14 | "primitive", 15 | "invalid", 16 | "failed" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | require_ci_to_pass: yes 4 | 5 | coverage: 6 | precision: 2 7 | round: down 8 | range: "70...100" 9 | 10 | status: 11 | project: yes 12 | patch: yes 13 | changes: no 14 | 15 | comment: 16 | layout: "header, diff" 17 | behavior: default 18 | require_changes: no 19 | 20 | ignore: 21 | - "strawberry/ext/mypy_plugin.py" 22 | - "setup.py" 23 | - "strawberry/experimental/pydantic/conversion_types.py" 24 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | # .coveragerc to control coverage.py 2 | [run] 3 | branch = True 4 | 5 | [report] 6 | # Regexes for lines to exclude from consideration 7 | exclude_lines = 8 | # Have to re-enable the standard pragma 9 | pragma: no cover 10 | 11 | # Don't complain about missing debug-only code: 12 | def __repr__ 13 | if self\.debug 14 | 15 | # Don't complain if tests don't hit defensive assertion code: 16 | raise AssertionError 17 | raise NotImplementedError 18 | raise UnsupportedTypeError 19 | 20 | # Don't complain if non-runnable code isn't run: 21 | if 0: 22 | if __name__ == .__main__.: 23 | 24 | # Don't complain about abstract methods, they aren't run: 25 | @(abc\.)?abstractmethod 26 | 27 | # Don't complain about TYPE_CHECKING 28 | if TYPE_CHECKING: 29 | 30 | @overload 31 | 32 | # Those are not supposed to be hit 33 | assert_never\(\w+\) 34 | 35 | ignore_errors = True 36 | 37 | omit = 38 | ./.venv/** 39 | noxfile.py 40 | 41 | [html] 42 | directory = coverage_html_report 43 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG VARIANT=3.13 2 | FROM mcr.microsoft.com/devcontainers/python:${VARIANT} 3 | 4 | RUN pip3 install poetry pre-commit 5 | RUN poetry config virtualenvs.in-project true 6 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Strawberry GraphQL", 3 | "build": { 4 | "dockerfile": "Dockerfile" 5 | }, 6 | 7 | "customizations": { 8 | "vscode": { 9 | "settings": { 10 | "terminal.integrated.shell.linux": "/bin/bash", 11 | "python.pythonPath": "/usr/local/bin/python", 12 | "python.linting.enabled": false, 13 | "python.linting.pylintEnabled": false, 14 | "ruff.enable": true, 15 | "ruff.organizeImports": true, 16 | "[python]": { 17 | "editor.formatOnSave": true, 18 | "editor.defaultFormatter": "charliermarsh.ruff", 19 | "editor.codeActionsOnSave": { 20 | "source.organizeImports.ruff": true 21 | } 22 | } 23 | }, 24 | "extensions": [ 25 | "ms-python.python", 26 | "ms-python.vscode-pylance", 27 | "charliermarsh.ruff", 28 | "eamodio.gitlens" 29 | ] 30 | } 31 | }, 32 | 33 | "postCreateCommand": "sh ./.devcontainer/post-install.sh" 34 | } 35 | -------------------------------------------------------------------------------- /.devcontainer/post-install.sh: -------------------------------------------------------------------------------- 1 | poetry install --with dev,integrations 2 | pre-commit install --install-hooks 3 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .github 3 | .benchmarks 4 | .devcotainer 5 | .venv 6 | .mypy_cache 7 | .nox 8 | .ruff_cache 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | 7 | [*.py] 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | max_line_length = 88 11 | insert_final_newline = true 12 | 13 | [*.yml] 14 | indent_size = 2 15 | insert_final_newline = true 16 | -------------------------------------------------------------------------------- /.github/bot-action/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-alpine 2 | 3 | RUN pip install httpx==0.18.2 4 | 5 | 6 | COPY . /action 7 | 8 | ENTRYPOINT ["python", "/action/main.py"] 9 | -------------------------------------------------------------------------------- /.github/bot-action/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Strawberry Bot Action' 2 | inputs: 3 | pr_number: 4 | required: true 5 | status: 6 | required: true 7 | change_type: 8 | required: true 9 | changelog_base64: 10 | required: true 11 | tweet: 12 | required: false 13 | release_card_url: 14 | required: false 15 | runs: 16 | using: 'docker' 17 | image: 'Dockerfile' 18 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: pip 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | groups: 9 | all-dependencies: 10 | patterns: 11 | - "*" 12 | allow: 13 | - dependency-type: direct 14 | -------------------------------------------------------------------------------- /.github/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/.github/logo.png -------------------------------------------------------------------------------- /.github/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.ruff] 2 | extend = "../pyproject.toml" 3 | 4 | [tool.ruff.lint] 5 | extend-ignore = ["T201"] 6 | -------------------------------------------------------------------------------- /.github/release-check-action/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker.io/python:3.8-alpine 2 | 3 | RUN pip install httpx==0.7.* 4 | 5 | COPY . /action 6 | 7 | ENTRYPOINT ["python", "/action/check.py"] 8 | -------------------------------------------------------------------------------- /.github/release-check-action/check.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import pathlib 3 | 4 | from config import GITHUB_WORKSPACE, RELEASE_FILE_PATH 5 | from release import InvalidReleaseFileError, get_release_info 6 | 7 | release_file = pathlib.Path(GITHUB_WORKSPACE) / RELEASE_FILE_PATH 8 | 9 | release_info = None 10 | status = "MISSING" 11 | 12 | if not release_file.exists(): 13 | status = "MISSING" 14 | else: 15 | try: 16 | info = get_release_info(release_file) 17 | release_info = { 18 | "changeType": info.change_type.name, 19 | "changelog": info.changelog, 20 | } 21 | 22 | status = "OK" 23 | except InvalidReleaseFileError: 24 | status = "INVALID" 25 | 26 | 27 | print(f"Status is {status}") 28 | print(f"::set-output name=release_status::{status}") 29 | 30 | if release_info: 31 | changelog = release_info["changelog"] 32 | encoded_changelog = base64.b64encode(changelog.encode("utf-8")).decode("ascii") 33 | 34 | print(f"::set-output name=changelog::{encoded_changelog}") 35 | print(f"::set-output name=change_type::{info.change_type.name}") 36 | else: 37 | print('::set-output name=changelog::""') 38 | -------------------------------------------------------------------------------- /.github/release-check-action/config.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | RELEASE_FILE_PATH = "RELEASE.md" 4 | GITHUB_SHA = os.environ["GITHUB_SHA"] 5 | GITHUB_EVENT_PATH = os.environ["GITHUB_EVENT_PATH"] 6 | GITHUB_WORKSPACE = os.environ["GITHUB_WORKSPACE"] 7 | API_URL = "https://strawberry-bot-r3o3etjz6a-ew.a.run.app/graphql" 8 | -------------------------------------------------------------------------------- /.github/release-check-action/release.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import dataclasses 4 | import re 5 | from enum import Enum 6 | from typing import TYPE_CHECKING 7 | 8 | if TYPE_CHECKING: 9 | from pathlib import Path 10 | 11 | 12 | RELEASE_TYPE_REGEX = re.compile(r"^[Rr]elease [Tt]ype: (major|minor|patch)$") 13 | 14 | 15 | class InvalidReleaseFileError(Exception): 16 | pass 17 | 18 | 19 | class ChangeType(Enum): 20 | MAJOR = "major" 21 | MINOR = "minor" 22 | PATCH = "patch" 23 | 24 | 25 | @dataclasses.dataclass 26 | class ReleaseInfo: 27 | change_type: ChangeType 28 | changelog: str 29 | 30 | 31 | # TODO: remove duplication when we migrate our deployer to GitHub Actions 32 | def get_release_info(file_path: Path) -> ReleaseInfo: 33 | with file_path.open("r") as f: 34 | line = f.readline() 35 | match = RELEASE_TYPE_REGEX.match(line) 36 | 37 | if not match: 38 | raise InvalidReleaseFileError 39 | 40 | change_type_key = match.group(1) 41 | change_type = ChangeType[change_type_key.upper()] 42 | changelog = "".join(f.readlines()).strip() 43 | 44 | return ReleaseInfo(change_type, changelog) 45 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: 🔐 CodeQL 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | schedule: 9 | - cron: '0 18 * * 5' 10 | 11 | jobs: 12 | analyze: 13 | runs-on: ubuntu-latest 14 | 15 | permissions: 16 | security-events: write 17 | actions: read 18 | contents: read 19 | 20 | steps: 21 | - name: Checkout repository 22 | uses: actions/checkout@v3 23 | 24 | - name: Initialize CodeQL 25 | uses: github/codeql-action/init@v2 26 | with: 27 | languages: python 28 | 29 | - name: Perform CodeQL Analysis 30 | uses: github/codeql-action/analyze@v2 31 | -------------------------------------------------------------------------------- /.github/workflows/invite-contributors.yml: -------------------------------------------------------------------------------- 1 | name: 👥 Invite contributors 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | invite-contributor: 10 | name: Invite contributors 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Invite contributors 15 | uses: strawberry-graphql/invite-to-org-action@v4 16 | with: 17 | organisation: "strawberry-graphql" 18 | comment: | 19 | Thanks for contributing to Strawberry! 🎉 You've been invited to join 20 | the Strawberry GraphQL organisation 😊 21 | 22 | You can also request a free sticker by filling this form: https://forms.gle/dmnfQUPoY5gZbVT67 23 | 24 | And don't forget to join our discord server: https://strawberry.rocks/discord 🔥 25 | team-slug: "strawberry-contributors" 26 | github-token: ${{ secrets.BOT_TOKEN }} 27 | -------------------------------------------------------------------------------- /.github/workflows/issue-manager.yml: -------------------------------------------------------------------------------- 1 | name: Issue Manager 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * *" 6 | issue_comment: 7 | types: 8 | - created 9 | issues: 10 | types: 11 | - labeled 12 | pull_request_target: 13 | types: 14 | - labeled 15 | workflow_dispatch: 16 | 17 | jobs: 18 | issue-manager: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: tiangolo/issue-manager@0.5.0 22 | with: 23 | token: ${{ secrets.GITHUB_TOKEN }} 24 | config: > 25 | { 26 | "info-needed": { 27 | "delay": "P14D", 28 | "message": "Hi, this issue requires extra info to be actionable. We're closing this issue because it has not been actionable for a while now. Feel free to provide the requested information and we'll happily open it again! 😊" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/slash-commands.yml: -------------------------------------------------------------------------------- 1 | name: 💬 Slash Command Dispatch 2 | 3 | on: 4 | issue_comment: 5 | types: [created] 6 | 7 | jobs: 8 | slashCommandDispatch: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Slash Command Dispatch 12 | uses: peter-evans/slash-command-dispatch@v2 13 | with: 14 | token: ${{ secrets.BOT_TOKEN }} 15 | reaction-token: ${{ secrets.BOT_TOKEN }} 16 | permission: admin 17 | commands: | 18 | pre-release 19 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | - before: | 3 | curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python - 4 | source $HOME/.poetry/env 5 | pip install pre-commit 6 | init: | 7 | poetry install 8 | pre-commit install --install-hooks 9 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "proseWrap": "always", 3 | "printWidth": 80 4 | } 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Patrick Arminio 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/breaking-changes.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: List of breaking changes and deprecations 3 | --- 4 | 5 | # List of breaking changes and deprecations 6 | 7 | - [Version 0.268.0 - 10 May 2025](./breaking-changes/0.268.0.md) 8 | - [Version 0.249.0 - 18 November 2024](./breaking-changes/0.249.0.md) 9 | - [Version 0.243.0 - 25 September 2024](./breaking-changes/0.243.0.md) 10 | - [Version 0.240.0 - 10 September 2024](./breaking-changes/0.240.0.md) 11 | - [Version 0.236.0 - 17 July 2024](./breaking-changes/0.236.0.md) 12 | - [Version 0.233.0 - 29 May 2024](./breaking-changes/0.233.0.md) 13 | - [Version 0.217.0 - 18 December 2023](./breaking-changes/0.217.0.md) 14 | - [Version 0.213.0 - 8 November 2023](./breaking-changes/0.213.0.md) 15 | - [Version 0.180.0 - 31 May 2023](./breaking-changes/0.180.0.md) 16 | - [Version 0.169.0 - 5 April 2023](./breaking-changes/0.169.0.md) 17 | - [Version 0.159.0 - 22 February 2023](./breaking-changes/0.159.0.md) 18 | - [Version 0.146.0 - 5 December 2022](./breaking-changes/0.146.0.md) 19 | -------------------------------------------------------------------------------- /docs/breaking-changes/0.169.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 0.169.0 Breaking changes 3 | slug: breaking-changes/0.169.0 4 | --- 5 | 6 | # v0.169.0 Introduces a couple of breaking changes in the HTTP integrations 7 | 8 | ## Flask 9 | 10 | Both `get_root_value` and `get_context` now receive the request as a parameter. 11 | 12 | If you're customizing these methods you can change the signature to: 13 | 14 | ```python 15 | def get_root_value(self, request: Request) -> Any: ... 16 | 17 | 18 | def get_context(self, request: Request, response: Response) -> Any: ... 19 | ``` 20 | 21 | The same is true for the async version of the view. 22 | 23 | ## Sanic 24 | 25 | The `get_root_value` method now receives the request as a parameter and it is 26 | async. 27 | 28 | If you're customizing this method you can change the signature to: 29 | 30 | ```python 31 | async def get_root_value(self, request: Request) -> Any: ... 32 | ``` 33 | -------------------------------------------------------------------------------- /docs/breaking-changes/0.180.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 0.180.0 Breaking changes 3 | slug: breaking-changes/0.180.0 4 | --- 5 | 6 | # v0.180.0 introduces a breaking change for the Django Channels HTTP integration 7 | 8 | The context object is now a `dict`. This means that you should access the 9 | context value using the `["key"]` syntax instead of the `.key` syntax. 10 | 11 | For the HTTP integration, there is also no `ws` key anymore and `request` is a 12 | custom request object containing the full request instead of a 13 | `GraphQLHTTPConsumer` instance. If you need to access the `GraphQLHTTPConsumer` 14 | instance in a HTTP connection, you can access it via 15 | `info.context["request"].consumer`. 16 | 17 | For the WebSockets integration, the context keys did not change, e.g. the values 18 | for `info.context["ws"]`, `info.context["request"]` and 19 | `info.context["connection_params"]` are the same as before. 20 | 21 | If you still want to use the `.key` syntax, you can override `get_context()` to 22 | return a custom dataclass there. See the Channels integration documentation for 23 | an example. 24 | -------------------------------------------------------------------------------- /docs/breaking-changes/0.213.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 0.213.0 Deprecation 3 | slug: breaking-changes/0.213.0 4 | --- 5 | 6 | # v0.213.0 introduces a deprecation for `graphiql` parameter 7 | 8 | All HTTP integration now will use `graphql_ide` instead of `graphiql` parameter. 9 | If you're not using the `graphiql` parameter, you can safely ignore this 10 | deprecation. 11 | 12 | If you're using the `graphiql` parameter, you should change it to `graphql_ide` 13 | instead. 14 | 15 | Here's an example of the changes: 16 | 17 | ```diff 18 | -graphql_app = GraphQLRouter(schema, graphiql=True) 19 | +graphql_app = GraphQLRouter(schema, graphql_ide="graphiql") 20 | 21 | -graphql_app = GraphQLRouter(schema, graphiql=False) 22 | +graphql_app = GraphQLRouter(schema, graphql_ide=None) 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/breaking-changes/0.217.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 0.217 Breaking Changes 3 | slug: breaking-changes/0.217.0 4 | --- 5 | 6 | # v0.217.0 changes how kwargs are passed to `has_permission` method 7 | 8 | Previously the `kwargs` argument keys for the `has_permission` method were using 9 | camel casing (depending on your schema configuration), now they will always 10 | follow the python name defined in your resolvers. 11 | 12 | ```python 13 | class IsAuthorized(BasePermission): 14 | message = "User is not authorized" 15 | 16 | def has_permission( 17 | self, source, info, **kwargs: typing.Any 18 | ) -> bool: # pragma: no cover 19 | # kwargs will have a key called "a_key" 20 | # instead of `aKey` 21 | 22 | return False 23 | 24 | 25 | @strawberry.type 26 | class Query: 27 | @strawberry.field(permission_classes=[IsAuthorized]) 28 | def name(self, a_key: str) -> str: # pragma: no cover 29 | return "Erik" 30 | ``` 31 | -------------------------------------------------------------------------------- /docs/breaking-changes/0.233.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 0.233.0 Breaking Changes 3 | slug: breaking-changes/0.233.0 4 | --- 5 | 6 | # v0.233.0 changes the `info` argument in `resolve_reference` in Federation 7 | 8 | In this release we have updated the `info` object passed to the 9 | `resolve_reference` function in Federation to be a `strawberry.Info` object 10 | instead of the one coming from GraphQL-core. 11 | 12 | If you need to access the original `info` object you can do so by accessing the 13 | `_raw_info` attribute. 14 | 15 | ```python 16 | import strawberry 17 | 18 | 19 | @strawberry.federation.type(keys=["upc"]) 20 | class Product: 21 | upc: str 22 | 23 | @classmethod 24 | def resolve_reference(cls, info: strawberry.Info, upc: str) -> "Product": 25 | # Access the original info object 26 | original_info = info._raw_info 27 | 28 | return Product(upc=upc) 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/breaking-changes/0.236.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 0.236.0 Breaking Changes 3 | slug: breaking-changes/0.236.0 4 | --- 5 | 6 | # v0.236.0 changes some of the imports 7 | 8 | This release changes the location of some files in the codebase, this is to make 9 | the codebase more organized and easier to navigate. 10 | 11 | Technically most of these changes should not affect you, but if you were 12 | importing some of the files directly you will need to update the imports. 13 | 14 | We created a codemod to help you with that, feel free to try and submit bugs if 15 | we missed something. 16 | 17 | ```bash 18 | strawberry upgrade update-imports 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/breaking-changes/0.240.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 0.240.0 Breaking Changes 3 | slug: breaking-changes/0.240.0 4 | --- 5 | 6 | # v0.240.0 updates `Schema.subscribe`'s signature 7 | 8 | In order to support schema extensions in subscriptions and errors that can be 9 | raised before the execution of the subscription, we had to update the signature 10 | of `Schema.subscribe`. 11 | 12 | Previously it was: 13 | 14 | ```python 15 | async def subscribe( 16 | self, 17 | query: str, 18 | variable_values: Optional[Dict[str, Any]] = None, 19 | context_value: Optional[Any] = None, 20 | root_value: Optional[Any] = None, 21 | operation_name: Optional[str] = None, 22 | ) -> Union[AsyncIterator[GraphQLExecutionResult], GraphQLExecutionResult]: 23 | ``` 24 | 25 | Now it is: 26 | 27 | ```python 28 | async def subscribe( 29 | self, 30 | query: Optional[str], 31 | variable_values: Optional[Dict[str, Any]] = None, 32 | context_value: Optional[Any] = None, 33 | root_value: Optional[Any] = None, 34 | operation_name: Optional[str] = None, 35 | ) -> Union[AsyncGenerator[ExecutionResult, None], PreExecutionError]: 36 | ``` 37 | -------------------------------------------------------------------------------- /docs/breaking-changes/0.249.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 0.249.0 Breaking Changes 3 | slug: breaking-changes/0.249.0 4 | --- 5 | 6 | # v0.249.0 Breaking Changes 7 | 8 | After a year-long deprecation period, the `SentryTracingExtension` has been 9 | removed in favor of the official Sentry SDK integration. 10 | 11 | To migrate, remove the `SentryTracingExtension` from your Strawberry schema and 12 | then follow the 13 | [official Sentry SDK integration guide](https://docs.sentry.io/platforms/python/integrations/strawberry/). 14 | -------------------------------------------------------------------------------- /docs/breaking-changes/0.251.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 0.251.0 Breaking Changes 3 | slug: breaking-changes/0.251.0 4 | --- 5 | 6 | # v0.251.0 Breaking Changes 7 | 8 | We slightly changed the signature of the `encode_json` method used to customize 9 | the JSON encoder used by our HTTP views. 10 | 11 | Originally, the method was only meant to encode HTTP response data. Starting 12 | with this release, it's also used to encode WebSocket messages. 13 | 14 | Previously, the method signature was: 15 | 16 | ```python 17 | def encode_json(self, response_data: GraphQLHTTPResponse) -> str: ... 18 | ``` 19 | 20 | To upgrade your code, change the method signature to the following and make sure 21 | your method can handle the same inputs as the built-in `json.dumps` method: 22 | 23 | ```python 24 | def encode_json(self, data: object) -> str: ... 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/codegen/schema-codegen.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Schema codegen 3 | --- 4 | 5 | # Schema codegen 6 | 7 | Strawberry supports code generation from SDL files. 8 | 9 | Let's assume we have the following SDL file: 10 | 11 | ```graphql 12 | type Query { 13 | user: User 14 | } 15 | 16 | type User { 17 | id: ID! 18 | name: String! 19 | } 20 | ``` 21 | 22 | by running the following command: 23 | 24 | ```shell 25 | strawberry schema-codegen schema.graphql 26 | ``` 27 | 28 | we'll get the following output: 29 | 30 | ```python 31 | import strawberry 32 | 33 | 34 | @strawberry.type 35 | class Query: 36 | user: User | None 37 | 38 | 39 | @strawberry.type 40 | class User: 41 | id: strawberry.ID 42 | name: str 43 | 44 | 45 | schema = strawberry.Schema(query=Query) 46 | ``` 47 | -------------------------------------------------------------------------------- /docs/concepts/async.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Async 3 | --- 4 | 5 | # Async 6 | 7 | Async is a concurrent programming design that has been supported in Python since 8 | version 3.4. To learn more about async in Python refer to 9 | [Real Python’s Async walkthrough](https://realpython.com/async-io-python/). 10 | 11 | Strawberry supports both async and non async resolvers, so you can mix and match 12 | them in your code. Here’s an example of an async resolver: 13 | 14 | ```python 15 | import asyncio 16 | import strawberry 17 | 18 | 19 | async def resolve_hello(root) -> str: 20 | await asyncio.sleep(1) 21 | 22 | return "Hello world" 23 | 24 | 25 | @strawberry.type 26 | class Query: 27 | hello: str = strawberry.field(resolver=resolve_hello) 28 | 29 | 30 | schema = strawberry.Schema(Query) 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/editors/mypy.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Mypy 3 | --- 4 | 5 | # Mypy 6 | 7 | Strawberry comes with support for 8 | [Mypy](https://mypy.readthedocs.io/en/stable/), a popular static type checker 9 | for Python. 10 | 11 | This guide will explain how to configure Mypy to work with Strawberry. 12 | 13 | ## Install Mypy 14 | 15 | The first thing we need to do is to install 16 | [Mypy](https://mypy.readthedocs.io/en/stable/), this is the tool that will 17 | perform the type checking. 18 | 19 | Once the tool is installed, we need to configure it to enable type checking and 20 | use the Strawberry plugin. To do so we need to create a `mypy.ini` file in the 21 | root of our project and add the following settings: 22 | 23 | ```ini 24 | [mypy] 25 | plugins = strawberry.ext.mypy_plugin 26 | ``` 27 | 28 | You can also configure Mypy inside the `pyproject.toml` file, like so: 29 | 30 | ```toml 31 | [tool.mypy] 32 | plugins = ["strawberry.ext.mypy_plugin"] 33 | ``` 34 | 35 | Once you have configured the settings, you can run `mypy` and you should be 36 | getting type checking errors. 37 | -------------------------------------------------------------------------------- /docs/editors/pylance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/docs/editors/pylance.png -------------------------------------------------------------------------------- /docs/errors/_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Some Error 3 | --- 4 | 5 | # Some Error Error 6 | 7 | ## Description 8 | 9 | This error is thrown when ... for example the following code will throw this 10 | error: 11 | 12 | ```python 13 | import strawberry 14 | 15 | schema = strawberry.Schema(query=Query) 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/errors/conflicting-arguments.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Conflicting Arguments Error 3 | --- 4 | 5 | # Conflicting Arguments Error 6 | 7 | ## Description 8 | 9 | This error is thrown when you define a resolver with multiple arguments that 10 | conflict with each other, like "self", "root", or any arguments annotated with 11 | strawberry.Parent. 12 | 13 | For example the following code will throw this error: 14 | 15 | ```python 16 | import strawberry 17 | 18 | 19 | @strawberry.type 20 | class Query: 21 | @strawberry.field 22 | def hello( 23 | self, root, parent: strawberry.Parent[str] 24 | ) -> str: # <-- self, root, and parent all identify the same input 25 | return f"hello world" 26 | ``` 27 | -------------------------------------------------------------------------------- /docs/errors/invalid-argument-type.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Invalid Argument Type Error 3 | --- 4 | 5 | # Invalid Argument Type Error 6 | 7 | ## Description 8 | 9 | This error is thrown when an argument is of the wrong type, it usually happens 10 | when passing **unions** or **interfaces** as an argument, for example the 11 | following code will throw this error: 12 | 13 | ```python 14 | import strawberry 15 | 16 | from typing import Union, Annotated 17 | 18 | 19 | @strawberry.type 20 | class TypeA: 21 | id: strawberry.ID 22 | 23 | 24 | ExampleUnion = Annotated[Union[TypeA], strawberry.union("ExampleUnion")] 25 | 26 | 27 | @strawberry.type 28 | class Query: 29 | @strawberry.field 30 | def example(self, data: Example) -> str: 31 | return "this is an example" 32 | 33 | 34 | schema = strawberry.Schema(query=Query) 35 | ``` 36 | 37 | ## Using union types as arguments 38 | 39 | The latest [GraphQL specification](https://spec.graphql.org/October2021/) 40 | doesn't allow using unions as arguments. There's currently an 41 | [RFC for adding a `oneOf` directive](https://github.com/graphql/graphql-spec/pull/825) 42 | that might work for your use case, but it's not yet implemented in the spec and 43 | Strawberry 44 | -------------------------------------------------------------------------------- /docs/errors/missing-arguments-annotations.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Missing arguments annotation Error 3 | --- 4 | 5 | # Missing arguments annotation Error 6 | 7 | ## Description 8 | 9 | This error is thrown when an argument is missing an annotation, for example the 10 | following code will throw this error: 11 | 12 | ```python 13 | import strawberry 14 | 15 | 16 | @strawberry.type 17 | class Query: 18 | @strawberry.field 19 | def hello(self, name) -> str: # <-- note name here is missing an annotation 20 | return f"hello {name}" 21 | 22 | 23 | schema = strawberry.Schema(query=Query) 24 | ``` 25 | 26 | This happens because Strawberry needs to know the type of every argument to be 27 | able to generate the correct GraphQL type. 28 | 29 | ## How to fix this error 30 | 31 | You can fix this error by adding an annotation to the argument, for example, the 32 | following code will fix this error: 33 | 34 | ```python 35 | import strawberry 36 | 37 | 38 | @strawberry.type 39 | class Query: 40 | @strawberry.field 41 | def hello(self, name: str) -> str: 42 | return f"hello {name}" 43 | 44 | 45 | schema = strawberry.Schema(query=Query) 46 | ``` 47 | -------------------------------------------------------------------------------- /docs/errors/missing-field-annotation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Missing field annotation Error 3 | --- 4 | 5 | # Missing field annotation Error 6 | 7 | ## Description 8 | 9 | This error is thrown when a field on a class is missing an annotation, for 10 | example the following code will throw this error: 11 | 12 | ```python 13 | import strawberry 14 | 15 | 16 | @strawberry.type 17 | class Query: 18 | name: str 19 | age = strawberry.field( 20 | name="ageInYears" 21 | ) # note that here we don't have a type for this field 22 | 23 | 24 | schema = strawberry.Schema(query=Query) 25 | ``` 26 | 27 | This happens because Strawberry needs to know the type of every field for a type 28 | to be able to generate the correct GraphQL type. 29 | 30 | ## How to fix this error 31 | 32 | You can fix this error by adding an annotation to the field, for example, the 33 | following code will fix this error: 34 | 35 | ```python 36 | import strawberry 37 | 38 | 39 | @strawberry.type 40 | class Query: 41 | name: str 42 | age: int = strawberry.field(name="ageInYears") 43 | 44 | 45 | schema = strawberry.Schema(query=Query) 46 | ``` 47 | -------------------------------------------------------------------------------- /docs/errors/missing-return-annotation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Missing return annotation Error 3 | --- 4 | 5 | # Missing return annotation Error 6 | 7 | ## Description 8 | 9 | This error is thrown when a resolver and it's corresponding field don't have a 10 | return annotation, for example the following code will throw this error: 11 | 12 | ```python 13 | import strawberry 14 | 15 | 16 | @strawberry.type 17 | class Query: 18 | @strawberry.field 19 | def example(self): 20 | return "this is an example" 21 | 22 | 23 | schema = strawberry.Schema(query=Query) 24 | ``` 25 | 26 | This happens because Strawberry needs to know the return type of the resolver to 27 | be able to generate the correct GraphQL type. 28 | 29 | ## How to fix this error 30 | 31 | You can fix this error by adding a return annotation to the resolver, for 32 | example, the following code will fix this error: 33 | 34 | ```python 35 | import strawberry 36 | 37 | 38 | @strawberry.type 39 | class Query: 40 | @strawberry.field 41 | def example(self) -> str: 42 | return "this is an example" 43 | 44 | 45 | schema = strawberry.Schema(query=Query) 46 | ``` 47 | -------------------------------------------------------------------------------- /docs/errors/object-is-not-an-enum.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Object is not an Enum Error 3 | --- 4 | 5 | # Object is not an Enum Error 6 | 7 | ## Description 8 | 9 | This error is thrown when applying `@strawberry.enum` to a non-enum object, for 10 | example the following code will throw this error: 11 | 12 | ```python 13 | import strawberry 14 | 15 | 16 | # note the lack of @strawberry.enum here: 17 | class NotAnEnum: 18 | A = "A" 19 | 20 | 21 | @strawberry.type 22 | class Query: 23 | field: NotAnEnum 24 | 25 | 26 | schema = strawberry.Schema(query=Query) 27 | ``` 28 | 29 | This happens because Strawberry expects all enums to be subclasses of `Enum`. 30 | 31 | ## How to fix this error 32 | 33 | You can fix this error by making sure the class you're applying 34 | `@strawberry.enum` to is a subclass of `Enum`. For example, the following code 35 | will fix this error: 36 | 37 | ```python 38 | import strawberry 39 | 40 | 41 | @strawberry.enum 42 | class NotAnEnum: 43 | A = "A" 44 | 45 | 46 | @strawberry.type 47 | class Query: 48 | field: NotAnEnum 49 | 50 | 51 | schema = strawberry.Schema(query=Query) 52 | ``` 53 | -------------------------------------------------------------------------------- /docs/errors/object-is-not-class.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Object is not an Class Error 3 | --- 4 | 5 | # Object is not an Class Error 6 | 7 | ## Description 8 | 9 | This error is thrown when applying `@strawberry.type/interface/input` to a 10 | non-class object, for example the following code will throw this error: 11 | 12 | ```python 13 | import strawberry 14 | 15 | 16 | @strawberry.type 17 | def a_function(): ... 18 | 19 | 20 | @strawberry.type 21 | class Query: 22 | field: a_function 23 | 24 | 25 | schema = strawberry.Schema(query=Query) 26 | ``` 27 | 28 | This happens because Strawberry expects all enums to be subclasses of `Enum`. 29 | 30 | ## How to fix this error 31 | 32 | You can fix this error by making sure the class you're applying 33 | `@strawberry.type/interface/input` to is a class. For example, the following 34 | code will fix this error: 35 | 36 | ```python 37 | import strawberry 38 | 39 | 40 | @strawberry.type 41 | class AFunction: 42 | field: int 43 | 44 | 45 | @strawberry.type 46 | class Query: 47 | field: AFunction 48 | 49 | 50 | schema = strawberry.Schema(query=Query) 51 | ``` 52 | -------------------------------------------------------------------------------- /docs/errors/unsupported-type.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Unsupported Type Error 3 | --- 4 | 5 | # Unsupported Type Error 6 | 7 | ## Description 8 | 9 | This error is thrown when trying to convert arguments with a type that 10 | Strawberry doesn't know about. It shouldn't happen with normal usage of 11 | Strawberry. 12 | -------------------------------------------------------------------------------- /docs/extensions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Extensions 3 | --- 4 | 5 | # Extensions 6 | 7 | Extensions allow you, as an application developer, to customise the GraphQL 8 | execution flow based on your needs. Strawberry provides multiple built in 9 | extensions that allow you extend the capability of your GraphQL server. 10 | 11 | If you can't find what you need here you can also build your own custom 12 | extension based on a standard interface. More details 13 | [here](/docs/guides/custom-extensions). 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/extensions/_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Extension Name 3 | summary: A summary of the extension. 4 | tags: comma,separated,list,of,tags 5 | --- 6 | 7 | # `ExtensionName` 8 | 9 | This extension does ... 10 | 11 | ## Usage example: 12 | 13 | ```python 14 | import strawberry 15 | from strawberry.extensions import ExtensionName 16 | 17 | schema = strawberry.Schema( 18 | Query, 19 | extensions=[ 20 | ExtensionName(), 21 | ], 22 | ) 23 | ``` 24 | 25 | ## API reference: 26 | 27 | ```python 28 | class ExtensionName(an_argument=None): ... 29 | ``` 30 | 31 | #### `an_argument: Optional[str] = None` 32 | 33 | Description of the argument. 34 | 35 | ## More examples: 36 | 37 |
38 | Using `an_argument` 39 | 40 | ```python 41 | import strawberry 42 | from strawberry.extensions import ValidationCache 43 | 44 | schema = strawberry.Schema( 45 | Query, 46 | extensions=[ 47 | ExtensionName(an_argument="something"), 48 | ], 49 | ) 50 | ``` 51 | 52 |
53 | -------------------------------------------------------------------------------- /docs/extensions/apollo-tracing.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Apollo Tracing 3 | summary: Add Apollo tracing to your GraphQL server. 4 | tags: tracing 5 | --- 6 | 7 | # `ApolloTracingExtension` 8 | 9 | This extension adds 10 | [tracing information](https://github.com/apollographql/apollo-tracing) to your 11 | response for [Apollo Engine](https://www.apollographql.com/platform/). 12 | 13 | ## Usage example: 14 | 15 | ```python 16 | import strawberry 17 | from strawberry.extensions.tracing import ApolloTracingExtension 18 | 19 | schema = strawberry.Schema( 20 | Query, 21 | extensions=[ 22 | ApolloTracingExtension, 23 | ], 24 | ) 25 | ``` 26 | 27 | 28 | 29 | If you are not running in an Async context then you'll need to use the sync 30 | version: 31 | 32 | ```python 33 | import strawberry 34 | from strawberry.extensions.tracing import ApolloTracingExtensionSync 35 | 36 | schema = strawberry.Schema( 37 | Query, 38 | extensions=[ 39 | ApolloTracingExtensionSync, 40 | ], 41 | ) 42 | ``` 43 | 44 | 45 | 46 | ## API reference: 47 | 48 | _No arguments_ 49 | -------------------------------------------------------------------------------- /docs/extensions/disable-validation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Disable Validation 3 | summary: Disable all query validation. 4 | tags: performance,validation 5 | --- 6 | 7 | # `DisableValidation` 8 | 9 | This extensions disables all query validation. This can be useful to improve 10 | performance in some specific cases, for example when dealing with internal APIs 11 | where queries can be trusted. 12 | 13 | 14 | 15 | Only do this if you know what you are doing! Disabling validation breaks the 16 | safety of having typed schema. If you are trying to improve performance you 17 | might want to consider using the [ValidationCache](./validation-cache) instead. 18 | 19 | 20 | 21 | ## Usage example: 22 | 23 | ```python 24 | import strawberry 25 | from strawberry.extensions import DisableValidation 26 | 27 | schema = strawberry.Schema( 28 | Query, 29 | extensions=[ 30 | DisableValidation(), 31 | ], 32 | ) 33 | ``` 34 | 35 | ## API reference: 36 | 37 | _No arguments_ 38 | -------------------------------------------------------------------------------- /docs/extensions/max-aliases-limiter.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Max Aliases Limiter 3 | summary: 4 | Add a validator to limit the maximum number of aliases in a GraphQL document. 5 | tags: security 6 | --- 7 | 8 | # `MaxAliasesLimiter` 9 | 10 | This extension adds a validator to limit the maximum number of aliases in a 11 | GraphQL document. 12 | 13 | ## Usage example: 14 | 15 | ```python 16 | import strawberry 17 | from strawberry.extensions import MaxAliasesLimiter 18 | 19 | schema = strawberry.Schema( 20 | Query, 21 | extensions=[ 22 | MaxAliasesLimiter(max_alias_count=15), 23 | ], 24 | ) 25 | ``` 26 | 27 | ## API reference: 28 | 29 | ```python 30 | class MaxAliasesLimiter(max_alias_count): ... 31 | ``` 32 | 33 | #### `max_alias_count: int` 34 | 35 | The maximum allowed number of aliases in a GraphQL document. 36 | -------------------------------------------------------------------------------- /docs/extensions/max-tokens-limiter.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Max Tokens Limiter 3 | summary: 4 | Add a validator to limit the maximum number of tokens in a GraphQL document. 5 | tags: security 6 | --- 7 | 8 | # `MaxTokensLimiter` 9 | 10 | This extension adds a validator to limit the maximum number of tokens in a 11 | GraphQL document sent to the server. 12 | 13 | ## Usage example: 14 | 15 | ```python 16 | import strawberry 17 | from strawberry.extensions import MaxTokensLimiter 18 | 19 | schema = strawberry.Schema( 20 | Query, 21 | extensions=[ 22 | MaxTokensLimiter(max_token_count=1000), 23 | ], 24 | ) 25 | ``` 26 | 27 | With the above configuration, if a client sends a query with more than 1000 28 | tokens, the server will respond with an error message. 29 | 30 | ## API reference: 31 | 32 | ```python 33 | class MaxTokensLimiter(max_token_count): ... 34 | ``` 35 | 36 | #### `max_token_count: int` 37 | 38 | The maximum allowed number of tokens in a GraphQL document. 39 | 40 | The following things are counted as tokens: 41 | 42 | - various brackets: "{", "}", "(", ")" 43 | - colon : 44 | - words 45 | 46 | Not counted: 47 | 48 | - quotes 49 | -------------------------------------------------------------------------------- /docs/extensions/pyinstrument.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PyInstrument 3 | summary: Easily Instrument your Schema 4 | tags: instrumentation,profiling 5 | --- 6 | 7 | # `PyInstrument` 8 | 9 | This extension allows you to instrument your schema and inspect the call stack. 10 | 11 | ## Usage example: 12 | 13 | ```python 14 | import strawberry 15 | from strawberry.extensions import pyinstrument 16 | 17 | schema = strawberry.Schema( 18 | Query, 19 | extensions=[ 20 | pyinstrument.PyInstrument(report_path="pyinstrument.html"), 21 | ], 22 | ) 23 | ``` 24 | 25 | ## API reference: 26 | 27 | ```python 28 | class PyInstrument(report_Path=Path("pyinstrument.html")): ... 29 | ``` 30 | 31 | #### `report_path: Path = Path("pyinstrument.html")` 32 | 33 | Path to write the HTML PyInstrument report 34 | -------------------------------------------------------------------------------- /docs/extensions/sentry-tracing.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Sentry Tracing 3 | summary: Add Sentry tracing to your GraphQL server. 4 | tags: tracing 5 | --- 6 | 7 | # `SentryTracingExtension` 8 | 9 | 10 | 11 | As of Sentry 1.32.0, Strawberry is officially supported by the Sentry SDK. 12 | Therefore, Strawberry's `SentryTracingExtension` has been deprecated in version 13 | 0.210.0 and finally removed with Strawberry 0.249.0 in favor of the official 14 | Sentry SDK integration. 15 | 16 | For more details, please refer to the 17 | [documentation for the official Sentry Strawberry integration](https://docs.sentry.io/platforms/python/integrations/strawberry/). 18 | 19 | 20 | -------------------------------------------------------------------------------- /docs/federation/custom_directives.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Exposing directives on the supergraph (Apollo Federation) 3 | --- 4 | 5 | # Exposing directives on the supergraph (Apollo Federation) 6 | 7 | By default (most) 8 | [schema directives are hidden from the supergraph schema](https://www.apollographql.com/docs/federation/federated-types/federated-directives/#composedirective). 9 | If you need to expose a directive to the supergraph, you can use the `compose` 10 | parameter on the `@strawberry.federation.schema_directives` decorator, here's an 11 | example: 12 | 13 | ```python 14 | import strawberry 15 | 16 | 17 | @strawberry.federation.schema_directive( 18 | locations=[Location.OBJECT], name="cacheControl", compose=True 19 | ) 20 | class CacheControl: 21 | max_age: int 22 | ``` 23 | 24 | This will create a `cacheControl` directive and it will also use 25 | [`@composeDirective`](https://www.apollographql.com/docs/federation/federated-types/federated-directives/#composedirective) 26 | on the schema to make sure it is included in the supergraph schema. 27 | -------------------------------------------------------------------------------- /docs/federation/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Apollo Federation 3 | --- 4 | 5 | # Apollo Federation 6 | 7 | Strawberry supports 8 | [Apollo Federation](https://www.apollographql.com/docs/federation/) out of the 9 | box, that means that you can create services using Strawberry and federate them 10 | via Apollo Gateway or Apollo Router. 11 | 12 | Strawberry is a schema first library, to use Apollo Federation you need to add 13 | directives to your schema, types and fields. Strawberry has built support for 14 | directives, but it also provide shortcuts for Apollo Federation. 15 | 16 | All shortcuts live under the `strawberry.federation` module. For example if you 17 | want to create an 18 | [Entity](https://www.apollographql.com/docs/federation/entities) you can do: 19 | 20 | ```python 21 | @strawberry.federation.type(keys=["id"]) 22 | class Book: 23 | id: strawberry.ID 24 | title: str 25 | ``` 26 | 27 | And strawberry will automatically add the right directives to the type and 28 | schema. 29 | 30 | # Getting started 31 | 32 | If you want to get started with Apollo Federation, you can use our 33 | [Apollo Federation guide](../guides/federation.md). 34 | -------------------------------------------------------------------------------- /docs/general/multipart-subscriptions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Multipart subscriptions 3 | --- 4 | 5 | # Multipart subscriptions 6 | 7 | Strawberry supports subscription over multipart responses. This is an 8 | [alternative protocol](https://www.apollographql.com/docs/router/executing-operations/subscription-multipart-protocol/) 9 | created by [Apollo](https://www.apollographql.com/) to support subscriptions 10 | over HTTP, and it is supported by default by Apollo Client. 11 | 12 | # Support 13 | 14 | We support multipart subscriptions out of the box in the following HTTP 15 | libraries: 16 | 17 | - Django (only in the Async view) 18 | - ASGI 19 | - Litestar 20 | - FastAPI 21 | - AioHTTP 22 | - Quart 23 | 24 | # Usage 25 | 26 | Multipart subscriptions are automatically enabled when using Subscription, so no 27 | additional configuration is required. 28 | -------------------------------------------------------------------------------- /docs/general/upgrades.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Upgrading Strawberry 3 | --- 4 | 5 | # Upgrading Strawberry 6 | 7 | 8 | 9 | We try to keep Strawberry as backwards compatible as possible, but sometimes we 10 | need to make updates to the public API. While we try to deprecate APIs before 11 | removing them, we also want to make it as easy as possible to upgrade to the 12 | latest version of Strawberry. 13 | 14 | For this reason we provide a CLI command that can automatically upgrade your 15 | codebase to use the updated APIs. 16 | 17 | At the moment we only support updating unions to use the new syntax with 18 | annotated, but in future we plan to add more commands to help with upgrading. 19 | 20 | Here's how you can use the command to upgrade your codebase: 21 | 22 | ```shell 23 | strawberry upgrade annotated-union . 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/guides/convert-to-dictionary.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Convert to Dictionary 3 | --- 4 | 5 | # Convert to Dictionary 6 | 7 | Strawberry provides a utility function to convert a Strawberry object to a 8 | dictionary. 9 | 10 | You can use `strawberry.asdict(...)` function: 11 | 12 | ```python 13 | @strawberry.type 14 | class User: 15 | name: str 16 | age: int 17 | 18 | 19 | # should be {"name": "Lorem", "age": 25} 20 | user_dict = strawberry.asdict(User(name="Lorem", age=25)) 21 | ``` 22 | -------------------------------------------------------------------------------- /docs/guides/schema-export.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Schema export 3 | --- 4 | 5 | # Schema export 6 | 7 | Sometimes IDE plugins and code generation tools require you to provide a GraphQL 8 | schema definition. 9 | 10 | Strawberry provides a command to export your schema definition. The exported 11 | schema will be described in the GraphQL schema definition language (SDL). 12 | 13 | To use the command line tools, you have to ensure Strawberry was installed with 14 | `strawberry-graphql[cli]`. 15 | 16 | You can export your schema using the following command: 17 | 18 | ```bash 19 | strawberry export-schema package.module:schema 20 | ``` 21 | 22 | where `schema` is the name of a Strawberry schema symbol or a callable symbol 23 | that returns a Strawberry schema and `package.module` is the qualified name of 24 | the module containing the symbol. The symbol name defaults to `schema` if not 25 | specified. 26 | 27 | In order to store the exported schema in a file, pipes or redirection can be 28 | utilized: 29 | 30 | ```bash 31 | strawberry export-schema package.module:schema > schema.graphql 32 | ``` 33 | 34 | Alternatively, the `--output` option can be used: 35 | 36 | ```bash 37 | strawberry export-schema package.module:schema --output schema.graphql 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/images/index-query-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/docs/images/index-query-example.png -------------------------------------------------------------------------------- /docs/images/index-server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/docs/images/index-server.png -------------------------------------------------------------------------------- /docs/images/subscriptions-count-websocket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/docs/images/subscriptions-count-websocket.png -------------------------------------------------------------------------------- /docs/integrations/starlette.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Starlette 3 | --- 4 | 5 | # Starlette 6 | 7 | Strawberry provides support for [Starlette](https://www.starlette.io/) with the 8 | ASGI integration. 9 | 10 | See below example for integrating Starlette with Strawberry: 11 | 12 | ```python 13 | from starlette.applications import Starlette 14 | from strawberry.asgi import GraphQL 15 | 16 | from api.schema import schema 17 | 18 | graphql_app = GraphQL(schema) 19 | 20 | app = Starlette() 21 | app.add_route("/graphql", graphql_app) 22 | app.add_websocket_route("/graphql", graphql_app) 23 | ``` 24 | 25 | For more information about Strawberry ASGI refer to 26 | [the documentation on ASGI](./asgi.md) 27 | -------------------------------------------------------------------------------- /federation-compatibility/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10-slim 2 | WORKDIR /web 3 | 4 | RUN apt update && apt install -y gcc python3-dev 5 | RUN pip install poetry 6 | 7 | COPY strawberry ./strawberry 8 | COPY pyproject.toml ./ 9 | COPY poetry.lock ./ 10 | COPY README.md ./ 11 | 12 | RUN poetry install --with integrations 13 | 14 | COPY federation-compatibility/schema.py ./ 15 | 16 | EXPOSE 4001 17 | 18 | CMD poetry run strawberry server -p 4001 -h 0.0.0.0 schema:schema 19 | -------------------------------------------------------------------------------- /federation-compatibility/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | products: 3 | build: 4 | context: . 5 | dockerfile: federation-compatibility/Dockerfile 6 | ports: 7 | - 4001:4001 8 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # we use poetry for our build, but this file seems to be required 4 | # in order to get GitHub dependencies graph to work 5 | 6 | import setuptools 7 | 8 | if __name__ == "__main__": 9 | setuptools.setup(name="strawberry-graphql") 10 | -------------------------------------------------------------------------------- /strawberry/__main__.py: -------------------------------------------------------------------------------- 1 | from .cli import run 2 | 3 | if __name__ == "__main__": 4 | run() 5 | -------------------------------------------------------------------------------- /strawberry/aiohttp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/aiohttp/__init__.py -------------------------------------------------------------------------------- /strawberry/aiohttp/test/__init__.py: -------------------------------------------------------------------------------- 1 | from .client import GraphQLTestClient 2 | 3 | __all__ = ["GraphQLTestClient"] 4 | -------------------------------------------------------------------------------- /strawberry/asgi/test/__init__.py: -------------------------------------------------------------------------------- 1 | from .client import GraphQLTestClient 2 | 3 | __all__ = ["GraphQLTestClient"] 4 | -------------------------------------------------------------------------------- /strawberry/chalice/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/chalice/__init__.py -------------------------------------------------------------------------------- /strawberry/channels/__init__.py: -------------------------------------------------------------------------------- 1 | from .handlers.base import ChannelsConsumer 2 | from .handlers.http_handler import ( 3 | ChannelsRequest, 4 | GraphQLHTTPConsumer, 5 | SyncGraphQLHTTPConsumer, 6 | ) 7 | from .handlers.ws_handler import GraphQLWSConsumer 8 | from .router import GraphQLProtocolTypeRouter 9 | 10 | __all__ = [ 11 | "ChannelsConsumer", 12 | "ChannelsRequest", 13 | "GraphQLHTTPConsumer", 14 | "GraphQLProtocolTypeRouter", 15 | "GraphQLWSConsumer", 16 | "SyncGraphQLHTTPConsumer", 17 | ] 18 | -------------------------------------------------------------------------------- /strawberry/channels/handlers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/channels/handlers/__init__.py -------------------------------------------------------------------------------- /strawberry/cli/__init__.py: -------------------------------------------------------------------------------- 1 | try: 2 | from .app import app 3 | from .commands.codegen import codegen as codegen # noqa: PLC0414 4 | from .commands.export_schema import export_schema as export_schema # noqa: PLC0414 5 | from .commands.schema_codegen import ( 6 | schema_codegen as schema_codegen, # noqa: PLC0414 7 | ) 8 | from .commands.server import server as server # noqa: PLC0414 9 | from .commands.upgrade import upgrade as upgrade # noqa: PLC0414 10 | 11 | def run() -> None: 12 | app() 13 | 14 | except ModuleNotFoundError as exc: 15 | from strawberry.exceptions import MissingOptionalDependenciesError 16 | 17 | raise MissingOptionalDependenciesError(extras=["cli"]) from exc 18 | -------------------------------------------------------------------------------- /strawberry/cli/app.py: -------------------------------------------------------------------------------- 1 | import typer 2 | 3 | app = typer.Typer(no_args_is_help=True) 4 | -------------------------------------------------------------------------------- /strawberry/cli/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/cli/commands/__init__.py -------------------------------------------------------------------------------- /strawberry/cli/commands/export_schema.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import typer 4 | 5 | from strawberry.cli.app import app 6 | from strawberry.cli.utils import load_schema 7 | from strawberry.printer import print_schema 8 | 9 | 10 | @app.command(help="Exports the schema") 11 | def export_schema( 12 | schema: str, 13 | app_dir: str = typer.Option( 14 | ".", 15 | "--app-dir", 16 | show_default=True, 17 | help=( 18 | "Look for the module in the specified directory, by adding this to the " 19 | "PYTHONPATH. Defaults to the current working directory. " 20 | "Works the same as `--app-dir` in uvicorn." 21 | ), 22 | ), 23 | output: Path = typer.Option( 24 | None, 25 | "--output", 26 | "-o", 27 | help="File to save the exported schema. If not provided, prints to console.", 28 | ), 29 | ) -> None: 30 | schema_symbol = load_schema(schema, app_dir) 31 | 32 | schema_text = print_schema(schema_symbol) 33 | 34 | if output: 35 | Path(output).write_text(schema_text) 36 | typer.echo(f"Schema exported to {output}") 37 | else: 38 | print(schema_text) # noqa: T201 39 | -------------------------------------------------------------------------------- /strawberry/cli/commands/schema_codegen.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from typing import Optional 3 | 4 | import typer 5 | 6 | from strawberry.cli.app import app 7 | from strawberry.schema_codegen import codegen 8 | 9 | 10 | @app.command(help="Generate code from a query") 11 | def schema_codegen( 12 | schema: Path = typer.Argument(exists=True), 13 | output: Optional[Path] = typer.Option( 14 | None, 15 | "-o", 16 | "--output", 17 | file_okay=True, 18 | dir_okay=False, 19 | writable=True, 20 | resolve_path=True, 21 | ), 22 | ) -> None: 23 | generated_output = codegen(schema.read_text()) 24 | 25 | if output is None: 26 | typer.echo(generated_output) 27 | return 28 | 29 | output.parent.mkdir(parents=True, exist_ok=True) 30 | output.write_text(generated_output) 31 | 32 | typer.echo(f"Code generated at `{output.name}`") 33 | -------------------------------------------------------------------------------- /strawberry/cli/commands/upgrade/_fake_progress.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from rich.progress import TaskID 4 | 5 | 6 | class FakeProgress: 7 | """A fake progress bar that does nothing. 8 | 9 | This is used when the user has only one file to process. 10 | """ 11 | 12 | def advance(self, task_id: TaskID) -> None: 13 | pass 14 | 15 | def add_task(self, *args: Any, **kwargs: Any) -> TaskID: 16 | return TaskID(0) 17 | 18 | def __enter__(self) -> "FakeProgress": 19 | return self 20 | 21 | def __exit__(self, *args: object, **kwargs: Any) -> None: 22 | pass 23 | -------------------------------------------------------------------------------- /strawberry/cli/constants.py: -------------------------------------------------------------------------------- 1 | DEBUG_SERVER_SCHEMA_ENV_VAR_KEY = "STRAWBERRY_DEBUG_SERVER_SCHEMA" 2 | DEBUG_SERVER_LOG_OPERATIONS = "STRAWBERRY_DEBUG_SERVER_LOG_OPERATIONS" 3 | -------------------------------------------------------------------------------- /strawberry/cli/debug_server.py: -------------------------------------------------------------------------------- 1 | import os 2 | from typing import Any 3 | 4 | from starlette.applications import Starlette 5 | from starlette.middleware.cors import CORSMiddleware 6 | 7 | from strawberry import Schema 8 | from strawberry.asgi import GraphQL 9 | from strawberry.cli.constants import ( 10 | DEBUG_SERVER_LOG_OPERATIONS, 11 | DEBUG_SERVER_SCHEMA_ENV_VAR_KEY, 12 | ) 13 | from strawberry.utils.importer import import_module_symbol 14 | 15 | app = Starlette(debug=True) 16 | app.add_middleware( 17 | CORSMiddleware, allow_headers=["*"], allow_origins=["*"], allow_methods=["*"] 18 | ) 19 | 20 | schema_import_string = os.environ[DEBUG_SERVER_SCHEMA_ENV_VAR_KEY] 21 | schema_symbol = import_module_symbol(schema_import_string, default_symbol_name="schema") 22 | log_operations = os.environ[DEBUG_SERVER_LOG_OPERATIONS] == "True" 23 | 24 | assert isinstance(schema_symbol, Schema) 25 | graphql_app = GraphQL[Any, Any](schema_symbol, debug=log_operations) 26 | 27 | paths = ["/", "/graphql"] 28 | for path in paths: 29 | app.add_route(path, graphql_app) # type: ignore 30 | app.add_websocket_route(path, graphql_app) # type: ignore 31 | -------------------------------------------------------------------------------- /strawberry/cli/utils/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import rich 4 | import typer 5 | 6 | from strawberry import Schema 7 | from strawberry.utils.importer import import_module_symbol 8 | 9 | 10 | def load_schema(schema: str, app_dir: str) -> Schema: 11 | sys.path.insert(0, app_dir) 12 | 13 | try: 14 | schema_symbol = import_module_symbol(schema, default_symbol_name="schema") 15 | except (ImportError, AttributeError) as exc: 16 | message = str(exc) 17 | 18 | rich.print(f"[red]Error: {message}") 19 | raise typer.Exit(2) # noqa: B904 20 | 21 | if callable(schema_symbol): 22 | try: 23 | schema_symbol = schema_symbol() 24 | except Exception as exc: # noqa: BLE001 25 | message = f"Error invoking schema_symbol: {exc}" 26 | rich.print(f"[red]Error: {message}") 27 | raise typer.Exit(2) # noqa: B904 28 | 29 | if not isinstance(schema_symbol, Schema): 30 | message = "The `schema` must be an instance of strawberry.Schema" 31 | rich.print(f"[red]Error: {message}") 32 | raise typer.Exit(2) 33 | 34 | return schema_symbol 35 | -------------------------------------------------------------------------------- /strawberry/cli/utils/load_schema.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/cli/utils/load_schema.py -------------------------------------------------------------------------------- /strawberry/codegen/__init__.py: -------------------------------------------------------------------------------- 1 | from .query_codegen import ( 2 | CodegenFile, 3 | CodegenResult, 4 | ConsolePlugin, 5 | QueryCodegen, 6 | QueryCodegenPlugin, 7 | ) 8 | 9 | __all__ = [ 10 | "CodegenFile", 11 | "CodegenResult", 12 | "ConsolePlugin", 13 | "QueryCodegen", 14 | "QueryCodegenPlugin", 15 | ] 16 | -------------------------------------------------------------------------------- /strawberry/codegen/exceptions.py: -------------------------------------------------------------------------------- 1 | class CodegenError(Exception): 2 | pass 3 | 4 | 5 | class NoOperationProvidedError(CodegenError): 6 | pass 7 | 8 | 9 | class NoOperationNameProvidedError(CodegenError): 10 | pass 11 | 12 | 13 | class MultipleOperationsProvidedError(CodegenError): 14 | pass 15 | 16 | 17 | __all__ = [ 18 | "CodegenError", 19 | "MultipleOperationsProvidedError", 20 | "NoOperationNameProvidedError", 21 | "NoOperationProvidedError", 22 | ] 23 | -------------------------------------------------------------------------------- /strawberry/codegen/plugins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/codegen/plugins/__init__.py -------------------------------------------------------------------------------- /strawberry/codemods/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/codemods/__init__.py -------------------------------------------------------------------------------- /strawberry/django/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | try: 4 | # import modules and objects from external strawberry-graphql-django 5 | # package so that it can be used through strawberry.django namespace 6 | from strawberry_django import * # noqa: F403 7 | except ModuleNotFoundError: 8 | import importlib 9 | 10 | def __getattr__(name: str) -> Any: 11 | # try to import submodule and raise exception only if it does not exist 12 | import_symbol = f"{__name__}.{name}" 13 | try: 14 | return importlib.import_module(import_symbol) 15 | except ModuleNotFoundError as e: 16 | raise AttributeError( 17 | f"Attempted import of {import_symbol} failed. Make sure to install the" 18 | "'strawberry-graphql-django' package to use the Strawberry Django " 19 | "extension API." 20 | ) from e 21 | -------------------------------------------------------------------------------- /strawberry/django/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig # pragma: no cover 2 | 3 | 4 | class StrawberryConfig(AppConfig): # pragma: no cover 5 | name = "strawberry" 6 | -------------------------------------------------------------------------------- /strawberry/django/context.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from dataclasses import dataclass 4 | from typing import TYPE_CHECKING, Any 5 | 6 | if TYPE_CHECKING: 7 | from django.http import HttpRequest, HttpResponse 8 | 9 | 10 | @dataclass 11 | class StrawberryDjangoContext: 12 | request: HttpRequest 13 | response: HttpResponse 14 | 15 | def __getitem__(self, key: str) -> Any: 16 | # __getitem__ override needed to avoid issues for who's 17 | # using info.context["request"] 18 | return super().__getattribute__(key) 19 | 20 | def get(self, key: str) -> Any: 21 | """Enable .get notation for accessing the request.""" 22 | return super().__getattribute__(key) 23 | 24 | 25 | __all__ = ["StrawberryDjangoContext"] 26 | -------------------------------------------------------------------------------- /strawberry/django/test/__init__.py: -------------------------------------------------------------------------------- 1 | from .client import GraphQLTestClient 2 | 3 | __all__ = ["GraphQLTestClient"] 4 | -------------------------------------------------------------------------------- /strawberry/django/test/client.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Optional 2 | 3 | from strawberry.test import BaseGraphQLTestClient 4 | 5 | 6 | class GraphQLTestClient(BaseGraphQLTestClient): 7 | def request( 8 | self, 9 | body: dict[str, object], 10 | headers: Optional[dict[str, object]] = None, 11 | files: Optional[dict[str, object]] = None, 12 | ) -> Any: 13 | if files: 14 | return self._client.post( 15 | self.url, data=body, format="multipart", headers=headers 16 | ) 17 | 18 | return self._client.post( 19 | self.url, data=body, content_type="application/json", headers=headers 20 | ) 21 | 22 | 23 | __all__ = ["GraphQLTestClient"] 24 | -------------------------------------------------------------------------------- /strawberry/exceptions/exception_source.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from pathlib import Path 3 | 4 | 5 | @dataclass 6 | class ExceptionSource: 7 | path: Path 8 | code: str 9 | start_line: int 10 | end_line: int 11 | error_line: int 12 | error_column: int 13 | error_column_end: int 14 | 15 | @property 16 | def path_relative_to_cwd(self) -> Path: 17 | if self.path.is_absolute(): 18 | return self.path.relative_to(Path.cwd()) 19 | 20 | return self.path 21 | -------------------------------------------------------------------------------- /strawberry/exceptions/missing_dependencies.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import Optional 4 | 5 | 6 | class MissingOptionalDependenciesError(Exception): 7 | """Some optional dependencies that are required for a particular task are missing.""" 8 | 9 | def __init__( 10 | self, 11 | *, 12 | packages: Optional[list[str]] = None, 13 | extras: Optional[list[str]] = None, 14 | ) -> None: 15 | """Initialize the error. 16 | 17 | Args: 18 | packages: List of packages that are required but missing. 19 | extras: List of extras that are required but missing. 20 | """ 21 | packages = packages or [] 22 | 23 | if extras: 24 | packages.append(f"'strawberry-graphql[{','.join(extras)}]'") 25 | 26 | hint = f" (hint: pip install {' '.join(packages)})" if packages else "" 27 | 28 | self.message = f"Some optional dependencies are missing{hint}" 29 | -------------------------------------------------------------------------------- /strawberry/exceptions/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/exceptions/utils/__init__.py -------------------------------------------------------------------------------- /strawberry/experimental/__init__.py: -------------------------------------------------------------------------------- 1 | try: 2 | from . import pydantic 3 | except ModuleNotFoundError: 4 | pass 5 | else: 6 | __all__ = ["pydantic"] 7 | -------------------------------------------------------------------------------- /strawberry/experimental/pydantic/__init__.py: -------------------------------------------------------------------------------- 1 | from .error_type import error_type 2 | from .exceptions import UnregisteredTypeException 3 | from .object_type import input, interface, type # noqa: A004 4 | 5 | __all__ = [ 6 | "UnregisteredTypeException", 7 | "error_type", 8 | "input", 9 | "interface", 10 | "type", 11 | ] 12 | -------------------------------------------------------------------------------- /strawberry/experimental/pydantic/conversion_types.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING, Any, Optional, TypeVar 4 | from typing_extensions import Protocol 5 | 6 | from pydantic import BaseModel 7 | 8 | if TYPE_CHECKING: 9 | from strawberry.types.base import StrawberryObjectDefinition 10 | 11 | 12 | PydanticModel = TypeVar("PydanticModel", bound=BaseModel) 13 | 14 | 15 | class StrawberryTypeFromPydantic(Protocol[PydanticModel]): 16 | """This class does not exist in runtime. 17 | 18 | It only makes the methods below visible for IDEs. 19 | """ 20 | 21 | def __init__(self, **kwargs: Any) -> None: ... 22 | 23 | @staticmethod 24 | def from_pydantic( 25 | instance: PydanticModel, extra: Optional[dict[str, Any]] = None 26 | ) -> StrawberryTypeFromPydantic[PydanticModel]: ... 27 | 28 | def to_pydantic(self, **kwargs: Any) -> PydanticModel: ... 29 | 30 | @property 31 | def __strawberry_definition__(self) -> StrawberryObjectDefinition: ... 32 | 33 | @property 34 | def _pydantic_type(self) -> type[PydanticModel]: ... 35 | -------------------------------------------------------------------------------- /strawberry/ext/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/ext/__init__.py -------------------------------------------------------------------------------- /strawberry/ext/dataclasses/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/ext/dataclasses/__init__.py -------------------------------------------------------------------------------- /strawberry/extensions/disable_validation.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Iterator 2 | 3 | from strawberry.extensions.base_extension import SchemaExtension 4 | 5 | 6 | class DisableValidation(SchemaExtension): 7 | """Disable query validation. 8 | 9 | Example: 10 | 11 | ```python 12 | import strawberry 13 | from strawberry.extensions import DisableValidation 14 | 15 | schema = strawberry.Schema( 16 | Query, 17 | extensions=[ 18 | DisableValidation, 19 | ], 20 | ) 21 | ``` 22 | """ 23 | 24 | def __init__(self) -> None: 25 | # There aren't any arguments to this extension yet but we might add 26 | # some in the future 27 | pass 28 | 29 | def on_operation(self) -> Iterator[None]: 30 | self.execution_context.validation_rules = () # remove all validation_rules 31 | yield 32 | 33 | 34 | __all__ = ["DisableValidation"] 35 | -------------------------------------------------------------------------------- /strawberry/extensions/pyinstrument.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pathlib import Path 4 | from typing import TYPE_CHECKING 5 | 6 | from pyinstrument import Profiler 7 | 8 | from strawberry.extensions.base_extension import SchemaExtension 9 | 10 | if TYPE_CHECKING: 11 | from collections.abc import Iterator 12 | 13 | 14 | class PyInstrument(SchemaExtension): 15 | """Extension to profile the execution time of resolvers using PyInstrument.""" 16 | 17 | def __init__( 18 | self, 19 | report_path: Path = Path("pyinstrument.html"), 20 | ) -> None: 21 | self._report_path = report_path 22 | 23 | def on_operation(self) -> Iterator[None]: 24 | profiler = Profiler() 25 | profiler.start() 26 | 27 | yield 28 | 29 | profiler.stop() 30 | 31 | self._report_path.write_text(profiler.output_html()) 32 | 33 | 34 | __all__ = ["PyInstrument"] 35 | -------------------------------------------------------------------------------- /strawberry/extensions/tracing/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING, Any, Callable 4 | 5 | from strawberry.extensions.utils import is_introspection_field 6 | from strawberry.resolvers import is_default_resolver 7 | 8 | if TYPE_CHECKING: 9 | from graphql import GraphQLResolveInfo 10 | 11 | 12 | def should_skip_tracing(resolver: Callable[..., Any], info: GraphQLResolveInfo) -> bool: 13 | if info.field_name not in info.parent_type.fields: 14 | return True 15 | resolver = info.parent_type.fields[info.field_name].resolve 16 | return ( 17 | is_introspection_field(info) 18 | or is_default_resolver(resolver) 19 | or resolver is None 20 | ) 21 | 22 | 23 | __all__ = ["should_skip_tracing"] 24 | -------------------------------------------------------------------------------- /strawberry/extensions/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING, Union 4 | 5 | if TYPE_CHECKING: 6 | from graphql import GraphQLResolveInfo 7 | 8 | 9 | def is_introspection_key(key: Union[str, int]) -> bool: 10 | # from: https://spec.graphql.org/June2018/#sec-Schema 11 | # > All types and directives defined within a schema must not have a name which 12 | # > begins with "__" (two underscores), as this is used exclusively 13 | # > by GraphQL`s introspection system. 14 | 15 | return str(key).startswith("__") 16 | 17 | 18 | def is_introspection_field(info: GraphQLResolveInfo) -> bool: 19 | path = info.path 20 | 21 | while path: 22 | if is_introspection_key(path.key): 23 | return True 24 | path = path.prev 25 | 26 | return False 27 | 28 | 29 | def get_path_from_info(info: GraphQLResolveInfo) -> list[str]: 30 | path = info.path 31 | elements = [] 32 | 33 | while path: 34 | elements.append(path.key) 35 | path = path.prev 36 | 37 | return elements[::-1] 38 | 39 | 40 | __all__ = ["get_path_from_info", "is_introspection_field", "is_introspection_key"] 41 | -------------------------------------------------------------------------------- /strawberry/fastapi/__init__.py: -------------------------------------------------------------------------------- 1 | from strawberry.fastapi.context import BaseContext 2 | from strawberry.fastapi.router import GraphQLRouter 3 | 4 | __all__ = ["BaseContext", "GraphQLRouter"] 5 | -------------------------------------------------------------------------------- /strawberry/fastapi/context.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Optional, Union 2 | 3 | from starlette.background import BackgroundTasks 4 | from starlette.requests import Request 5 | from starlette.responses import Response 6 | from starlette.websockets import WebSocket 7 | 8 | CustomContext = Union["BaseContext", dict[str, Any]] 9 | MergedContext = Union[ 10 | "BaseContext", dict[str, Union[Any, BackgroundTasks, Request, Response, WebSocket]] 11 | ] 12 | 13 | 14 | class BaseContext: 15 | connection_params: Optional[Any] = None 16 | 17 | def __init__(self) -> None: 18 | self.request: Optional[Union[Request, WebSocket]] = None 19 | self.background_tasks: Optional[BackgroundTasks] = None 20 | self.response: Optional[Response] = None 21 | 22 | 23 | __all__ = ["BaseContext"] 24 | -------------------------------------------------------------------------------- /strawberry/federation/__init__.py: -------------------------------------------------------------------------------- 1 | from .argument import argument 2 | from .enum import enum, enum_value 3 | from .field import field 4 | from .mutation import mutation 5 | from .object_type import input, interface, interface_object, type # noqa: A004 6 | from .scalar import scalar 7 | from .schema import Schema 8 | from .schema_directive import schema_directive 9 | from .union import union 10 | 11 | __all__ = [ 12 | "Schema", 13 | "argument", 14 | "enum", 15 | "enum_value", 16 | "field", 17 | "input", 18 | "interface", 19 | "interface_object", 20 | "mutation", 21 | "scalar", 22 | "schema_directive", 23 | "type", 24 | "union", 25 | ] 26 | -------------------------------------------------------------------------------- /strawberry/federation/argument.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Iterable 2 | from typing import Optional 3 | 4 | from strawberry.types.arguments import StrawberryArgumentAnnotation 5 | 6 | 7 | def argument( 8 | description: Optional[str] = None, 9 | name: Optional[str] = None, 10 | deprecation_reason: Optional[str] = None, 11 | directives: Iterable[object] = (), 12 | inaccessible: bool = False, 13 | tags: Optional[Iterable[str]] = (), 14 | ) -> StrawberryArgumentAnnotation: 15 | from strawberry.federation.schema_directives import Inaccessible, Tag 16 | 17 | directives = list(directives) 18 | 19 | if inaccessible: 20 | directives.append(Inaccessible()) 21 | 22 | if tags: 23 | directives.extend(Tag(name=tag) for tag in tags) 24 | 25 | return StrawberryArgumentAnnotation( 26 | description=description, 27 | name=name, 28 | deprecation_reason=deprecation_reason, 29 | directives=directives, 30 | ) 31 | 32 | 33 | __all__ = ["argument"] 34 | -------------------------------------------------------------------------------- /strawberry/federation/mutation.py: -------------------------------------------------------------------------------- 1 | from .field import field 2 | 3 | mutation = field 4 | 5 | __all__ = ["mutation"] 6 | -------------------------------------------------------------------------------- /strawberry/federation/types.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | from strawberry.types.enum import enum 4 | from strawberry.types.scalar import scalar 5 | 6 | FieldSet = scalar(str, name="_FieldSet") 7 | 8 | LinkImport = scalar(object, name="link__Import") 9 | 10 | 11 | @enum(name="link__Purpose") 12 | class LinkPurpose(Enum): 13 | SECURITY = "SECURITY" 14 | EXECUTION = "EXECUTION" 15 | 16 | 17 | __all__ = ["FieldSet", "LinkImport", "LinkPurpose"] 18 | -------------------------------------------------------------------------------- /strawberry/field_extensions/__init__.py: -------------------------------------------------------------------------------- 1 | from .input_mutation import InputMutationExtension 2 | 3 | __all__ = [ 4 | "InputMutationExtension", 5 | ] 6 | -------------------------------------------------------------------------------- /strawberry/file_uploads/__init__.py: -------------------------------------------------------------------------------- 1 | from .scalars import Upload 2 | 3 | __all__ = ["Upload"] 4 | -------------------------------------------------------------------------------- /strawberry/file_uploads/scalars.py: -------------------------------------------------------------------------------- 1 | from typing import NewType 2 | 3 | from strawberry.types.scalar import scalar 4 | 5 | Upload = scalar(NewType("Upload", bytes), parse_value=lambda x: x) 6 | 7 | __all__ = ["Upload"] 8 | -------------------------------------------------------------------------------- /strawberry/flask/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/flask/__init__.py -------------------------------------------------------------------------------- /strawberry/http/exceptions.py: -------------------------------------------------------------------------------- 1 | class HTTPException(Exception): 2 | def __init__(self, status_code: int, reason: str) -> None: 3 | self.status_code = status_code 4 | self.reason = reason 5 | 6 | 7 | class NonTextMessageReceived(Exception): 8 | pass 9 | 10 | 11 | class NonJsonMessageReceived(Exception): 12 | pass 13 | 14 | 15 | class WebSocketDisconnected(Exception): 16 | pass 17 | 18 | 19 | __all__ = ["HTTPException"] 20 | -------------------------------------------------------------------------------- /strawberry/http/ides.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | from typing import Optional 3 | from typing_extensions import Literal 4 | 5 | GraphQL_IDE = Literal["graphiql", "apollo-sandbox", "pathfinder"] 6 | 7 | 8 | def get_graphql_ide_html( 9 | graphql_ide: Optional[GraphQL_IDE] = "graphiql", 10 | ) -> str: 11 | here = pathlib.Path(__file__).parents[1] 12 | 13 | if graphql_ide == "apollo-sandbox": 14 | path = here / "static/apollo-sandbox.html" 15 | elif graphql_ide == "pathfinder": 16 | path = here / "static/pathfinder.html" 17 | else: 18 | path = here / "static/graphiql.html" 19 | 20 | return path.read_text(encoding="utf-8") 21 | 22 | 23 | __all__ = ["GraphQL_IDE", "get_graphql_ide_html"] 24 | -------------------------------------------------------------------------------- /strawberry/http/parse_content_type.py: -------------------------------------------------------------------------------- 1 | from email.message import Message 2 | 3 | 4 | def parse_content_type(content_type: str) -> tuple[str, dict[str, str]]: 5 | """Parse a content type header into a mime-type and a dictionary of parameters.""" 6 | email = Message() 7 | email["content-type"] = content_type 8 | 9 | params = email.get_params() 10 | 11 | assert params 12 | 13 | mime_type, _ = params.pop(0) 14 | 15 | return mime_type, dict(params) 16 | -------------------------------------------------------------------------------- /strawberry/http/temporal_response.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass, field 2 | 3 | 4 | @dataclass 5 | class TemporalResponse: 6 | status_code: int = 200 7 | headers: dict[str, str] = field(default_factory=dict) 8 | 9 | 10 | __all__ = ["TemporalResponse"] 11 | -------------------------------------------------------------------------------- /strawberry/http/types.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Mapping 2 | from typing import Any, Optional 3 | from typing_extensions import Literal, TypedDict 4 | 5 | HTTPMethod = Literal[ 6 | "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE" 7 | ] 8 | 9 | QueryParams = Mapping[str, Optional[str]] 10 | 11 | 12 | class FormData(TypedDict): 13 | files: Mapping[str, Any] 14 | form: Mapping[str, Any] 15 | 16 | 17 | __all__ = ["FormData", "HTTPMethod", "QueryParams"] 18 | -------------------------------------------------------------------------------- /strawberry/http/typevars.py: -------------------------------------------------------------------------------- 1 | from typing_extensions import TypeVar 2 | 3 | Request = TypeVar("Request", contravariant=True) 4 | Response = TypeVar("Response") 5 | SubResponse = TypeVar("SubResponse") 6 | WebSocketRequest = TypeVar("WebSocketRequest") 7 | WebSocketResponse = TypeVar("WebSocketResponse") 8 | Context = TypeVar("Context", default=None) 9 | RootValue = TypeVar("RootValue", default=None) 10 | 11 | 12 | __all__ = [ 13 | "Context", 14 | "Request", 15 | "Response", 16 | "RootValue", 17 | "SubResponse", 18 | "WebSocketRequest", 19 | "WebSocketResponse", 20 | ] 21 | -------------------------------------------------------------------------------- /strawberry/litestar/__init__.py: -------------------------------------------------------------------------------- 1 | from .controller import ( 2 | BaseContext, 3 | HTTPContextType, 4 | WebSocketContextType, 5 | make_graphql_controller, 6 | ) 7 | 8 | __all__ = [ 9 | "BaseContext", 10 | "HTTPContextType", 11 | "WebSocketContextType", 12 | "make_graphql_controller", 13 | ] 14 | -------------------------------------------------------------------------------- /strawberry/printer/__init__.py: -------------------------------------------------------------------------------- 1 | from .printer import print_schema 2 | 3 | __all__ = ["print_schema"] 4 | -------------------------------------------------------------------------------- /strawberry/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/py.typed -------------------------------------------------------------------------------- /strawberry/quart/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/quart/__init__.py -------------------------------------------------------------------------------- /strawberry/relay/__init__.py: -------------------------------------------------------------------------------- 1 | from .fields import ConnectionExtension, NodeExtension, connection, node 2 | from .types import ( 3 | Connection, 4 | Edge, 5 | GlobalID, 6 | GlobalIDValueError, 7 | ListConnection, 8 | Node, 9 | NodeID, 10 | NodeType, 11 | PageInfo, 12 | ) 13 | from .utils import from_base64, to_base64 14 | 15 | __all__ = [ 16 | "Connection", 17 | "ConnectionExtension", 18 | "Edge", 19 | "GlobalID", 20 | "GlobalIDValueError", 21 | "ListConnection", 22 | "Node", 23 | "NodeExtension", 24 | "NodeID", 25 | "NodeType", 26 | "PageInfo", 27 | "connection", 28 | "from_base64", 29 | "node", 30 | "to_base64", 31 | ] 32 | -------------------------------------------------------------------------------- /strawberry/resolvers.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Callable 2 | 3 | 4 | def is_default_resolver(func: Callable[..., Any]) -> bool: 5 | """Check whether the function is a default resolver or a user provided one.""" 6 | return getattr(func, "_is_default", False) 7 | 8 | 9 | __all__ = ["is_default_resolver"] 10 | -------------------------------------------------------------------------------- /strawberry/sanic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/sanic/__init__.py -------------------------------------------------------------------------------- /strawberry/sanic/context.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | from typing_extensions import TypedDict 3 | 4 | from sanic.request import Request 5 | from strawberry.http.temporal_response import TemporalResponse 6 | 7 | 8 | class StrawberrySanicContext(TypedDict): 9 | request: Request 10 | response: TemporalResponse 11 | 12 | # see https://github.com/python/mypy/issues/13066 for the type ignore 13 | def __getattr__(self, key: str) -> object: # type: ignore 14 | # a warning will be raised because this is not supported anymore 15 | # but we need to keep it for backwards compatibility 16 | 17 | warnings.warn( 18 | "Accessing context attributes via the dot notation is deprecated, " 19 | "please use context.get('key') or context['key'] instead", 20 | DeprecationWarning, 21 | stacklevel=2, 22 | ) 23 | 24 | return super().__getitem__(key) 25 | 26 | 27 | __all__ = ["StrawberrySanicContext"] 28 | -------------------------------------------------------------------------------- /strawberry/sanic/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING, Any, Optional, Union, cast 4 | 5 | if TYPE_CHECKING: 6 | from sanic.request import File, Request 7 | 8 | 9 | def convert_request_to_files_dict(request: Request) -> dict[str, Any]: 10 | """Converts the request.files dictionary to a dictionary of sanic Request objects. 11 | 12 | `request.files` has the following format, even if only a single file is uploaded: 13 | 14 | ```python 15 | { 16 | "textFile": [ 17 | sanic.request.File(type="text/plain", body=b"strawberry", name="textFile.txt") 18 | ] 19 | } 20 | ``` 21 | 22 | Note that the dictionary entries are lists. 23 | """ 24 | request_files = cast("Optional[dict[str, list[File]]]", request.files) 25 | 26 | if not request_files: 27 | return {} 28 | 29 | files_dict: dict[str, Union[File, list[File]]] = {} 30 | 31 | for field_name, file_list in request_files.items(): 32 | assert len(file_list) == 1 33 | 34 | files_dict[field_name] = file_list[0] 35 | 36 | return files_dict 37 | 38 | 39 | __all__ = ["convert_request_to_files_dict"] 40 | -------------------------------------------------------------------------------- /strawberry/schema/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import BaseSchema 2 | from .schema import Schema 3 | 4 | __all__ = ["BaseSchema", "Schema"] 5 | -------------------------------------------------------------------------------- /strawberry/schema/config.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from dataclasses import InitVar, dataclass, field 4 | from typing import Any, Callable 5 | 6 | from strawberry.types.info import Info 7 | 8 | from .name_converter import NameConverter 9 | 10 | 11 | @dataclass 12 | class StrawberryConfig: 13 | auto_camel_case: InitVar[bool] = None # pyright: reportGeneralTypeIssues=false 14 | name_converter: NameConverter = field(default_factory=NameConverter) 15 | default_resolver: Callable[[Any, str], object] = getattr 16 | relay_max_results: int = 100 17 | relay_use_legacy_global_id: bool = False 18 | disable_field_suggestions: bool = False 19 | info_class: type[Info] = Info 20 | 21 | def __post_init__( 22 | self, 23 | auto_camel_case: bool, 24 | ) -> None: 25 | if auto_camel_case is not None: 26 | self.name_converter.auto_camel_case = auto_camel_case 27 | 28 | if not issubclass(self.info_class, Info): 29 | raise TypeError("`info_class` must be a subclass of strawberry.Info") 30 | 31 | 32 | __all__ = ["StrawberryConfig"] 33 | -------------------------------------------------------------------------------- /strawberry/schema/exceptions.py: -------------------------------------------------------------------------------- 1 | from strawberry.types.graphql import OperationType 2 | 3 | 4 | class CannotGetOperationTypeError(Exception): 5 | """Internal error raised when we cannot get the operation type from a GraphQL document.""" 6 | 7 | 8 | class InvalidOperationTypeError(Exception): 9 | def __init__(self, operation_type: OperationType) -> None: 10 | self.operation_type = operation_type 11 | 12 | def as_http_error_reason(self, method: str) -> str: 13 | operation_type = { 14 | OperationType.QUERY: "queries", 15 | OperationType.MUTATION: "mutations", 16 | OperationType.SUBSCRIPTION: "subscriptions", 17 | }[self.operation_type] 18 | 19 | return f"{operation_type} are not allowed when using {method}" 20 | 21 | 22 | __all__ = [ 23 | "CannotGetOperationTypeError", 24 | "InvalidOperationTypeError", 25 | ] 26 | -------------------------------------------------------------------------------- /strawberry/schema/types/__init__.py: -------------------------------------------------------------------------------- 1 | from .concrete_type import ConcreteType 2 | 3 | __all__ = ["ConcreteType"] 4 | -------------------------------------------------------------------------------- /strawberry/schema/types/concrete_type.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import dataclasses 4 | from typing import TYPE_CHECKING, Union 5 | 6 | from graphql import GraphQLField, GraphQLInputField, GraphQLType 7 | 8 | if TYPE_CHECKING: 9 | from strawberry.types.base import StrawberryObjectDefinition 10 | from strawberry.types.enum import EnumDefinition 11 | from strawberry.types.scalar import ScalarDefinition 12 | from strawberry.types.union import StrawberryUnion 13 | 14 | Field = Union[GraphQLInputField, GraphQLField] 15 | 16 | 17 | @dataclasses.dataclass 18 | class ConcreteType: 19 | definition: Union[ 20 | StrawberryObjectDefinition, EnumDefinition, ScalarDefinition, StrawberryUnion 21 | ] 22 | implementation: GraphQLType 23 | 24 | 25 | TypeMap = dict[str, ConcreteType] 26 | 27 | 28 | __all__ = ["ConcreteType", "Field", "GraphQLType", "TypeMap"] 29 | -------------------------------------------------------------------------------- /strawberry/schema/validation_rules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/schema/validation_rules/__init__.py -------------------------------------------------------------------------------- /strawberry/schema_directives.py: -------------------------------------------------------------------------------- 1 | from strawberry.schema_directive import Location, schema_directive 2 | 3 | 4 | @schema_directive(locations=[Location.INPUT_OBJECT], name="oneOf") 5 | class OneOf: ... 6 | 7 | 8 | __all__ = ["OneOf"] 9 | -------------------------------------------------------------------------------- /strawberry/static/apollo-sandbox.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Strawberry Apollo Sandbox 5 | 13 | 26 | 27 | 28 | 29 |
30 | 31 | 32 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /strawberry/subscriptions/__init__.py: -------------------------------------------------------------------------------- 1 | GRAPHQL_TRANSPORT_WS_PROTOCOL = "graphql-transport-ws" 2 | GRAPHQL_WS_PROTOCOL = "graphql-ws" 3 | 4 | 5 | __all__ = [ 6 | "GRAPHQL_TRANSPORT_WS_PROTOCOL", 7 | "GRAPHQL_WS_PROTOCOL", 8 | ] 9 | -------------------------------------------------------------------------------- /strawberry/subscriptions/protocols/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/subscriptions/protocols/__init__.py -------------------------------------------------------------------------------- /strawberry/subscriptions/protocols/graphql_transport_ws/__init__.py: -------------------------------------------------------------------------------- 1 | # Code 4406 is "Subprotocol not acceptable" 2 | WS_4406_PROTOCOL_NOT_ACCEPTABLE = 4406 3 | 4 | 5 | __all__ = [ 6 | "WS_4406_PROTOCOL_NOT_ACCEPTABLE", 7 | ] 8 | -------------------------------------------------------------------------------- /strawberry/subscriptions/protocols/graphql_ws/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/subscriptions/protocols/graphql_ws/__init__.py -------------------------------------------------------------------------------- /strawberry/test/__init__.py: -------------------------------------------------------------------------------- 1 | from .client import BaseGraphQLTestClient, Body, Response 2 | 3 | __all__ = ["BaseGraphQLTestClient", "Body", "Response"] 4 | -------------------------------------------------------------------------------- /strawberry/tools/__init__.py: -------------------------------------------------------------------------------- 1 | from .create_type import create_type 2 | from .merge_types import merge_types 3 | 4 | __all__ = [ 5 | "create_type", 6 | "merge_types", 7 | ] 8 | -------------------------------------------------------------------------------- /strawberry/tools/merge_types.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | from collections import Counter 3 | from itertools import chain 4 | 5 | import strawberry 6 | from strawberry.types.base import has_object_definition 7 | 8 | 9 | def merge_types(name: str, types: tuple[type, ...]) -> type: 10 | """Merge multiple Strawberry types into one. 11 | 12 | For example, given two queries `A` and `B`, one can merge them into a 13 | super type as follows: 14 | 15 | merge_types("SuperQuery", (B, A)) 16 | 17 | This is essentially the same as: 18 | 19 | class SuperQuery(B, A): 20 | ... 21 | """ 22 | if not types: 23 | raise ValueError("Can't merge types if none are supplied") 24 | 25 | fields = chain( 26 | *(t.__strawberry_definition__.fields for t in types if has_object_definition(t)) 27 | ) 28 | counter = Counter(f.name for f in fields) 29 | dupes = [f for f, c in counter.most_common() if c > 1] 30 | if dupes: 31 | warnings.warn( 32 | "{} has overridden fields: {}".format(name, ", ".join(dupes)), stacklevel=2 33 | ) 34 | 35 | return strawberry.type(type(name, types, {})) 36 | 37 | 38 | __all__ = ["merge_types"] 39 | -------------------------------------------------------------------------------- /strawberry/types/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import get_object_definition, has_object_definition 2 | from .execution import ExecutionContext, ExecutionResult, SubscriptionExecutionResult 3 | from .info import Info 4 | 5 | __all__ = [ 6 | "ExecutionContext", 7 | "ExecutionResult", 8 | "Info", 9 | "Info", 10 | "SubscriptionExecutionResult", 11 | "get_object_definition", 12 | "has_object_definition", 13 | ] 14 | -------------------------------------------------------------------------------- /strawberry/types/cast.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import Any, TypeVar, overload 4 | 5 | _T = TypeVar("_T", bound=object) 6 | 7 | TYPE_CAST_ATTRIBUTE = "__as_strawberry_type__" 8 | 9 | 10 | @overload 11 | def cast(type_: type, obj: None) -> None: ... 12 | 13 | 14 | @overload 15 | def cast(type_: type, obj: _T) -> _T: ... 16 | 17 | 18 | def cast(type_: type, obj: _T | None) -> _T | None: 19 | """Cast an object to given type. 20 | 21 | This is used to mark an object as a cast object, so that the type can be 22 | picked up when resolving unions/interfaces in case of ambiguity, which can 23 | happen when returning an alike object instead of an instance of the type 24 | (e.g. returning a Django, Pydantic or SQLAlchemy object) 25 | """ 26 | if obj is None: 27 | return None 28 | 29 | setattr(obj, TYPE_CAST_ATTRIBUTE, type_) 30 | return obj 31 | 32 | 33 | def get_strawberry_type_cast(obj: Any) -> type | None: 34 | """Get the type of a cast object.""" 35 | return getattr(obj, TYPE_CAST_ATTRIBUTE, None) 36 | -------------------------------------------------------------------------------- /strawberry/types/fields/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/strawberry/types/fields/__init__.py -------------------------------------------------------------------------------- /strawberry/types/graphql.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import enum 4 | from typing import TYPE_CHECKING 5 | 6 | if TYPE_CHECKING: 7 | from strawberry.http.types import HTTPMethod 8 | 9 | 10 | class OperationType(enum.Enum): 11 | QUERY = "query" 12 | MUTATION = "mutation" 13 | SUBSCRIPTION = "subscription" 14 | 15 | @staticmethod 16 | def from_http(method: HTTPMethod) -> set[OperationType]: 17 | if method == "GET": 18 | return { 19 | OperationType.QUERY, 20 | # subscriptions are supported via GET in the multipart protocol 21 | OperationType.SUBSCRIPTION, 22 | } 23 | 24 | if method == "POST": 25 | return { 26 | OperationType.QUERY, 27 | OperationType.MUTATION, 28 | OperationType.SUBSCRIPTION, 29 | } 30 | 31 | raise ValueError(f"Unsupported HTTP method: {method}") # pragma: no cover 32 | 33 | 34 | __all__ = ["OperationType"] 35 | -------------------------------------------------------------------------------- /strawberry/types/private.py: -------------------------------------------------------------------------------- 1 | from typing import Annotated, TypeVar 2 | 3 | from strawberry.utils.typing import type_has_annotation 4 | 5 | 6 | class StrawberryPrivate: ... 7 | 8 | 9 | T = TypeVar("T") 10 | 11 | Private = Annotated[T, StrawberryPrivate()] 12 | """Represents a field that won't be exposed in the GraphQL schema. 13 | 14 | Example: 15 | 16 | ```python 17 | import strawberry 18 | 19 | 20 | @strawberry.type 21 | class User: 22 | name: str 23 | age: strawberry.Private[int] 24 | ``` 25 | """ 26 | 27 | 28 | def is_private(type_: object) -> bool: 29 | return type_has_annotation(type_, StrawberryPrivate) 30 | 31 | 32 | __all__ = ["Private", "is_private"] 33 | -------------------------------------------------------------------------------- /strawberry/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from graphql.version import VersionInfo, version_info 2 | 3 | IS_GQL_33 = version_info >= VersionInfo.from_str("3.3.0a0") 4 | IS_GQL_32 = not IS_GQL_33 5 | -------------------------------------------------------------------------------- /strawberry/utils/await_maybe.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | from collections.abc import AsyncIterator, Awaitable, Iterator 3 | from typing import TypeVar, Union 4 | 5 | T = TypeVar("T") 6 | 7 | AwaitableOrValue = Union[Awaitable[T], T] 8 | AsyncIteratorOrIterator = Union[AsyncIterator[T], Iterator[T]] 9 | 10 | 11 | async def await_maybe(value: AwaitableOrValue[T]) -> T: 12 | if inspect.isawaitable(value): 13 | return await value 14 | 15 | return value 16 | 17 | 18 | __all__ = ["AsyncIteratorOrIterator", "AwaitableOrValue", "await_maybe"] 19 | -------------------------------------------------------------------------------- /strawberry/utils/dataclasses.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import sys 4 | from dataclasses import ( # type: ignore 5 | _FIELD, 6 | _FIELD_INITVAR, 7 | _FIELDS, 8 | _POST_INIT_NAME, 9 | _set_new_attribute, 10 | ) 11 | from typing import Any 12 | 13 | from strawberry.ext.dataclasses.dataclasses import dataclass_init_fn 14 | 15 | 16 | def add_custom_init_fn(cls: Any) -> None: 17 | fields = [ 18 | f 19 | for f in getattr(cls, _FIELDS).values() 20 | if f._field_type in (_FIELD, _FIELD_INITVAR) 21 | ] 22 | globals_ = sys.modules[cls.__module__].__dict__ 23 | 24 | _set_new_attribute( 25 | cls, 26 | "__init__", 27 | dataclass_init_fn( 28 | fields=fields, 29 | frozen=False, 30 | has_post_init=hasattr(cls, _POST_INIT_NAME), 31 | self_name="__dataclass_self__" if "self" in fields else "self", 32 | globals_=globals_, 33 | ), 34 | ) 35 | 36 | 37 | __all__ = ["add_custom_init_fn"] 38 | -------------------------------------------------------------------------------- /strawberry/utils/deprecations.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import warnings 4 | from typing import Any, Optional 5 | 6 | 7 | class DEPRECATION_MESSAGES: # noqa: N801 8 | _TYPE_DEFINITION = ( 9 | "_type_definition is deprecated, use __strawberry_definition__ instead" 10 | ) 11 | 12 | 13 | class DeprecatedDescriptor: 14 | def __init__(self, msg: str, alias: object, attr_name: str) -> None: 15 | self.msg = msg 16 | self.alias = alias 17 | self.attr_name = attr_name 18 | 19 | def warn(self) -> None: 20 | warnings.warn(self.msg, stacklevel=2) 21 | 22 | def __get__(self, obj: Optional[object], type: Optional[type] = None) -> Any: 23 | self.warn() 24 | return self.alias 25 | 26 | def inject(self, klass: type) -> None: 27 | setattr(klass, self.attr_name, self) 28 | 29 | 30 | __all__ = ["DEPRECATION_MESSAGES", "DeprecatedDescriptor"] 31 | -------------------------------------------------------------------------------- /strawberry/utils/importer.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | from typing import Optional 3 | 4 | 5 | def import_module_symbol( 6 | selector: str, default_symbol_name: Optional[str] = None 7 | ) -> object: 8 | if ":" in selector: 9 | module_name, symbol_name = selector.split(":", 1) 10 | elif default_symbol_name: 11 | module_name, symbol_name = selector, default_symbol_name 12 | else: 13 | raise ValueError("Selector does not include a symbol name") 14 | 15 | module = importlib.import_module(module_name) 16 | symbol = module 17 | 18 | for attribute_name in symbol_name.split("."): 19 | symbol = getattr(symbol, attribute_name) 20 | 21 | return symbol 22 | 23 | 24 | __all__ = ["import_module_symbol"] 25 | -------------------------------------------------------------------------------- /strawberry/utils/logging.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import logging 4 | from typing import TYPE_CHECKING, Any, Optional 5 | 6 | if TYPE_CHECKING: 7 | from typing import Final 8 | 9 | from graphql.error import GraphQLError 10 | 11 | from strawberry.types import ExecutionContext 12 | 13 | 14 | class StrawberryLogger: 15 | logger: Final[logging.Logger] = logging.getLogger("strawberry.execution") 16 | 17 | @classmethod 18 | def error( 19 | cls, 20 | error: GraphQLError, 21 | execution_context: Optional[ExecutionContext] = None, 22 | # https://www.python.org/dev/peps/pep-0484/#arbitrary-argument-lists-and-default-argument-values 23 | **logger_kwargs: Any, 24 | ) -> None: 25 | cls.logger.error(error, exc_info=error.original_error, **logger_kwargs) 26 | 27 | 28 | __all__ = ["StrawberryLogger"] 29 | -------------------------------------------------------------------------------- /strawberry/utils/str_converters.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | # Adapted from this response in Stackoverflow 5 | # http://stackoverflow.com/a/19053800/1072990 6 | def to_camel_case(snake_str: str) -> str: 7 | components = snake_str.split("_") 8 | # We capitalize the first letter of each component except the first one 9 | # with the 'capitalize' method and join them together. 10 | return components[0] + "".join(x.capitalize() if x else "_" for x in components[1:]) 11 | 12 | 13 | TO_KEBAB_CASE_RE = re.compile("((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))") 14 | 15 | 16 | def to_kebab_case(name: str) -> str: 17 | return TO_KEBAB_CASE_RE.sub(r"-\1", name).lower() 18 | 19 | 20 | def capitalize_first(name: str) -> str: 21 | return name[0].upper() + name[1:] 22 | 23 | 24 | def to_snake_case(name: str) -> str: 25 | name = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name) 26 | return re.sub("([a-z0-9])([A-Z])", r"\1_\2", name).lower() 27 | 28 | 29 | __all__ = ["capitalize_first", "to_camel_case", "to_kebab_case", "to_snake_case"] 30 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/__init__.py -------------------------------------------------------------------------------- /tests/a.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING, Annotated, Optional 4 | 5 | import strawberry 6 | 7 | if TYPE_CHECKING: 8 | from tests.b import B 9 | 10 | 11 | @strawberry.type 12 | class A: 13 | id: strawberry.ID 14 | 15 | @strawberry.field 16 | async def b(self) -> Annotated[B, strawberry.lazy("tests.b")]: 17 | from tests.b import B 18 | 19 | return B(id=self.id) 20 | 21 | @strawberry.field 22 | async def optional_b(self) -> Annotated[B, strawberry.lazy("tests.b")] | None: 23 | from tests.b import B 24 | 25 | return B(id=self.id) 26 | 27 | @strawberry.field 28 | async def optional_b2(self) -> Optional[Annotated[B, strawberry.lazy("tests.b")]]: 29 | from tests.b import B 30 | 31 | return B(id=self.id) 32 | -------------------------------------------------------------------------------- /tests/asgi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/asgi/__init__.py -------------------------------------------------------------------------------- /tests/asgi/test_async.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING, Optional 4 | 5 | import pytest 6 | 7 | import strawberry 8 | 9 | if TYPE_CHECKING: 10 | from starlette.testclient import TestClient 11 | 12 | 13 | @pytest.fixture 14 | def test_client() -> TestClient: 15 | from starlette.testclient import TestClient 16 | 17 | from strawberry.asgi import GraphQL 18 | 19 | @strawberry.type 20 | class Query: 21 | @strawberry.field 22 | async def hello(self, name: Optional[str] = None) -> str: 23 | return f"Hello {name or 'world'}" 24 | 25 | async_schema = strawberry.Schema(Query) 26 | app = GraphQL[None, None](async_schema) 27 | return TestClient(app) 28 | 29 | 30 | def test_simple_query(test_client: TestClient): 31 | response = test_client.post("/", json={"query": "{ hello }"}) 32 | 33 | assert response.json() == {"data": {"hello": "Hello world"}} 34 | -------------------------------------------------------------------------------- /tests/b.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING, Annotated, Optional 4 | 5 | import strawberry 6 | 7 | if TYPE_CHECKING: 8 | from tests.a import A 9 | 10 | 11 | @strawberry.type 12 | class B: 13 | id: strawberry.ID 14 | 15 | @strawberry.field 16 | async def a(self) -> Annotated[A, strawberry.lazy("tests.a"), object()]: 17 | from tests.a import A 18 | 19 | return A(id=self.id) 20 | 21 | @strawberry.field 22 | async def a_list( 23 | self, 24 | ) -> list[Annotated[A, strawberry.lazy("tests.a")]]: # pragma: no cover 25 | from tests.a import A 26 | 27 | return [A(id=self.id)] 28 | 29 | @strawberry.field 30 | async def optional_a( 31 | self, 32 | ) -> Annotated[A, strawberry.lazy("tests.a"), object()] | None: 33 | from tests.a import A 34 | 35 | return A(id=self.id) 36 | 37 | @strawberry.field 38 | async def optional_a2( 39 | self, 40 | ) -> Optional[Annotated[A, strawberry.lazy("tests.a"), object()]]: 41 | from tests.a import A 42 | 43 | return A(id=self.id) 44 | -------------------------------------------------------------------------------- /tests/benchmarks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/benchmarks/__init__.py -------------------------------------------------------------------------------- /tests/benchmarks/queries/items.graphql: -------------------------------------------------------------------------------- 1 | query Items($count: Int!) { 2 | items(count: $count) { 3 | name 4 | index 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/benchmarks/queries/many_fields.graphql: -------------------------------------------------------------------------------- 1 | { 2 | people { 3 | age 4 | description 5 | address 6 | name 7 | propA 8 | propB 9 | propC 10 | propD 11 | propE 12 | propF 13 | propG 14 | propH 15 | propI 16 | propJ 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/benchmarks/queries/many_fields_directives.graphql: -------------------------------------------------------------------------------- 1 | { 2 | people { 3 | age 4 | description 5 | address 6 | name 7 | propA 8 | propB 9 | propC 10 | propD 11 | propE 12 | propF 13 | propG @uppercase 14 | propH @uppercase 15 | propI 16 | propJ 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/benchmarks/queries/simple.graphql: -------------------------------------------------------------------------------- 1 | { 2 | hello 3 | } 4 | -------------------------------------------------------------------------------- /tests/c.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import strawberry 4 | 5 | 6 | @strawberry.type 7 | class C: 8 | id: strawberry.ID 9 | -------------------------------------------------------------------------------- /tests/channels/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/channels/__init__.py -------------------------------------------------------------------------------- /tests/cli/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/cli/__init__.py -------------------------------------------------------------------------------- /tests/cli/conftest.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import sys 4 | from typing import TYPE_CHECKING 5 | 6 | import pytest 7 | from pytest_mock import MockFixture 8 | from typer.testing import CliRunner 9 | 10 | if TYPE_CHECKING: 11 | from starlette.testclient import TestClient 12 | from typer import Typer 13 | 14 | 15 | @pytest.fixture 16 | def cli_runner(mocker: MockFixture) -> CliRunner: 17 | # Mock of uvicorn.run 18 | uvicorn_run_patch = mocker.patch("uvicorn.run") 19 | uvicorn_run_patch.return_value = True 20 | return CliRunner() 21 | 22 | 23 | @pytest.fixture 24 | def debug_server_client(mocker: MockFixture) -> TestClient: 25 | from starlette.testclient import TestClient 26 | 27 | schema_import_path = "tests.fixtures.sample_package.sample_module" 28 | mocker.patch.object(sys, "argv", ["strawberry", "server", schema_import_path]) 29 | 30 | from strawberry.cli.debug_server import app 31 | 32 | return TestClient(app) 33 | 34 | 35 | @pytest.fixture 36 | def cli_app() -> Typer: 37 | from strawberry.cli.app import app 38 | 39 | return app 40 | -------------------------------------------------------------------------------- /tests/cli/fixtures/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/cli/fixtures/__init__.py -------------------------------------------------------------------------------- /tests/cli/fixtures/unions.py: -------------------------------------------------------------------------------- 1 | import strawberry 2 | 3 | # create a few types and then a union type 4 | 5 | 6 | @strawberry.type 7 | class Foo: 8 | a: str 9 | 10 | 11 | @strawberry.type 12 | class Bar: 13 | b: str 14 | 15 | 16 | @strawberry.type 17 | class Baz: 18 | c: str 19 | 20 | 21 | @strawberry.type 22 | class Qux: 23 | d: str 24 | 25 | 26 | # this is the union type 27 | 28 | Union1 = strawberry.union(name="Union1", types=(Foo, Bar, Baz, Qux)) 29 | Union2 = strawberry.union(name="Union2", types=(Baz, Qux)) 30 | -------------------------------------------------------------------------------- /tests/cli/snapshots/unions.py: -------------------------------------------------------------------------------- 1 | import strawberry 2 | from typing import Annotated 3 | 4 | # create a few types and then a union type 5 | 6 | 7 | @strawberry.type 8 | class Foo: 9 | a: str 10 | 11 | 12 | @strawberry.type 13 | class Bar: 14 | b: str 15 | 16 | 17 | @strawberry.type 18 | class Baz: 19 | c: str 20 | 21 | 22 | @strawberry.type 23 | class Qux: 24 | d: str 25 | 26 | 27 | # this is the union type 28 | 29 | Union1 = Annotated[Foo | Bar | Baz | Qux, strawberry.union(name="Union1")] 30 | Union2 = Annotated[Baz | Qux, strawberry.union(name="Union2")] 31 | -------------------------------------------------------------------------------- /tests/cli/snapshots/unions_py38.py: -------------------------------------------------------------------------------- 1 | import strawberry 2 | from typing import Annotated, Union 3 | 4 | # create a few types and then a union type 5 | 6 | 7 | @strawberry.type 8 | class Foo: 9 | a: str 10 | 11 | 12 | @strawberry.type 13 | class Bar: 14 | b: str 15 | 16 | 17 | @strawberry.type 18 | class Baz: 19 | c: str 20 | 21 | 22 | @strawberry.type 23 | class Qux: 24 | d: str 25 | 26 | 27 | # this is the union type 28 | 29 | Union1 = Annotated[Union[Foo, Bar, Baz, Qux], strawberry.union(name="Union1")] 30 | Union2 = Annotated[Union[Baz, Qux], strawberry.union(name="Union2")] 31 | -------------------------------------------------------------------------------- /tests/cli/snapshots/unions_typing_extension.py: -------------------------------------------------------------------------------- 1 | import strawberry 2 | from typing_extensions import Annotated 3 | 4 | # create a few types and then a union type 5 | 6 | 7 | @strawberry.type 8 | class Foo: 9 | a: str 10 | 11 | 12 | @strawberry.type 13 | class Bar: 14 | b: str 15 | 16 | 17 | @strawberry.type 18 | class Baz: 19 | c: str 20 | 21 | 22 | @strawberry.type 23 | class Qux: 24 | d: str 25 | 26 | 27 | # this is the union type 28 | 29 | Union1 = Annotated[Foo | Bar | Baz | Qux, strawberry.union(name="Union1")] 30 | Union2 = Annotated[Baz | Qux, strawberry.union(name="Union2")] 31 | -------------------------------------------------------------------------------- /tests/codegen/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/codegen/__init__.py -------------------------------------------------------------------------------- /tests/codegen/lazy_type.py: -------------------------------------------------------------------------------- 1 | import strawberry 2 | 3 | 4 | @strawberry.type 5 | class LaziestType: 6 | something: bool 7 | -------------------------------------------------------------------------------- /tests/codegen/queries/alias.graphql: -------------------------------------------------------------------------------- 1 | query OperationName { 2 | id 3 | second_id: id 4 | a_float: float 5 | lazy { 6 | lazy: something 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/codegen/queries/basic.graphql: -------------------------------------------------------------------------------- 1 | query OperationName { 2 | id 3 | integer 4 | float 5 | boolean 6 | uuid 7 | date 8 | datetime 9 | time 10 | decimal 11 | lazy { 12 | something 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/codegen/queries/custom_scalar.graphql: -------------------------------------------------------------------------------- 1 | query OperationName { 2 | json 3 | } 4 | -------------------------------------------------------------------------------- /tests/codegen/queries/enum.graphql: -------------------------------------------------------------------------------- 1 | query OperationName { 2 | enum 3 | } 4 | -------------------------------------------------------------------------------- /tests/codegen/queries/fragment.graphql: -------------------------------------------------------------------------------- 1 | fragment PersonName on Person { 2 | name 3 | } 4 | 5 | query OperationName { 6 | person { 7 | ...PersonName 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/codegen/queries/generic_types.graphql: -------------------------------------------------------------------------------- 1 | query ListLifeGeneric { 2 | listLife { 3 | items1 { 4 | name 5 | age 6 | } 7 | items2 { 8 | name 9 | age 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/codegen/queries/interface.graphql: -------------------------------------------------------------------------------- 1 | query OperationName { 2 | interface { 3 | id 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/codegen/queries/interface_fragments.graphql: -------------------------------------------------------------------------------- 1 | query OperationName { 2 | interface { 3 | id 4 | ... on BlogPost { 5 | title 6 | } 7 | ... on Image { 8 | url 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/codegen/queries/interface_fragments_with_spread.graphql: -------------------------------------------------------------------------------- 1 | fragment PartialBlogPost on BlogPost { 2 | title 3 | } 4 | 5 | query OperationName { 6 | interface { 7 | id 8 | ... on BlogPost { 9 | ...PartialBlogPost 10 | } 11 | ... on Image { 12 | url 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/codegen/queries/interface_single_fragment.graphql: -------------------------------------------------------------------------------- 1 | query OperationName { 2 | interface { 3 | id 4 | ... on BlogPost { 5 | title 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/codegen/queries/multiple_types.graphql: -------------------------------------------------------------------------------- 1 | query OperationName { 2 | person { 3 | name 4 | } 5 | listOfPeople { 6 | name 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/codegen/queries/multiple_types_optional.graphql: -------------------------------------------------------------------------------- 1 | query OperationName { 2 | optionalPerson { 3 | name 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/codegen/queries/mutation-fragment.graphql: -------------------------------------------------------------------------------- 1 | fragment IdFragment on BlogPost { 2 | id 3 | } 4 | 5 | mutation addBook($input: String!) { 6 | addBook(input: $input) { 7 | ...IdFragment 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/codegen/queries/mutation.graphql: -------------------------------------------------------------------------------- 1 | mutation addBook($input: String!) { 2 | addBook(input: $input) { 3 | id 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/codegen/queries/mutation_with_object.graphql: -------------------------------------------------------------------------------- 1 | mutation AddBlogPosts($input: [BlogPostInput!]!) { 2 | addBlogPosts(input: {posts: $input}) { 3 | posts { 4 | title 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/codegen/queries/nullable_list_of_non_scalars.graphql: -------------------------------------------------------------------------------- 1 | query OperationName { 2 | optionalListOfPeople { 3 | name 4 | age 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/codegen/queries/optional_and_lists.graphql: -------------------------------------------------------------------------------- 1 | query OperationName { 2 | optionalInt 3 | listOfInt 4 | listOfOptionalInt 5 | optionalListOfOptionalInt 6 | } 7 | -------------------------------------------------------------------------------- /tests/codegen/queries/union.graphql: -------------------------------------------------------------------------------- 1 | query OperationName { 2 | union { 3 | ... on Animal { 4 | age 5 | } 6 | ... on Person { 7 | name 8 | } 9 | } 10 | optionalUnion { 11 | ... on Animal { 12 | age 13 | } 14 | ... on Person { 15 | name 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/codegen/queries/union_return.graphql: -------------------------------------------------------------------------------- 1 | query OperationName { 2 | getPersonOrAnimal { 3 | ... on Person { 4 | name 5 | age 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/codegen/queries/union_with_one_type.graphql: -------------------------------------------------------------------------------- 1 | query OperationName { 2 | union { 3 | ... on Animal { 4 | age 5 | name 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/codegen/queries/union_with_typename.graphql: -------------------------------------------------------------------------------- 1 | query OperationName { 2 | __typename 3 | union { 4 | ... on Animal { 5 | age 6 | } 7 | ... on Person { 8 | name 9 | } 10 | } 11 | optionalUnion { 12 | ... on Animal { 13 | age 14 | } 15 | ... on Person { 16 | name 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/codegen/queries/union_with_typename_and_fragment.graphql: -------------------------------------------------------------------------------- 1 | fragment AnimalProjection on Animal { 2 | age 3 | } 4 | 5 | query OperationName { 6 | __typename 7 | union { 8 | ... on Animal { 9 | ...AnimalProjection 10 | } 11 | ... on Person { 12 | name 13 | } 14 | } 15 | optionalUnion { 16 | ... on Animal { 17 | ...AnimalProjection 18 | } 19 | ... on Person { 20 | name 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/codegen/queries/variables.graphql: -------------------------------------------------------------------------------- 1 | query OperationName($id: ID, $input: ExampleInput!, $ids: [ID!]!, $ids2: [ID], $ids3: [[ID]]) { 2 | withInputs(id: $id, input: $input) 3 | } 4 | -------------------------------------------------------------------------------- /tests/codegen/queries/with_directives.graphql: -------------------------------------------------------------------------------- 1 | query OperationName @owner(name: "Patrick", age: 30, items: [1, 2, 3], enum: NAME, bool: true) { 2 | person { 3 | name @root 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/alias.py: -------------------------------------------------------------------------------- 1 | class OperationNameResultLazy: 2 | # alias for something 3 | lazy: bool 4 | 5 | class OperationNameResult: 6 | id: str 7 | # alias for id 8 | second_id: str 9 | # alias for float 10 | a_float: float 11 | lazy: OperationNameResultLazy 12 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/basic.py: -------------------------------------------------------------------------------- 1 | from uuid import UUID 2 | from datetime import date, datetime, time 3 | from decimal import Decimal 4 | 5 | class OperationNameResultLazy: 6 | something: bool 7 | 8 | class OperationNameResult: 9 | id: str 10 | integer: int 11 | float: float 12 | boolean: bool 13 | uuid: UUID 14 | date: date 15 | datetime: datetime 16 | time: time 17 | decimal: Decimal 18 | lazy: OperationNameResultLazy 19 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/custom_scalar.py: -------------------------------------------------------------------------------- 1 | from typing import NewType 2 | 3 | JSON = NewType("JSON", str) 4 | 5 | class OperationNameResult: 6 | json: JSON 7 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/enum.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | class Color(Enum): 4 | RED = "RED" 5 | GREEN = "GREEN" 6 | BLUE = "BLUE" 7 | 8 | class OperationNameResult: 9 | enum: Color 10 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/fragment.py: -------------------------------------------------------------------------------- 1 | class PersonName: 2 | # typename: Person 3 | name: str 4 | 5 | class OperationNameResult: 6 | person: PersonName 7 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/generic_types.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | class ListLifeGenericResultListLifeItems1: 4 | name: str 5 | age: int 6 | 7 | class ListLifeGenericResultListLifeItems2: 8 | name: str 9 | age: int 10 | 11 | class ListLifeGenericResultListLife: 12 | items1: list[ListLifeGenericResultListLifeItems1] 13 | items2: list[ListLifeGenericResultListLifeItems2] 14 | 15 | class ListLifeGenericResult: 16 | list_life: ListLifeGenericResultListLife 17 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/interface.py: -------------------------------------------------------------------------------- 1 | class OperationNameResultInterface: 2 | id: str 3 | 4 | class OperationNameResult: 5 | interface: OperationNameResultInterface 6 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/interface_fragments.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | class OperationNameResultInterfaceBlogPost: 4 | # typename: BlogPost 5 | id: str 6 | title: str 7 | 8 | class OperationNameResultInterfaceImage: 9 | # typename: Image 10 | id: str 11 | url: str 12 | 13 | OperationNameResultInterface = Union[OperationNameResultInterfaceBlogPost, OperationNameResultInterfaceImage] 14 | 15 | class OperationNameResult: 16 | interface: OperationNameResultInterface 17 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/interface_fragments_with_spread.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | class PartialBlogPost: 4 | # typename: BlogPost 5 | title: str 6 | 7 | class OperationNameResultInterfaceBlogPost: 8 | # typename: BlogPost 9 | id: str 10 | title: str 11 | 12 | class OperationNameResultInterfaceImage: 13 | # typename: Image 14 | id: str 15 | url: str 16 | 17 | OperationNameResultInterface = Union[OperationNameResultInterfaceBlogPost, OperationNameResultInterfaceImage] 18 | 19 | class OperationNameResult: 20 | interface: OperationNameResultInterface 21 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/interface_single_fragment.py: -------------------------------------------------------------------------------- 1 | class OperationNameResultInterfaceBlogPost: 2 | # typename: BlogPost 3 | id: str 4 | title: str 5 | 6 | class OperationNameResult: 7 | interface: OperationNameResultInterfaceBlogPost 8 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/multiple_types.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | class OperationNameResultPerson: 4 | name: str 5 | 6 | class OperationNameResultListOfPeople: 7 | name: str 8 | 9 | class OperationNameResult: 10 | person: OperationNameResultPerson 11 | list_of_people: list[OperationNameResultListOfPeople] 12 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/multiple_types_optional.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | class OperationNameResultOptionalPerson: 4 | name: str 5 | 6 | class OperationNameResult: 7 | optional_person: Optional[OperationNameResultOptionalPerson] 8 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/mutation-fragment.py: -------------------------------------------------------------------------------- 1 | class IdFragment: 2 | # typename: BlogPost 3 | id: str 4 | 5 | class addBookResult: 6 | add_book: IdFragment 7 | 8 | class addBookVariables: 9 | input: str 10 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/mutation.py: -------------------------------------------------------------------------------- 1 | class addBookResultAddBook: 2 | id: str 3 | 4 | class addBookResult: 5 | add_book: addBookResultAddBook 6 | 7 | class addBookVariables: 8 | input: str 9 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/mutation_with_object.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional 2 | from enum import Enum 3 | 4 | class AddBlogPostsResultAddBlogPostsPosts: 5 | title: str 6 | 7 | class AddBlogPostsResultAddBlogPosts: 8 | posts: list[AddBlogPostsResultAddBlogPostsPosts] 9 | 10 | class AddBlogPostsResult: 11 | add_blog_posts: AddBlogPostsResultAddBlogPosts 12 | 13 | class Color(Enum): 14 | RED = "RED" 15 | GREEN = "GREEN" 16 | BLUE = "BLUE" 17 | 18 | class BlogPostInput: 19 | title: str = "I replaced my doorbell. You wouldn't believe what happened next!" 20 | color: Color = Color.RED 21 | pi: float = 3.14159 22 | a_bool: bool = True 23 | an_int: int = 42 24 | an_optional_int: Optional[int] = None 25 | 26 | class AddBlogPostsVariables: 27 | input: list[BlogPostInput] 28 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/nullable_list_of_non_scalars.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional 2 | 3 | class OperationNameResultOptionalListOfPeople: 4 | name: str 5 | age: int 6 | 7 | class OperationNameResult: 8 | optional_list_of_people: Optional[list[OperationNameResultOptionalListOfPeople]] 9 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/optional_and_lists.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional 2 | 3 | class OperationNameResult: 4 | optional_int: Optional[int] 5 | list_of_int: list[int] 6 | list_of_optional_int: list[Optional[int]] 7 | optional_list_of_optional_int: Optional[list[Optional[int]]] 8 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/union.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Union 2 | 3 | class OperationNameResultUnionAnimal: 4 | # typename: Animal 5 | age: int 6 | 7 | class OperationNameResultUnionPerson: 8 | # typename: Person 9 | name: str 10 | 11 | OperationNameResultUnion = Union[OperationNameResultUnionAnimal, OperationNameResultUnionPerson] 12 | 13 | class OperationNameResultOptionalUnionAnimal: 14 | # typename: Animal 15 | age: int 16 | 17 | class OperationNameResultOptionalUnionPerson: 18 | # typename: Person 19 | name: str 20 | 21 | OperationNameResultOptionalUnion = Union[OperationNameResultOptionalUnionAnimal, OperationNameResultOptionalUnionPerson] 22 | 23 | class OperationNameResult: 24 | union: OperationNameResultUnion 25 | optional_union: Optional[OperationNameResultOptionalUnion] 26 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/union_return.py: -------------------------------------------------------------------------------- 1 | class OperationNameResultGetPersonOrAnimalPerson: 2 | # typename: Person 3 | name: str 4 | age: int 5 | 6 | class OperationNameResult: 7 | get_person_or_animal: OperationNameResultGetPersonOrAnimalPerson 8 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/union_with_one_type.py: -------------------------------------------------------------------------------- 1 | class OperationNameResultUnionAnimal: 2 | # typename: Animal 3 | age: int 4 | name: str 5 | 6 | class OperationNameResult: 7 | union: OperationNameResultUnionAnimal 8 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/union_with_typename.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Union 2 | 3 | class OperationNameResultUnionAnimal: 4 | # typename: Animal 5 | age: int 6 | 7 | class OperationNameResultUnionPerson: 8 | # typename: Person 9 | name: str 10 | 11 | OperationNameResultUnion = Union[OperationNameResultUnionAnimal, OperationNameResultUnionPerson] 12 | 13 | class OperationNameResultOptionalUnionAnimal: 14 | # typename: Animal 15 | age: int 16 | 17 | class OperationNameResultOptionalUnionPerson: 18 | # typename: Person 19 | name: str 20 | 21 | OperationNameResultOptionalUnion = Union[OperationNameResultOptionalUnionAnimal, OperationNameResultOptionalUnionPerson] 22 | 23 | class OperationNameResult: 24 | union: OperationNameResultUnion 25 | optional_union: Optional[OperationNameResultOptionalUnion] 26 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/union_with_typename_and_fragment.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Union 2 | 3 | class AnimalProjection: 4 | # typename: Animal 5 | age: int 6 | 7 | class OperationNameResultUnionPerson: 8 | # typename: Person 9 | name: str 10 | 11 | OperationNameResultUnion = Union[AnimalProjection, OperationNameResultUnionPerson] 12 | 13 | class OperationNameResultOptionalUnionPerson: 14 | # typename: Person 15 | name: str 16 | 17 | OperationNameResultOptionalUnion = Union[AnimalProjection, OperationNameResultOptionalUnionPerson] 18 | 19 | class OperationNameResult: 20 | union: OperationNameResultUnion 21 | optional_union: Optional[OperationNameResultOptionalUnion] 22 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/variables.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional 2 | 3 | class OperationNameResult: 4 | with_inputs: bool 5 | 6 | class PersonInput: 7 | name: str 8 | age: Optional[int] = None 9 | 10 | class ExampleInput: 11 | id: str 12 | name: str 13 | age: int 14 | person: Optional[PersonInput] 15 | people: list[PersonInput] 16 | optional_people: Optional[list[PersonInput]] 17 | 18 | class OperationNameVariables: 19 | id: Optional[str] 20 | input: ExampleInput 21 | ids: list[str] 22 | ids2: Optional[list[Optional[str]]] 23 | ids3: Optional[list[Optional[list[Optional[str]]]]] 24 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/python/with_directives.py: -------------------------------------------------------------------------------- 1 | class OperationNameResultPerson: 2 | name: str 3 | 4 | class OperationNameResult: 5 | person: OperationNameResultPerson 6 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/alias.ts: -------------------------------------------------------------------------------- 1 | type OperationNameResultLazy = { 2 | // alias for something 3 | lazy: boolean 4 | } 5 | 6 | type OperationNameResult = { 7 | id: string 8 | // alias for id 9 | second_id: string 10 | // alias for float 11 | a_float: number 12 | lazy: OperationNameResultLazy 13 | } 14 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/basic.ts: -------------------------------------------------------------------------------- 1 | type OperationNameResultLazy = { 2 | something: boolean 3 | } 4 | 5 | type OperationNameResult = { 6 | id: string 7 | integer: number 8 | float: number 9 | boolean: boolean 10 | uuid: string 11 | date: string 12 | datetime: string 13 | time: string 14 | decimal: string 15 | lazy: OperationNameResultLazy 16 | } 17 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/custom_scalar.ts: -------------------------------------------------------------------------------- 1 | type JSON = string 2 | 3 | type OperationNameResult = { 4 | json: JSON 5 | } 6 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/enum.ts: -------------------------------------------------------------------------------- 1 | enum Color { 2 | RED = "RED", 3 | GREEN = "GREEN", 4 | BLUE = "BLUE", 5 | } 6 | 7 | type OperationNameResult = { 8 | enum: Color 9 | } 10 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/fragment.ts: -------------------------------------------------------------------------------- 1 | type PersonName = { 2 | name: string 3 | } 4 | 5 | type OperationNameResult = { 6 | person: PersonName 7 | } 8 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/generic_types.ts: -------------------------------------------------------------------------------- 1 | type ListLifeGenericResultListLifeItems1 = { 2 | name: string 3 | age: number 4 | } 5 | 6 | type ListLifeGenericResultListLifeItems2 = { 7 | name: string 8 | age: number 9 | } 10 | 11 | type ListLifeGenericResultListLife = { 12 | items1: ListLifeGenericResultListLifeItems1[] 13 | items2: ListLifeGenericResultListLifeItems2[] 14 | } 15 | 16 | type ListLifeGenericResult = { 17 | list_life: ListLifeGenericResultListLife 18 | } 19 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/interface.ts: -------------------------------------------------------------------------------- 1 | type OperationNameResultInterface = { 2 | id: string 3 | } 4 | 5 | type OperationNameResult = { 6 | interface: OperationNameResultInterface 7 | } 8 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/interface_fragments.ts: -------------------------------------------------------------------------------- 1 | type OperationNameResultInterfaceBlogPost = { 2 | id: string 3 | title: string 4 | } 5 | 6 | type OperationNameResultInterfaceImage = { 7 | id: string 8 | url: string 9 | } 10 | 11 | type OperationNameResultInterface = OperationNameResultInterfaceBlogPost | OperationNameResultInterfaceImage 12 | 13 | type OperationNameResult = { 14 | interface: OperationNameResultInterface 15 | } 16 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/interface_fragments_with_spread.ts: -------------------------------------------------------------------------------- 1 | type PartialBlogPost = { 2 | title: string 3 | } 4 | 5 | type OperationNameResultInterfaceBlogPost = { 6 | id: string 7 | title: string 8 | } 9 | 10 | type OperationNameResultInterfaceImage = { 11 | id: string 12 | url: string 13 | } 14 | 15 | type OperationNameResultInterface = OperationNameResultInterfaceBlogPost | OperationNameResultInterfaceImage 16 | 17 | type OperationNameResult = { 18 | interface: OperationNameResultInterface 19 | } 20 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/interface_single_fragment.ts: -------------------------------------------------------------------------------- 1 | type OperationNameResultInterfaceBlogPost = { 2 | id: string 3 | title: string 4 | } 5 | 6 | type OperationNameResult = { 7 | interface: OperationNameResultInterfaceBlogPost 8 | } 9 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/multiple_types.ts: -------------------------------------------------------------------------------- 1 | type OperationNameResultPerson = { 2 | name: string 3 | } 4 | 5 | type OperationNameResultListOfPeople = { 6 | name: string 7 | } 8 | 9 | type OperationNameResult = { 10 | person: OperationNameResultPerson 11 | list_of_people: OperationNameResultListOfPeople[] 12 | } 13 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/multiple_types_optional.ts: -------------------------------------------------------------------------------- 1 | type OperationNameResultOptionalPerson = { 2 | name: string 3 | } 4 | 5 | type OperationNameResult = { 6 | optional_person: OperationNameResultOptionalPerson | undefined 7 | } 8 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/mutation-fragment.ts: -------------------------------------------------------------------------------- 1 | type IdFragment = { 2 | id: string 3 | } 4 | 5 | type addBookResult = { 6 | add_book: IdFragment 7 | } 8 | 9 | type addBookVariables = { 10 | input: string 11 | } 12 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/mutation.ts: -------------------------------------------------------------------------------- 1 | type addBookResultAddBook = { 2 | id: string 3 | } 4 | 5 | type addBookResult = { 6 | add_book: addBookResultAddBook 7 | } 8 | 9 | type addBookVariables = { 10 | input: string 11 | } 12 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/mutation_with_object.ts: -------------------------------------------------------------------------------- 1 | type AddBlogPostsResultAddBlogPostsPosts = { 2 | title: string 3 | } 4 | 5 | type AddBlogPostsResultAddBlogPosts = { 6 | posts: AddBlogPostsResultAddBlogPostsPosts[] 7 | } 8 | 9 | type AddBlogPostsResult = { 10 | add_blog_posts: AddBlogPostsResultAddBlogPosts 11 | } 12 | 13 | enum Color { 14 | RED = "RED", 15 | GREEN = "GREEN", 16 | BLUE = "BLUE", 17 | } 18 | 19 | type BlogPostInput = { 20 | title: string 21 | color: Color 22 | pi: number 23 | a_bool: boolean 24 | an_int: number 25 | an_optional_int: number | undefined 26 | } 27 | 28 | type AddBlogPostsVariables = { 29 | input: BlogPostInput[] 30 | } 31 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/nullable_list_of_non_scalars.ts: -------------------------------------------------------------------------------- 1 | type OperationNameResultOptionalListOfPeople = { 2 | name: string 3 | age: number 4 | } 5 | 6 | type OperationNameResult = { 7 | optional_list_of_people: OperationNameResultOptionalListOfPeople[] | undefined 8 | } 9 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/optional_and_lists.ts: -------------------------------------------------------------------------------- 1 | type OperationNameResult = { 2 | optional_int: number | undefined 3 | list_of_int: number[] 4 | list_of_optional_int: (number | undefined)[] 5 | optional_list_of_optional_int: (number | undefined)[] | undefined 6 | } 7 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/union.ts: -------------------------------------------------------------------------------- 1 | type OperationNameResultUnionAnimal = { 2 | age: number 3 | } 4 | 5 | type OperationNameResultUnionPerson = { 6 | name: string 7 | } 8 | 9 | type OperationNameResultUnion = OperationNameResultUnionAnimal | OperationNameResultUnionPerson 10 | 11 | type OperationNameResultOptionalUnionAnimal = { 12 | age: number 13 | } 14 | 15 | type OperationNameResultOptionalUnionPerson = { 16 | name: string 17 | } 18 | 19 | type OperationNameResultOptionalUnion = OperationNameResultOptionalUnionAnimal | OperationNameResultOptionalUnionPerson 20 | 21 | type OperationNameResult = { 22 | union: OperationNameResultUnion 23 | optional_union: OperationNameResultOptionalUnion | undefined 24 | } 25 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/union_return.ts: -------------------------------------------------------------------------------- 1 | type OperationNameResultGetPersonOrAnimalPerson = { 2 | name: string 3 | age: number 4 | } 5 | 6 | type OperationNameResult = { 7 | get_person_or_animal: OperationNameResultGetPersonOrAnimalPerson 8 | } 9 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/union_with_one_type.ts: -------------------------------------------------------------------------------- 1 | type OperationNameResultUnionAnimal = { 2 | age: number 3 | name: string 4 | } 5 | 6 | type OperationNameResult = { 7 | union: OperationNameResultUnionAnimal 8 | } 9 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/union_with_typename.ts: -------------------------------------------------------------------------------- 1 | type OperationNameResultUnionAnimal = { 2 | age: number 3 | } 4 | 5 | type OperationNameResultUnionPerson = { 6 | name: string 7 | } 8 | 9 | type OperationNameResultUnion = OperationNameResultUnionAnimal | OperationNameResultUnionPerson 10 | 11 | type OperationNameResultOptionalUnionAnimal = { 12 | age: number 13 | } 14 | 15 | type OperationNameResultOptionalUnionPerson = { 16 | name: string 17 | } 18 | 19 | type OperationNameResultOptionalUnion = OperationNameResultOptionalUnionAnimal | OperationNameResultOptionalUnionPerson 20 | 21 | type OperationNameResult = { 22 | __typename: string 23 | union: OperationNameResultUnion 24 | optional_union: OperationNameResultOptionalUnion | undefined 25 | } 26 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/union_with_typename_and_fragment.ts: -------------------------------------------------------------------------------- 1 | type AnimalProjection = { 2 | age: number 3 | } 4 | 5 | type OperationNameResultUnionPerson = { 6 | name: string 7 | } 8 | 9 | type OperationNameResultUnion = AnimalProjection | OperationNameResultUnionPerson 10 | 11 | type OperationNameResultOptionalUnionPerson = { 12 | name: string 13 | } 14 | 15 | type OperationNameResultOptionalUnion = AnimalProjection | OperationNameResultOptionalUnionPerson 16 | 17 | type OperationNameResult = { 18 | __typename: string 19 | union: OperationNameResultUnion 20 | optional_union: OperationNameResultOptionalUnion | undefined 21 | } 22 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/variables.ts: -------------------------------------------------------------------------------- 1 | type OperationNameResult = { 2 | with_inputs: boolean 3 | } 4 | 5 | type PersonInput = { 6 | name: string 7 | age: number | undefined 8 | } 9 | 10 | type ExampleInput = { 11 | id: string 12 | name: string 13 | age: number 14 | person: PersonInput | undefined 15 | people: PersonInput[] 16 | optional_people: PersonInput[] | undefined 17 | } 18 | 19 | type OperationNameVariables = { 20 | id: string | undefined 21 | input: ExampleInput 22 | ids: string[] 23 | ids2: (string | undefined)[] | undefined 24 | ids3: ((string | undefined)[] | undefined)[] | undefined 25 | } 26 | -------------------------------------------------------------------------------- /tests/codegen/snapshots/typescript/with_directives.ts: -------------------------------------------------------------------------------- 1 | type OperationNameResultPerson = { 2 | name: string 3 | } 4 | 5 | type OperationNameResult = { 6 | person: OperationNameResultPerson 7 | } 8 | -------------------------------------------------------------------------------- /tests/codegen/test_print_operation.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import pytest 4 | 5 | from strawberry.codegen import QueryCodegen 6 | from strawberry.codegen.plugins.print_operation import PrintOperationPlugin 7 | 8 | HERE = Path(__file__).parent 9 | QUERIES = list(HERE.glob("queries/*.graphql")) 10 | 11 | 12 | @pytest.mark.parametrize("query", QUERIES, ids=[x.name for x in QUERIES]) 13 | def test_codegen( 14 | query: Path, 15 | schema, 16 | ): 17 | generator = QueryCodegen(schema, plugins=[PrintOperationPlugin(query)]) 18 | query_content = query.read_text() 19 | 20 | result = generator.run(query_content) 21 | 22 | assert result.to_string() == query_content 23 | -------------------------------------------------------------------------------- /tests/codemods/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/codemods/__init__.py -------------------------------------------------------------------------------- /tests/d.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING, Annotated 4 | 5 | import strawberry 6 | 7 | if TYPE_CHECKING: 8 | from tests.c import C 9 | 10 | 11 | @strawberry.type 12 | class D: 13 | id: strawberry.ID 14 | 15 | @strawberry.field 16 | async def c_list( 17 | self, 18 | ) -> list[Annotated[C, strawberry.lazy("tests.c")]]: # pragma: no cover 19 | from tests.c import C 20 | 21 | return [C(id=self.id)] 22 | -------------------------------------------------------------------------------- /tests/django/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/django/__init__.py -------------------------------------------------------------------------------- /tests/django/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/django/app/__init__.py -------------------------------------------------------------------------------- /tests/django/app/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Example(models.Model): # noqa: DJ008 5 | name = models.CharField(max_length=100) 6 | -------------------------------------------------------------------------------- /tests/django/app/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from strawberry.django.views import GraphQLView as BaseGraphQLView 4 | from tests.views.schema import Query, schema 5 | 6 | 7 | class GraphQLView(BaseGraphQLView): 8 | def get_root_value(self, request) -> Query: 9 | return Query() 10 | 11 | 12 | urlpatterns = [ 13 | path("graphql/", GraphQLView.as_view(schema=schema)), 14 | ] 15 | -------------------------------------------------------------------------------- /tests/django/conftest.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | import pytest 6 | 7 | if TYPE_CHECKING: 8 | from strawberry.django.test import GraphQLTestClient 9 | 10 | 11 | @pytest.fixture 12 | def graphql_client() -> GraphQLTestClient: 13 | from django.test.client import Client 14 | 15 | from strawberry.django.test import GraphQLTestClient 16 | 17 | return GraphQLTestClient(Client()) 18 | -------------------------------------------------------------------------------- /tests/django/django_settings.py: -------------------------------------------------------------------------------- 1 | SECRET_KEY = 1 2 | 3 | INSTALLED_APPS = ["tests.django.app"] 4 | ROOT_URLCONF = "tests.django.app.urls" 5 | 6 | TEMPLATES = [ 7 | { 8 | "BACKEND": "django.template.backends.django.DjangoTemplates", 9 | "DIRS": [], 10 | "APP_DIRS": True, 11 | } 12 | ] 13 | 14 | DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}} 15 | 16 | # This is for channels integration, but only one django settings can be used 17 | # per pytest_django settings 18 | CHANNEL_LAYERS = {"default": {"BACKEND": "channels.layers.InMemoryChannelLayer"}} 19 | -------------------------------------------------------------------------------- /tests/django/test_extensions.py: -------------------------------------------------------------------------------- 1 | def test_extensions(graphql_client): 2 | query = "{ hello }" 3 | 4 | response = graphql_client.query(query) 5 | 6 | assert response.extensions["example"] == "example" 7 | -------------------------------------------------------------------------------- /tests/enums/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/enums/__init__.py -------------------------------------------------------------------------------- /tests/exceptions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/exceptions/__init__.py -------------------------------------------------------------------------------- /tests/exceptions/classes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/exceptions/classes/__init__.py -------------------------------------------------------------------------------- /tests/exceptions/test_exception_source.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | import pytest 5 | 6 | from strawberry.exceptions.exception_source import ExceptionSource 7 | 8 | pytestmark = pytest.mark.skipif( 9 | sys.platform == "win32", reason="Test is meant to run on Unix systems" 10 | ) 11 | 12 | 13 | def test_returns_relative_path(mocker): 14 | mocker.patch.object(Path, "cwd", return_value="/home/user/project/") 15 | 16 | source = ExceptionSource( 17 | path=Path("/home/user/project/src/main.py"), 18 | code="", 19 | start_line=1, 20 | end_line=1, 21 | error_line=1, 22 | error_column=1, 23 | error_column_end=1, 24 | ) 25 | 26 | assert source.path_relative_to_cwd == Path("src/main.py") 27 | 28 | 29 | def test_returns_relative_path_when_is_already_relative(): 30 | source = ExceptionSource( 31 | path=Path("src/main.py"), 32 | code="", 33 | start_line=1, 34 | end_line=1, 35 | error_line=1, 36 | error_column=1, 37 | error_column_end=1, 38 | ) 39 | 40 | assert source.path_relative_to_cwd == Path("src/main.py") 41 | -------------------------------------------------------------------------------- /tests/experimental/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/experimental/__init__.py -------------------------------------------------------------------------------- /tests/experimental/pydantic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/experimental/pydantic/__init__.py -------------------------------------------------------------------------------- /tests/experimental/pydantic/schema/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/experimental/pydantic/schema/__init__.py -------------------------------------------------------------------------------- /tests/experimental/pydantic/schema/test_forward_reference.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import textwrap 4 | from typing import Optional 5 | 6 | import pydantic 7 | 8 | import strawberry 9 | 10 | 11 | def test_auto_fields(): 12 | global User 13 | 14 | class UserModel(pydantic.BaseModel): 15 | age: int 16 | password: Optional[str] 17 | other: float 18 | 19 | @strawberry.experimental.pydantic.type(UserModel) 20 | class User: 21 | age: strawberry.auto 22 | password: strawberry.auto 23 | 24 | @strawberry.type 25 | class Query: 26 | @strawberry.field 27 | def user(self) -> User: 28 | return User(age=1, password="ABC") 29 | 30 | schema = strawberry.Schema(query=Query) 31 | 32 | expected_schema = """ 33 | type Query { 34 | user: User! 35 | } 36 | 37 | type User { 38 | age: Int! 39 | password: String 40 | } 41 | """ 42 | 43 | assert str(schema) == textwrap.dedent(expected_schema).strip() 44 | 45 | query = "{ user { age } }" 46 | 47 | result = schema.execute_sync(query) 48 | 49 | assert not result.errors 50 | assert result.data["user"]["age"] == 1 51 | -------------------------------------------------------------------------------- /tests/experimental/pydantic/utils.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from strawberry.experimental.pydantic._compat import IS_PYDANTIC_V2 4 | 5 | needs_pydantic_v2 = pytest.mark.skipif( 6 | not IS_PYDANTIC_V2, reason="requires Pydantic v2" 7 | ) 8 | needs_pydantic_v1 = pytest.mark.skipif(IS_PYDANTIC_V2, reason="requires Pydantic v1") 9 | -------------------------------------------------------------------------------- /tests/extensions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/extensions/__init__.py -------------------------------------------------------------------------------- /tests/extensions/tracing/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/extensions/tracing/__init__.py -------------------------------------------------------------------------------- /tests/fastapi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/fastapi/__init__.py -------------------------------------------------------------------------------- /tests/fastapi/app.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Union 2 | 3 | from fastapi import BackgroundTasks, Depends, FastAPI, Request, WebSocket 4 | from strawberry.fastapi import GraphQLRouter 5 | from tests.views.schema import schema 6 | 7 | 8 | def custom_context_dependency() -> str: 9 | return "Hi!" 10 | 11 | 12 | async def get_context( 13 | background_tasks: BackgroundTasks, 14 | request: Request = None, 15 | ws: WebSocket = None, 16 | custom_value=Depends(custom_context_dependency), 17 | ) -> dict[str, Any]: 18 | return { 19 | "custom_value": custom_value, 20 | "request": request or ws, 21 | "background_tasks": background_tasks, 22 | } 23 | 24 | 25 | async def get_root_value( 26 | request: Request = None, ws: WebSocket = None 27 | ) -> Union[Request, WebSocket]: 28 | return request or ws 29 | 30 | 31 | def create_app(schema=schema, **kwargs: Any) -> FastAPI: 32 | app = FastAPI() 33 | 34 | graphql_app = GraphQLRouter( 35 | schema, context_getter=get_context, root_value_getter=get_root_value, **kwargs 36 | ) 37 | app.include_router(graphql_app, prefix="/graphql") 38 | 39 | return app 40 | -------------------------------------------------------------------------------- /tests/fastapi/test_async.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING, Optional 4 | 5 | import pytest 6 | 7 | import strawberry 8 | 9 | if TYPE_CHECKING: 10 | from starlette.testclient import TestClient 11 | 12 | 13 | @pytest.fixture 14 | def test_client() -> TestClient: 15 | from starlette.testclient import TestClient 16 | 17 | from tests.fastapi.app import create_app 18 | 19 | @strawberry.type 20 | class Query: 21 | @strawberry.field 22 | async def hello(self, name: Optional[str] = None) -> str: 23 | return f"Hello {name or 'world'}" 24 | 25 | async_schema = strawberry.Schema(Query) 26 | app = create_app(schema=async_schema) 27 | return TestClient(app) 28 | 29 | 30 | def test_simple_query(test_client: TestClient): 31 | response = test_client.post("/graphql", json={"query": "{ hello }"}) 32 | 33 | assert response.json() == {"data": {"hello": "Hello world"}} 34 | -------------------------------------------------------------------------------- /tests/federation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/federation/__init__.py -------------------------------------------------------------------------------- /tests/federation/printer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/federation/printer/__init__.py -------------------------------------------------------------------------------- /tests/federation/printer/test_interface_object.py: -------------------------------------------------------------------------------- 1 | import textwrap 2 | 3 | import strawberry 4 | 5 | 6 | def test_interface_object(): 7 | @strawberry.federation.interface_object(keys=["id"]) 8 | class SomeInterface: 9 | id: strawberry.ID 10 | 11 | schema = strawberry.federation.Schema( 12 | types=[SomeInterface], enable_federation_2=True 13 | ) 14 | 15 | expected = """ 16 | schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@interfaceObject", "@key"]) { 17 | query: Query 18 | } 19 | 20 | type Query { 21 | _entities(representations: [_Any!]!): [_Entity]! 22 | _service: _Service! 23 | } 24 | 25 | type SomeInterface @key(fields: "id") @interfaceObject { 26 | id: ID! 27 | } 28 | 29 | scalar _Any 30 | 31 | union _Entity = SomeInterface 32 | 33 | type _Service { 34 | sdl: String! 35 | } 36 | """ 37 | 38 | assert schema.as_str() == textwrap.dedent(expected).strip() 39 | -------------------------------------------------------------------------------- /tests/federation/printer/test_one_of.py: -------------------------------------------------------------------------------- 1 | import textwrap 2 | from typing import Optional 3 | 4 | import strawberry 5 | 6 | 7 | def test_prints_one_of_directive(): 8 | @strawberry.federation.input(one_of=True, tags=["myTag", "anotherTag"]) 9 | class Input: 10 | a: Optional[str] = strawberry.UNSET 11 | b: Optional[int] = strawberry.UNSET 12 | 13 | @strawberry.federation.type 14 | class Query: 15 | hello: str 16 | 17 | schema = strawberry.federation.Schema( 18 | query=Query, types=[Input], enable_federation_2=True 19 | ) 20 | 21 | expected = """ 22 | directive @oneOf on INPUT_OBJECT 23 | 24 | schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@tag"]) { 25 | query: Query 26 | } 27 | 28 | input Input @tag(name: "myTag") @tag(name: "anotherTag") @oneOf { 29 | a: String 30 | b: Int 31 | } 32 | 33 | type Query { 34 | _service: _Service! 35 | hello: String! 36 | } 37 | 38 | scalar _Any 39 | 40 | type _Service { 41 | sdl: String! 42 | } 43 | """ 44 | 45 | assert schema.as_str() == textwrap.dedent(expected).strip() 46 | -------------------------------------------------------------------------------- /tests/federation/test_types.py: -------------------------------------------------------------------------------- 1 | import strawberry 2 | 3 | 4 | def test_type(): 5 | @strawberry.federation.type(keys=["id"]) 6 | class Location: 7 | id: strawberry.ID 8 | 9 | assert Location(id=strawberry.ID("1")).id == "1" 10 | 11 | 12 | def test_type_and_override(): 13 | @strawberry.federation.type(keys=["id"]) 14 | class Location: 15 | id: strawberry.ID 16 | address: str = strawberry.federation.field(override="start") 17 | 18 | location = Location(id=strawberry.ID("1"), address="ABC") 19 | 20 | assert location.id == "1" 21 | assert location.address == "ABC" 22 | 23 | 24 | def test_type_and_override_with_resolver(): 25 | @strawberry.federation.type(keys=["id"]) 26 | class Location: 27 | id: strawberry.ID 28 | address: str = strawberry.federation.field( 29 | override="start", resolver=lambda: "ABC" 30 | ) 31 | 32 | location = Location(id=strawberry.ID("1")) 33 | 34 | assert location.id == "1" 35 | -------------------------------------------------------------------------------- /tests/fields/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/fields/__init__.py -------------------------------------------------------------------------------- /tests/fields/test_field_basics.py: -------------------------------------------------------------------------------- 1 | import strawberry 2 | 3 | 4 | def test_type_add_type_definition_with_fields(): 5 | @strawberry.type 6 | class Query: 7 | name: str 8 | age: int 9 | 10 | definition = Query.__strawberry_definition__ 11 | 12 | assert definition.name == "Query" 13 | assert len(definition.fields) == 2 14 | 15 | assert definition.fields[0].python_name == "name" 16 | assert definition.fields[0].type is str 17 | 18 | assert definition.fields[1].python_name == "age" 19 | assert definition.fields[1].type is int 20 | 21 | 22 | def test_passing_nothing_to_fields(): 23 | @strawberry.type 24 | class Query: 25 | name: str = strawberry.field() 26 | age: int = strawberry.field() 27 | 28 | definition = Query.__strawberry_definition__ 29 | 30 | assert definition.name == "Query" 31 | assert len(definition.fields) == 2 32 | 33 | assert definition.fields[0].python_name == "name" 34 | assert definition.fields[0].type is str 35 | 36 | assert definition.fields[1].python_name == "age" 37 | assert definition.fields[1].type is int 38 | -------------------------------------------------------------------------------- /tests/fields/test_field_descriptions.py: -------------------------------------------------------------------------------- 1 | import strawberry 2 | 3 | 4 | def test_field_descriptions(): 5 | description = "this description is super cool" 6 | 7 | field = strawberry.field(description=description) 8 | 9 | assert field.description == description 10 | -------------------------------------------------------------------------------- /tests/fields/test_field_names.py: -------------------------------------------------------------------------------- 1 | import strawberry 2 | 3 | 4 | def test_field_name_standard(): 5 | standard_field = strawberry.field() 6 | 7 | assert standard_field.python_name is None 8 | assert standard_field.graphql_name is None 9 | 10 | 11 | def test_field_name_standard_on_schema(): 12 | @strawberry.type() 13 | class Query: 14 | normal_field: int 15 | 16 | [field] = Query.__strawberry_definition__.fields 17 | 18 | assert field.python_name == "normal_field" 19 | assert field.graphql_name is None 20 | 21 | 22 | def test_field_name_override(): 23 | field_name = "override" 24 | 25 | standard_field = strawberry.field(name=field_name) 26 | 27 | assert standard_field.python_name is None # Set once field is added to a Schema 28 | assert standard_field.graphql_name == field_name 29 | 30 | 31 | def test_field_name_override_with_schema(): 32 | field_name = "override_name" 33 | 34 | @strawberry.type() 35 | class Query: 36 | override_field: bool = strawberry.field(name=field_name) 37 | 38 | [field] = Query.__strawberry_definition__.fields 39 | 40 | assert field.python_name == "override_field" 41 | assert field.graphql_name == field_name 42 | -------------------------------------------------------------------------------- /tests/file_uploads/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/file_uploads/__init__.py -------------------------------------------------------------------------------- /tests/fixtures/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/fixtures/__init__.py -------------------------------------------------------------------------------- /tests/fixtures/sample_package/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/fixtures/sample_package/__init__.py -------------------------------------------------------------------------------- /tests/fixtures/sample_package/sample_module.py: -------------------------------------------------------------------------------- 1 | import strawberry 2 | 3 | 4 | class SampleClass: 5 | def __init__(self, schema): 6 | self.schema = schema 7 | 8 | 9 | @strawberry.type 10 | class User: 11 | name: str 12 | age: int 13 | 14 | 15 | @strawberry.type 16 | class Query: 17 | @strawberry.field 18 | def user(self) -> User: 19 | return User(name="Patrick", age=100) 20 | 21 | 22 | def create_schema(): 23 | return strawberry.Schema(query=Query) 24 | 25 | 26 | schema = create_schema() 27 | sample_instance = SampleClass(schema) 28 | not_a_schema = 42 29 | -------------------------------------------------------------------------------- /tests/http/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/http/__init__.py -------------------------------------------------------------------------------- /tests/http/clients/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/http/clients/__init__.py -------------------------------------------------------------------------------- /tests/http/context.py: -------------------------------------------------------------------------------- 1 | def get_context(context: object) -> dict[str, object]: 2 | assert isinstance(context, dict) 3 | 4 | return {**context, "custom_value": "a value from context"} 5 | -------------------------------------------------------------------------------- /tests/http/test_http.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from strawberry.http.base import BaseView 4 | 5 | from .clients.base import HttpClient 6 | 7 | 8 | @pytest.mark.parametrize("method", ["delete", "head", "put", "patch"]) 9 | async def test_does_only_allow_get_and_post( 10 | method: str, 11 | http_client: HttpClient, 12 | ): 13 | response = await http_client.request(url="/graphql", method=method) # type: ignore 14 | 15 | assert response.status_code == 405 16 | 17 | 18 | async def test_the_http_handler_uses_the_views_decode_json_method( 19 | http_client: HttpClient, mocker 20 | ): 21 | spy = mocker.spy(BaseView, "decode_json") 22 | 23 | response = await http_client.query(query="{ hello }") 24 | assert response.status_code == 200 25 | 26 | data = response.json["data"] 27 | assert isinstance(data, dict) 28 | assert data["hello"] == "Hello world" 29 | 30 | assert spy.call_count == 1 31 | -------------------------------------------------------------------------------- /tests/http/test_mutation.py: -------------------------------------------------------------------------------- 1 | from .clients.base import HttpClient 2 | 3 | 4 | async def test_mutation(http_client: HttpClient): 5 | response = await http_client.query( 6 | query="mutation { hello }", 7 | headers={ 8 | "Content-Type": "application/json", 9 | }, 10 | ) 11 | data = response.json["data"] 12 | 13 | assert response.status_code == 200 14 | assert data["hello"] == "strawberry" 15 | -------------------------------------------------------------------------------- /tests/http/test_process_result.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing_extensions import Literal 4 | 5 | import pytest 6 | 7 | from strawberry.http import GraphQLHTTPResponse 8 | from strawberry.types import ExecutionResult 9 | 10 | from .clients.base import HttpClient 11 | 12 | 13 | def process_result(result: ExecutionResult) -> GraphQLHTTPResponse: 14 | if result.data: 15 | return { 16 | "data": {key.upper(): result for key, result in result.data.items()}, 17 | } 18 | 19 | return {} 20 | 21 | 22 | @pytest.fixture 23 | def http_client(http_client_class) -> HttpClient: 24 | return http_client_class(result_override=process_result) 25 | 26 | 27 | @pytest.mark.parametrize("method", ["get", "post"]) 28 | async def test_custom_process_result( 29 | method: Literal["get", "post"], http_client: HttpClient 30 | ): 31 | response = await http_client.query( 32 | method=method, 33 | query="{ hello }", 34 | ) 35 | assert response.json["data"] == {"HELLO": "Hello world"} 36 | -------------------------------------------------------------------------------- /tests/litestar/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/litestar/__init__.py -------------------------------------------------------------------------------- /tests/litestar/app.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from litestar import Litestar, Request 4 | from litestar.di import Provide 5 | from strawberry.litestar import make_graphql_controller 6 | from tests.views.schema import schema 7 | 8 | 9 | def custom_context_dependency() -> str: 10 | return "Hi!" 11 | 12 | 13 | async def get_root_value(request: Request = None): 14 | return request 15 | 16 | 17 | async def get_context(app_dependency: str, request: Request = None): 18 | return {"custom_value": app_dependency, "request": request} 19 | 20 | 21 | def create_app(schema=schema, **kwargs: Any): 22 | GraphQLController = make_graphql_controller( 23 | schema, 24 | path="/graphql", 25 | context_getter=get_context, 26 | root_value_getter=get_root_value, 27 | **kwargs, 28 | ) 29 | 30 | return Litestar( 31 | route_handlers=[GraphQLController], 32 | dependencies={ 33 | "app_dependency": Provide(custom_context_dependency, sync_to_thread=True) 34 | }, 35 | ) 36 | -------------------------------------------------------------------------------- /tests/litestar/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.fixture 5 | def test_client(): 6 | from litestar.testing import TestClient 7 | from tests.litestar.app import create_app 8 | 9 | app = create_app() 10 | return TestClient(app) 11 | 12 | 13 | @pytest.fixture 14 | def test_client_keep_alive(): 15 | from litestar.testing import TestClient 16 | from tests.litestar.app import create_app 17 | 18 | app = create_app(keep_alive=True, keep_alive_interval=0.1) 19 | return TestClient(app) 20 | -------------------------------------------------------------------------------- /tests/objects/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/objects/__init__.py -------------------------------------------------------------------------------- /tests/objects/generics/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/objects/generics/__init__.py -------------------------------------------------------------------------------- /tests/objects/test_inheritance.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | import strawberry 4 | 5 | 6 | def test_inherited_fields(): 7 | @strawberry.type 8 | class A: 9 | a: str = strawberry.field(default="") 10 | 11 | @strawberry.type 12 | class B(A): 13 | b: Optional[str] = strawberry.field(default=None) 14 | 15 | assert strawberry.Schema(query=B) 16 | -------------------------------------------------------------------------------- /tests/objects/test_object_instantiation.py: -------------------------------------------------------------------------------- 1 | import strawberry 2 | 3 | 4 | def test_can_instantiate_types_directly(): 5 | @strawberry.type 6 | class User: 7 | username: str 8 | 9 | @strawberry.field 10 | def email(self) -> str: 11 | return self.username + "@somesite.com" 12 | 13 | user = User(username="abc") 14 | assert user.username == "abc" 15 | assert user.email() == "abc@somesite.com" 16 | -------------------------------------------------------------------------------- /tests/plugins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/plugins/__init__.py -------------------------------------------------------------------------------- /tests/python_312/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/python_312/__init__.py -------------------------------------------------------------------------------- /tests/relay/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/relay/__init__.py -------------------------------------------------------------------------------- /tests/sanic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/sanic/__init__.py -------------------------------------------------------------------------------- /tests/schema/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/schema/__init__.py -------------------------------------------------------------------------------- /tests/schema/extensions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/schema/extensions/__init__.py -------------------------------------------------------------------------------- /tests/schema/extensions/schema_extensions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/schema/extensions/schema_extensions/__init__.py -------------------------------------------------------------------------------- /tests/schema/extensions/test_imports.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | def test_can_import(mocker): 5 | # mocking sys.modules.ddtrace so we don't get an ImportError 6 | mocker.patch.dict("sys.modules", ddtrace=mocker.MagicMock()) 7 | 8 | 9 | def test_fails_if_import_is_not_found(): 10 | with pytest.raises(ImportError): 11 | from strawberry.extensions.tracing import Blueberry # noqa: F401 12 | -------------------------------------------------------------------------------- /tests/schema/extensions/test_input_mutation_future.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import textwrap 4 | from uuid import UUID 5 | 6 | import strawberry 7 | from strawberry.field_extensions import InputMutationExtension 8 | 9 | 10 | @strawberry.type 11 | class Query: 12 | @strawberry.field 13 | async def hello(self) -> str: 14 | return "hi" 15 | 16 | 17 | @strawberry.type 18 | class Mutation: 19 | @strawberry.mutation(extensions=[InputMutationExtension()]) 20 | async def buggy(self, some_id: UUID) -> None: 21 | del some_id 22 | 23 | 24 | def test_schema(): 25 | schema = strawberry.Schema(query=Query, mutation=Mutation) 26 | 27 | expected_schema = ''' 28 | input BuggyInput { 29 | someId: UUID! 30 | } 31 | 32 | type Mutation { 33 | buggy( 34 | """Input data for `buggy` mutation""" 35 | input: BuggyInput! 36 | ): Void 37 | } 38 | 39 | type Query { 40 | hello: String! 41 | } 42 | 43 | scalar UUID 44 | 45 | """Represents NULL values""" 46 | scalar Void 47 | ''' 48 | 49 | assert textwrap.dedent(expected_schema).strip() == str(schema).strip() 50 | -------------------------------------------------------------------------------- /tests/schema/test_annotated/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/schema/test_annotated/__init__.py -------------------------------------------------------------------------------- /tests/schema/test_annotated/type_a.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import Annotated, Optional 4 | from uuid import UUID 5 | 6 | import strawberry 7 | 8 | 9 | @strawberry.type 10 | class Query: 11 | @strawberry.field 12 | def get_testing( 13 | self, 14 | info: strawberry.Info, 15 | id_: Annotated[UUID, strawberry.argument(name="id")], 16 | ) -> Optional[str]: ... 17 | -------------------------------------------------------------------------------- /tests/schema/test_annotated/type_b.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import Annotated, Optional 4 | from uuid import UUID 5 | 6 | import strawberry 7 | 8 | 9 | @strawberry.type 10 | class Query: 11 | @strawberry.field 12 | def get_testing( 13 | self, 14 | id_: Annotated[UUID, strawberry.argument(name="id")], 15 | info: strawberry.Info, 16 | ) -> Optional[str]: ... 17 | -------------------------------------------------------------------------------- /tests/schema/test_config.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from strawberry.schema.config import StrawberryConfig 4 | from strawberry.types.info import Info 5 | 6 | 7 | def test_config_post_init_auto_camel_case(): 8 | config = StrawberryConfig(auto_camel_case=True) 9 | 10 | assert config.name_converter.auto_camel_case is True 11 | 12 | 13 | def test_config_post_init_no_auto_camel_case(): 14 | config = StrawberryConfig(auto_camel_case=False) 15 | 16 | assert config.name_converter.auto_camel_case is False 17 | 18 | 19 | def test_config_post_init_info_class(): 20 | class CustomInfo(Info): 21 | test: str = "foo" 22 | 23 | config = StrawberryConfig(info_class=CustomInfo) 24 | 25 | assert config.info_class is CustomInfo 26 | assert config.info_class.test == "foo" 27 | 28 | 29 | def test_config_post_init_info_class_is_default(): 30 | config = StrawberryConfig() 31 | 32 | assert config.info_class is Info 33 | 34 | 35 | def test_config_post_init_info_class_is_not_subclass(): 36 | with pytest.raises(TypeError) as exc_info: 37 | StrawberryConfig(info_class=object) 38 | 39 | assert str(exc_info.value) == "`info_class` must be a subclass of strawberry.Info" 40 | -------------------------------------------------------------------------------- /tests/schema/test_dataloaders.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | import pytest 4 | 5 | import strawberry 6 | from strawberry.dataloader import DataLoader 7 | 8 | 9 | @pytest.mark.asyncio 10 | async def test_can_use_dataloaders(mocker): 11 | @dataclass 12 | class User: 13 | id: str 14 | 15 | async def idx(keys) -> list[User]: 16 | return [User(key) for key in keys] 17 | 18 | mock_loader = mocker.Mock(side_effect=idx) 19 | 20 | loader = DataLoader(load_fn=mock_loader) 21 | 22 | @strawberry.type 23 | class Query: 24 | @strawberry.field 25 | async def get_user(self, id: strawberry.ID) -> str: 26 | user = await loader.load(id) 27 | 28 | return user.id 29 | 30 | schema = strawberry.Schema(query=Query) 31 | 32 | query = """{ 33 | a: getUser(id: "1") 34 | b: getUser(id: "2") 35 | }""" 36 | 37 | result = await schema.execute(query) 38 | 39 | assert not result.errors 40 | assert result.data == { 41 | "a": "1", 42 | "b": "2", 43 | } 44 | 45 | mock_loader.assert_called_once_with(["1", "2"]) 46 | -------------------------------------------------------------------------------- /tests/schema/test_lazy/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/schema/test_lazy/__init__.py -------------------------------------------------------------------------------- /tests/schema/test_lazy/schema.py: -------------------------------------------------------------------------------- 1 | import strawberry 2 | from tests.schema.test_lazy.type_c import Query 3 | 4 | schema = strawberry.Schema(query=Query) 5 | -------------------------------------------------------------------------------- /tests/schema/test_lazy/test_lazy.py: -------------------------------------------------------------------------------- 1 | import textwrap 2 | 3 | import strawberry 4 | from strawberry.printer import print_schema 5 | 6 | 7 | def test_cyclic_import(): 8 | from .type_a import TypeA 9 | from .type_b import TypeB 10 | 11 | @strawberry.type 12 | class Query: 13 | a: TypeA 14 | b: TypeB 15 | 16 | expected = """ 17 | type Query { 18 | a: TypeA! 19 | b: TypeB! 20 | } 21 | 22 | type TypeA { 23 | listOfB: [TypeB!] 24 | typeB: TypeB! 25 | } 26 | 27 | type TypeB { 28 | typeA: TypeA! 29 | typeAList: [TypeA!]! 30 | typeCList: [TypeC!]! 31 | } 32 | 33 | type TypeC { 34 | name: String! 35 | } 36 | """ 37 | 38 | schema = strawberry.Schema(Query) 39 | 40 | assert print_schema(schema) == textwrap.dedent(expected).strip() 41 | -------------------------------------------------------------------------------- /tests/schema/test_lazy/type_a.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING, Annotated, Optional 2 | 3 | import strawberry 4 | 5 | if TYPE_CHECKING: 6 | from .type_b import TypeB 7 | 8 | 9 | @strawberry.type 10 | class TypeA: 11 | list_of_b: Optional[ 12 | list[Annotated["TypeB", strawberry.lazy("tests.schema.test_lazy.type_b")]] 13 | ] = None 14 | 15 | @strawberry.field 16 | def type_b(self) -> Annotated["TypeB", strawberry.lazy(".type_b")]: 17 | from .type_b import TypeB 18 | 19 | return TypeB() 20 | -------------------------------------------------------------------------------- /tests/schema/test_lazy/type_b.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING, Annotated 2 | 3 | import strawberry 4 | 5 | if TYPE_CHECKING: 6 | from .type_a import TypeA 7 | from .type_c import TypeC 8 | 9 | ListTypeA = list[TypeA] 10 | ListTypeC = list[TypeC] 11 | else: 12 | TypeA = Annotated["TypeA", strawberry.lazy("tests.schema.test_lazy.type_a")] 13 | ListTypeA = list[ 14 | Annotated["TypeA", strawberry.lazy("tests.schema.test_lazy.type_a")] 15 | ] 16 | ListTypeC = list[ 17 | Annotated["TypeC", strawberry.lazy("tests.schema.test_lazy.type_c")] 18 | ] 19 | 20 | 21 | @strawberry.type 22 | class TypeB: 23 | @strawberry.field() 24 | def type_a( 25 | self, 26 | ) -> TypeA: 27 | from .type_a import TypeA 28 | 29 | return TypeA() 30 | 31 | @strawberry.field() 32 | def type_a_list( 33 | self, 34 | ) -> ListTypeA: # pragma: no cover 35 | from .type_a import TypeA 36 | 37 | return [TypeA()] 38 | 39 | @strawberry.field() 40 | def type_c_list( 41 | self, 42 | ) -> ListTypeC: # pragma: no cover 43 | from .type_c import TypeC 44 | 45 | return [TypeC()] 46 | -------------------------------------------------------------------------------- /tests/schema/test_lazy/type_c.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from typing import Annotated, Generic, TypeVar 3 | 4 | import strawberry 5 | 6 | T = TypeVar("T") 7 | 8 | 9 | @strawberry.type 10 | class TypeC: 11 | name: str 12 | 13 | 14 | @strawberry.type 15 | class Edge(Generic[T]): 16 | @strawberry.field 17 | def node(self) -> T: # type: ignore 18 | ... 19 | 20 | 21 | @strawberry.type 22 | class Query: 23 | type_a: Edge[TypeC] 24 | type_b: Edge[Annotated["TypeC", strawberry.lazy("tests.schema.test_lazy.type_c")]] 25 | 26 | 27 | if __name__ == "__main__": 28 | schema = strawberry.Schema(query=Query) 29 | sys.stdout.write(f"{schema.as_str()}\n") 30 | -------------------------------------------------------------------------------- /tests/schema/test_lazy/type_d.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from typing import Annotated, Generic, TypeVar 3 | 4 | import strawberry 5 | 6 | T = TypeVar("T") 7 | 8 | 9 | class Mixin(Generic[T]): 10 | node: T 11 | 12 | 13 | @strawberry.type 14 | class TypeD(Mixin[int]): 15 | name: str 16 | 17 | 18 | @strawberry.type 19 | class Query: 20 | type_d_1: TypeD 21 | type_d: Annotated["TypeD", strawberry.lazy("tests.schema.test_lazy.type_d")] 22 | 23 | 24 | if __name__ == "__main__": 25 | schema = strawberry.Schema(query=Query) 26 | sys.stdout.write(f"{schema.as_str()}\n") 27 | -------------------------------------------------------------------------------- /tests/schema/test_lazy_types/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/schema/test_lazy_types/__init__.py -------------------------------------------------------------------------------- /tests/schema/test_lazy_types/test_cyclic.py: -------------------------------------------------------------------------------- 1 | import textwrap 2 | 3 | import strawberry 4 | from strawberry.printer import print_schema 5 | 6 | 7 | def test_cyclic_import(): 8 | from .type_a import TypeA 9 | from .type_b import TypeB 10 | 11 | @strawberry.type 12 | class Query: 13 | a: TypeA 14 | b: TypeB 15 | 16 | expected = """ 17 | type Query { 18 | a: TypeA! 19 | b: TypeB! 20 | } 21 | 22 | type TypeA { 23 | listOfB: [TypeB!] 24 | typeB: TypeB! 25 | } 26 | 27 | type TypeB { 28 | typeA: TypeA! 29 | } 30 | """ 31 | 32 | schema = strawberry.Schema(Query) 33 | 34 | assert print_schema(schema) == textwrap.dedent(expected).strip() 35 | -------------------------------------------------------------------------------- /tests/schema/test_lazy_types/test_lazy_enums.py: -------------------------------------------------------------------------------- 1 | import enum 2 | import textwrap 3 | from typing import TYPE_CHECKING 4 | 5 | import pytest 6 | 7 | import strawberry 8 | from strawberry.printer import print_schema 9 | 10 | if TYPE_CHECKING: 11 | import tests 12 | 13 | 14 | @strawberry.enum 15 | class LazyEnum(enum.Enum): 16 | BREAD = "BREAD" 17 | 18 | 19 | def test_lazy_enum(): 20 | with pytest.deprecated_call(): 21 | 22 | @strawberry.type 23 | class Query: 24 | a: strawberry.LazyType[ 25 | "LazyEnum", "tests.schema.test_lazy_types.test_lazy_enums" 26 | ] 27 | 28 | expected = """ 29 | enum LazyEnum { 30 | BREAD 31 | } 32 | 33 | type Query { 34 | a: LazyEnum! 35 | } 36 | """ 37 | 38 | schema = strawberry.Schema(Query) 39 | 40 | assert print_schema(schema) == textwrap.dedent(expected).strip() 41 | -------------------------------------------------------------------------------- /tests/schema/test_lazy_types/type_a.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING, Optional 2 | 3 | import strawberry 4 | 5 | if TYPE_CHECKING: 6 | import tests.schema.test_lazy_types 7 | 8 | from .type_b import TypeB 9 | 10 | 11 | @strawberry.type 12 | class TypeA: 13 | list_of_b: Optional[ 14 | list[strawberry.LazyType["TypeB", "tests.schema.test_lazy_types.type_b"]] 15 | ] = None 16 | 17 | @strawberry.field 18 | def type_b(self) -> strawberry.LazyType["TypeB", ".type_b"]: # noqa: F722 19 | from .type_b import TypeB 20 | 21 | return TypeB() 22 | -------------------------------------------------------------------------------- /tests/schema/test_lazy_types/type_b.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING 2 | 3 | import strawberry 4 | 5 | if TYPE_CHECKING: 6 | import tests 7 | 8 | from .type_a import TypeA 9 | 10 | 11 | @strawberry.type 12 | class TypeB: 13 | @strawberry.field() 14 | def type_a( 15 | self, 16 | ) -> strawberry.LazyType["TypeA", "tests.schema.test_lazy_types.type_a"]: 17 | from .type_a import TypeA 18 | 19 | return TypeA() 20 | -------------------------------------------------------------------------------- /tests/schema/test_unresolved_fields.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import strawberry 4 | from strawberry.exceptions.unresolved_field_type import UnresolvedFieldTypeError 5 | 6 | 7 | @pytest.mark.raises_strawberry_exception( 8 | UnresolvedFieldTypeError, 9 | match=( 10 | "Could not resolve the type of 'user'. Check that " 11 | "the class is accessible from the global module scope." 12 | ), 13 | ) 14 | def test_unresolved_field_fails(): 15 | @strawberry.type 16 | class Query: 17 | user: "User" # type: ignore # noqa: F821 18 | 19 | strawberry.Schema(query=Query) 20 | 21 | 22 | @pytest.mark.raises_strawberry_exception( 23 | UnresolvedFieldTypeError, 24 | match=( 25 | "Could not resolve the type of 'user'. Check that " 26 | "the class is accessible from the global module scope." 27 | ), 28 | ) 29 | def test_unresolved_field_with_resolver_fails(): 30 | @strawberry.type 31 | class Query: 32 | @strawberry.field 33 | def user(self) -> "User": # type: ignore # noqa: F821 34 | ... 35 | 36 | strawberry.Schema(query=Query) 37 | -------------------------------------------------------------------------------- /tests/schema/types/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/schema/types/__init__.py -------------------------------------------------------------------------------- /tests/schema_codegen/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/schema_codegen/__init__.py -------------------------------------------------------------------------------- /tests/schema_codegen/test_order.py: -------------------------------------------------------------------------------- 1 | import textwrap 2 | 3 | from strawberry.schema_codegen import codegen 4 | 5 | 6 | def test_generates_used_interface_before(): 7 | schema = """ 8 | type Human implements Being { 9 | id: ID! 10 | name: String! 11 | friends: [Human] 12 | } 13 | 14 | type Cat implements Being { 15 | id: ID! 16 | name: String! 17 | livesLeft: Int 18 | } 19 | 20 | interface Being { 21 | id: ID! 22 | name: String! 23 | } 24 | """ 25 | 26 | expected = textwrap.dedent( 27 | """ 28 | import strawberry 29 | 30 | @strawberry.interface 31 | class Being: 32 | id: strawberry.ID 33 | name: str 34 | 35 | @strawberry.type 36 | class Human(Being): 37 | id: strawberry.ID 38 | name: str 39 | friends: list[Human | None] | None 40 | 41 | @strawberry.type 42 | class Cat(Being): 43 | id: strawberry.ID 44 | name: str 45 | lives_left: int | None 46 | """ 47 | ).strip() 48 | 49 | assert codegen(schema).strip() == expected 50 | -------------------------------------------------------------------------------- /tests/schema_codegen/test_union.py: -------------------------------------------------------------------------------- 1 | import textwrap 2 | 3 | from strawberry.schema_codegen import codegen 4 | 5 | 6 | def test_union(): 7 | schema = """ 8 | union User = Admin | Client 9 | 10 | type Admin { 11 | name: String! 12 | } 13 | 14 | type Client { 15 | name: String! 16 | } 17 | """ 18 | 19 | expected = textwrap.dedent( 20 | """ 21 | import strawberry 22 | from typing import Annotated 23 | 24 | User = Annotated[Admin | Client, strawberry.union(name="User")] 25 | 26 | @strawberry.type 27 | class Admin: 28 | name: str 29 | 30 | @strawberry.type 31 | class Client: 32 | name: str 33 | """ 34 | ).strip() 35 | 36 | assert codegen(schema).strip() == expected 37 | -------------------------------------------------------------------------------- /tests/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/test/__init__.py -------------------------------------------------------------------------------- /tests/test_deprecations.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import strawberry 4 | from strawberry.utils.deprecations import DEPRECATION_MESSAGES 5 | 6 | 7 | @strawberry.type 8 | class A: 9 | a: int 10 | 11 | 12 | def test_type_definition_is_aliased(): 13 | with pytest.warns( 14 | match="_type_definition is deprecated, use __strawberry_definition__ instead" 15 | ): 16 | assert A.__strawberry_definition__ is A._type_definition 17 | 18 | 19 | def test_get_warns(): 20 | with pytest.warns(match=DEPRECATION_MESSAGES._TYPE_DEFINITION): 21 | assert A._type_definition.fields[0] 22 | 23 | 24 | def test_can_import_type_definition(): 25 | from strawberry.types.base import TypeDefinition 26 | 27 | assert TypeDefinition 28 | -------------------------------------------------------------------------------- /tests/test_info.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | import pytest 4 | 5 | import strawberry 6 | 7 | 8 | def test_can_use_info_with_two_arguments(): 9 | CustomInfo = strawberry.Info[int, str] 10 | 11 | assert CustomInfo.__args__ == (int, str) 12 | 13 | 14 | def test_can_use_info_with_one_argument(): 15 | CustomInfo = strawberry.Info[int] 16 | 17 | assert CustomInfo.__args__ == (int, Any) 18 | 19 | 20 | def test_cannot_use_info_with_more_than_two_arguments(): 21 | with pytest.raises( 22 | TypeError, 23 | match="Too many (arguments|parameters) for ; actual 3, expected 2", 24 | ): 25 | strawberry.Info[int, str, int] # type: ignore 26 | -------------------------------------------------------------------------------- /tests/test_inspect.py: -------------------------------------------------------------------------------- 1 | from strawberry.utils.inspect import in_async_context 2 | 3 | 4 | def test_in_async_context_sync(): 5 | assert not in_async_context() 6 | 7 | 8 | async def test_in_async_context_async(): 9 | assert in_async_context() 10 | 11 | 12 | async def test_in_async_context_async_with_inner_sync_function(): 13 | def inner_sync_function(): 14 | assert in_async_context() 15 | 16 | inner_sync_function() 17 | -------------------------------------------------------------------------------- /tests/test_printer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/test_printer/__init__.py -------------------------------------------------------------------------------- /tests/test_printer/test_one_of.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import textwrap 4 | 5 | import strawberry 6 | from strawberry.schema_directives import OneOf 7 | 8 | 9 | @strawberry.input(directives=[OneOf()]) 10 | class ExampleInputTagged: 11 | a: str | None 12 | b: int | None 13 | 14 | 15 | @strawberry.type 16 | class ExampleResult: 17 | a: str | None 18 | b: int | None 19 | 20 | 21 | @strawberry.type 22 | class Query: 23 | @strawberry.field 24 | def test(self, input: ExampleInputTagged) -> ExampleResult: # pragma: no cover 25 | return input # type: ignore 26 | 27 | 28 | schema = strawberry.Schema(query=Query) 29 | 30 | 31 | def test_prints_one_of_directive(): 32 | expected_type = """ 33 | directive @oneOf on INPUT_OBJECT 34 | 35 | input ExampleInputTagged @oneOf { 36 | a: String 37 | b: Int 38 | } 39 | 40 | type ExampleResult { 41 | a: String 42 | b: Int 43 | } 44 | 45 | type Query { 46 | test(input: ExampleInputTagged!): ExampleResult! 47 | } 48 | """ 49 | 50 | assert str(schema) == textwrap.dedent(expected_type).strip() 51 | -------------------------------------------------------------------------------- /tests/test_repr.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | import strawberry 4 | 5 | 6 | def test_repr_type(): 7 | @strawberry.type 8 | class MyType: 9 | s: str 10 | i: int 11 | b: bool 12 | f: float 13 | id: strawberry.ID 14 | 15 | assert ( 16 | repr(MyType(s="a", i=1, b=True, f=3.2, id="123")) 17 | == "test_repr_type..MyType(s='a', i=1, b=True, f=3.2, id='123')" 18 | ) 19 | 20 | 21 | def test_repr_enum(): 22 | @strawberry.enum() 23 | class Test(Enum): 24 | A = 1 25 | B = 2 26 | C = 3 27 | 28 | assert repr(Test(1)) == "" 29 | -------------------------------------------------------------------------------- /tests/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/tools/__init__.py -------------------------------------------------------------------------------- /tests/typecheckers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/typecheckers/__init__.py -------------------------------------------------------------------------------- /tests/typecheckers/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/typecheckers/utils/__init__.py -------------------------------------------------------------------------------- /tests/typecheckers/utils/marks.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | import sys 3 | 4 | import pytest 5 | 6 | 7 | def pyright_exist() -> bool: 8 | return shutil.which("pyright") is not None 9 | 10 | 11 | def mypy_exists() -> bool: 12 | return shutil.which("mypy") is not None 13 | 14 | 15 | skip_on_windows = pytest.mark.skipif( 16 | sys.platform == "win32", 17 | reason="Do not run pyright on windows due to path issues", 18 | ) 19 | 20 | requires_pyright = pytest.mark.skipif( 21 | not pyright_exist(), 22 | reason="These tests require pyright", 23 | ) 24 | 25 | requires_mypy = pytest.mark.skipif( 26 | not mypy_exists(), 27 | reason="These tests require mypy", 28 | ) 29 | -------------------------------------------------------------------------------- /tests/typecheckers/utils/result.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Literal 3 | 4 | ResultType = Literal[ 5 | "error", 6 | "information", 7 | "note", 8 | ] 9 | 10 | 11 | @dataclass 12 | class Result: 13 | type: ResultType 14 | message: str 15 | line: int 16 | column: int 17 | -------------------------------------------------------------------------------- /tests/typecheckers/utils/typecheck.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import concurrent.futures 4 | from dataclasses import dataclass 5 | 6 | from .mypy import run_mypy 7 | from .pyright import run_pyright 8 | from .result import Result 9 | 10 | 11 | @dataclass 12 | class TypecheckResult: 13 | pyright: list[Result] 14 | mypy: list[Result] 15 | 16 | 17 | def typecheck(code: str, strict: bool = True) -> TypecheckResult: 18 | with concurrent.futures.ThreadPoolExecutor() as executor: 19 | pyright_future = executor.submit(run_pyright, code, strict=strict) 20 | mypy_future = executor.submit(run_mypy, code, strict=strict) 21 | 22 | pyright_results = pyright_future.result() 23 | mypy_results = mypy_future.result() 24 | 25 | return TypecheckResult(pyright=pyright_results, mypy=mypy_results) 26 | -------------------------------------------------------------------------------- /tests/types/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/types/__init__.py -------------------------------------------------------------------------------- /tests/types/cross_module_resolvers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/types/cross_module_resolvers/__init__.py -------------------------------------------------------------------------------- /tests/types/cross_module_resolvers/a_mod.py: -------------------------------------------------------------------------------- 1 | import strawberry 2 | 3 | 4 | def a_resolver() -> list["AObject"]: 5 | return [] 6 | 7 | 8 | @strawberry.type 9 | class ABase: 10 | a_name: str 11 | 12 | 13 | @strawberry.type 14 | class AObject(ABase): 15 | a_age: int 16 | 17 | @strawberry.field 18 | def a_is_of_full_age(self) -> bool: 19 | return self.a_age >= 18 20 | -------------------------------------------------------------------------------- /tests/types/cross_module_resolvers/b_mod.py: -------------------------------------------------------------------------------- 1 | import strawberry 2 | 3 | 4 | def b_resolver() -> list["BObject"]: 5 | return [] 6 | 7 | 8 | @strawberry.type 9 | class BBase: 10 | b_name: str = strawberry.field() 11 | 12 | 13 | @strawberry.type 14 | class BObject(BBase): 15 | b_age: int = strawberry.field() 16 | 17 | @strawberry.field 18 | def b_is_of_full_age(self) -> bool: 19 | return self.b_age >= 18 20 | -------------------------------------------------------------------------------- /tests/types/cross_module_resolvers/x_mod.py: -------------------------------------------------------------------------------- 1 | def typeless_resolver() -> list: # pragma: no cover 2 | return [] 3 | -------------------------------------------------------------------------------- /tests/types/resolving/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/types/resolving/__init__.py -------------------------------------------------------------------------------- /tests/types/resolving/test_enums.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | import strawberry 4 | from strawberry.annotation import StrawberryAnnotation 5 | 6 | 7 | def test_basic(): 8 | @strawberry.enum 9 | class NumaNuma(Enum): 10 | MA = "ma" 11 | I = "i" # noqa: E741 12 | A = "a" 13 | HI = "hi" 14 | 15 | annotation = StrawberryAnnotation(NumaNuma) 16 | resolved = annotation.resolve() 17 | 18 | # TODO: Remove reference to .enum_definition with StrawberryEnum 19 | assert resolved is NumaNuma._enum_definition 20 | -------------------------------------------------------------------------------- /tests/types/resolving/test_forward_references.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import strawberry 4 | from strawberry.annotation import StrawberryAnnotation 5 | 6 | 7 | def test_forward_reference(): 8 | global ForwardClass 9 | 10 | annotation = StrawberryAnnotation("ForwardClass", namespace=globals()) 11 | 12 | @strawberry.type 13 | class ForwardClass: 14 | backward: bool 15 | 16 | resolved = annotation.resolve() 17 | 18 | assert resolved is ForwardClass 19 | 20 | del ForwardClass 21 | 22 | 23 | @pytest.mark.xfail(reason="Combining locals() and globals() strangely makes this fail") 24 | def test_forward_reference_locals_and_globals(): 25 | global BackwardClass 26 | 27 | namespace = {**locals(), **globals()} 28 | 29 | annotation = StrawberryAnnotation("BackwardClass", namespace=namespace) 30 | 31 | @strawberry.type 32 | class BackwardClass: 33 | backward: bool 34 | 35 | resolved = annotation.resolve() 36 | 37 | assert resolved is BackwardClass 38 | 39 | del BackwardClass 40 | -------------------------------------------------------------------------------- /tests/types/resolving/test_literals.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Union 2 | 3 | from strawberry.annotation import StrawberryAnnotation 4 | 5 | 6 | def test_bool(): 7 | annotation = StrawberryAnnotation(bool) 8 | resolved = annotation.resolve() 9 | 10 | assert resolved is bool 11 | 12 | 13 | def test_float(): 14 | annotation = StrawberryAnnotation(float) 15 | resolved = annotation.resolve() 16 | 17 | assert resolved is float 18 | 19 | 20 | def test_int(): 21 | annotation = StrawberryAnnotation(int) 22 | resolved = annotation.resolve() 23 | 24 | assert resolved is int 25 | 26 | 27 | def test_str(): 28 | annotation = StrawberryAnnotation(str) 29 | resolved = annotation.resolve() 30 | 31 | assert resolved is str 32 | 33 | 34 | def test_none(): 35 | annotation = StrawberryAnnotation(None) 36 | annotation.resolve() 37 | 38 | annotation = StrawberryAnnotation(type(None)) 39 | annotation.resolve() 40 | 41 | annotation = StrawberryAnnotation(Optional[int]) 42 | annotation.resolve() 43 | 44 | annotation = StrawberryAnnotation(Union[None, int]) 45 | annotation.resolve() 46 | -------------------------------------------------------------------------------- /tests/types/test_cast.py: -------------------------------------------------------------------------------- 1 | import strawberry 2 | from strawberry.types.cast import get_strawberry_type_cast 3 | 4 | 5 | def test_cast(): 6 | @strawberry.type 7 | class SomeType: ... 8 | 9 | class OtherType: ... 10 | 11 | obj = OtherType 12 | assert get_strawberry_type_cast(obj) is None 13 | 14 | cast_obj = strawberry.cast(SomeType, obj) 15 | assert cast_obj is obj 16 | assert get_strawberry_type_cast(cast_obj) is SomeType 17 | 18 | 19 | def test_cast_none_obj(): 20 | @strawberry.type 21 | class SomeType: ... 22 | 23 | obj = None 24 | assert get_strawberry_type_cast(obj) is None 25 | 26 | cast_obj = strawberry.cast(SomeType, obj) 27 | assert cast_obj is None 28 | assert get_strawberry_type_cast(obj) is None 29 | -------------------------------------------------------------------------------- /tests/types/test_deferred_annotations.py: -------------------------------------------------------------------------------- 1 | from sys import modules 2 | from types import ModuleType 3 | 4 | import strawberry 5 | 6 | deferred_module_source = """ 7 | from __future__ import annotations 8 | 9 | import strawberry 10 | 11 | @strawberry.type 12 | class User: 13 | username: str 14 | email: str 15 | 16 | @strawberry.interface 17 | class UserContent: 18 | created_by: User 19 | """ 20 | 21 | 22 | def test_deferred_other_module(): 23 | mod = ModuleType("tests.deferred_module") 24 | modules[mod.__name__] = mod 25 | 26 | try: 27 | exec(deferred_module_source, mod.__dict__) # noqa: S102 28 | 29 | @strawberry.type 30 | class Post(mod.UserContent): 31 | title: str 32 | body: str 33 | 34 | definition = Post.__strawberry_definition__ 35 | assert definition.fields[0].type == mod.User 36 | finally: 37 | del modules[mod.__name__] 38 | -------------------------------------------------------------------------------- /tests/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/utils/__init__.py -------------------------------------------------------------------------------- /tests/utils/test_get_first_operation.py: -------------------------------------------------------------------------------- 1 | from graphql import OperationType, parse 2 | 3 | from strawberry.utils.operation import get_first_operation 4 | 5 | 6 | def test_document_without_operation_definition_notes(): 7 | document = parse( 8 | """ 9 | fragment Test on Query { 10 | hello 11 | } 12 | """ 13 | ) 14 | assert get_first_operation(document) is None 15 | 16 | 17 | def test_single_operation_definition_note(): 18 | document = parse( 19 | """ 20 | query Operation1 { 21 | hello 22 | } 23 | """ 24 | ) 25 | assert get_first_operation(document) is not None 26 | assert get_first_operation(document).operation == OperationType.QUERY 27 | 28 | 29 | def test_multiple_operation_definition_notes(): 30 | document = parse( 31 | """ 32 | mutation Operation1 { 33 | hello 34 | } 35 | query Operation2 { 36 | hello 37 | } 38 | """ 39 | ) 40 | assert get_first_operation(document) is not None 41 | assert get_first_operation(document).operation == OperationType.MUTATION 42 | -------------------------------------------------------------------------------- /tests/utils/test_logging.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from graphql.error import GraphQLError 4 | 5 | from strawberry.utils.logging import StrawberryLogger 6 | 7 | 8 | def test_strawberry_logger_error(caplog): 9 | caplog.set_level(logging.ERROR, logger="strawberry.execution") 10 | 11 | exc = GraphQLError("test exception") 12 | StrawberryLogger.error(exc) 13 | 14 | assert caplog.record_tuples == [ 15 | ("strawberry.execution", logging.ERROR, "test exception") 16 | ] 17 | -------------------------------------------------------------------------------- /tests/utils/test_pretty_print.py: -------------------------------------------------------------------------------- 1 | from decimal import Decimal 2 | 3 | from strawberry.utils.debug import pretty_print_graphql_operation 4 | 5 | 6 | def test_pretty_print(mocker): 7 | mock = mocker.patch("builtins.print") 8 | 9 | pretty_print_graphql_operation("Example", "{ query }", variables={}) 10 | 11 | mock.assert_called_with("{ \x1b[38;5;125mquery\x1b[39m }\n") 12 | 13 | 14 | def test_pretty_print_variables(mocker): 15 | mock = mocker.patch("builtins.print") 16 | 17 | pretty_print_graphql_operation("Example", "{ query }", variables={"example": 1}) 18 | 19 | mock.assert_called_with( 20 | "{\n\x1b[38;5;250m " 21 | '\x1b[39m\x1b[38;5;28;01m"example"\x1b[39;00m:\x1b[38;5;250m ' 22 | "\x1b[39m\x1b[38;5;241m1\x1b[39m\n}\n" 23 | ) 24 | 25 | 26 | def test_pretty_print_variables_object(mocker): 27 | mock = mocker.patch("builtins.print") 28 | 29 | pretty_print_graphql_operation( 30 | "Example", "{ query }", variables={"example": Decimal(1)} 31 | ) 32 | 33 | mock.assert_called_with( 34 | "{\n\x1b[38;5;250m " 35 | '\x1b[39m\x1b[38;5;28;01m"example"\x1b[39;00m:\x1b[38;5;250m ' 36 | "\x1b[39m\x1b[38;5;124m\"Decimal('1')\"\x1b[39m\n}\n" 37 | ) 38 | -------------------------------------------------------------------------------- /tests/views/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/views/__init__.py -------------------------------------------------------------------------------- /tests/websockets/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strawberry-graphql/strawberry/552d61adc5fe6b8098e8499ac78761bf4936fec5/tests/websockets/__init__.py --------------------------------------------------------------------------------