├── .dialyzer_ignore.exs ├── .formatter.exs ├── .github ├── issue_template.md ├── pull_request_template.md └── workflows │ ├── check_dependents.yml │ └── elixir.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── LICENSE.md ├── README.md ├── config ├── config.exs ├── dev.exs └── test.exs ├── guides ├── adapters.md ├── assets │ └── tutorial │ │ ├── graphiql.png │ │ ├── graphiql_blank.png │ │ ├── graphiql_create_user.png │ │ ├── graphiql_new_post_sub.png │ │ └── graphiql_user_posts.png ├── batching.md ├── client │ ├── apollo.md │ ├── javascript.md │ └── relay.md ├── complexity-analysis.md ├── context-and-authentication.md ├── custom-scalars.md ├── dataloader.md ├── deprecation.md ├── errors.md ├── file-uploads.md ├── importing-fields.md ├── importing-types.md ├── introduction │ ├── community.md │ ├── installation.md │ ├── learning.md │ └── overview.md ├── introspection.md ├── middleware-and-plugins.md ├── plug-phoenix.md ├── schemas.md ├── subscriptions.md ├── telemetry.md ├── testing.md ├── tutorial │ ├── complex-arguments.md │ ├── conclusion.md │ ├── dataloader.md │ ├── mutations.md │ ├── our-first-query.md │ ├── query-arguments.md │ ├── start.md │ └── subscriptions.md ├── upgrading │ ├── v1.4.md │ └── v1.5.md └── variables.md ├── lib ├── absinthe.ex ├── absinthe │ ├── adapter.ex │ ├── adapter │ │ ├── language_conventions.ex │ │ ├── passthrough.ex │ │ ├── strict_language_conventions.ex │ │ └── underscore.ex │ ├── blueprint.ex │ ├── blueprint │ │ ├── directive.ex │ │ ├── document.ex │ │ ├── document │ │ │ ├── field.ex │ │ │ ├── fragment.ex │ │ │ ├── fragment │ │ │ │ ├── inline.ex │ │ │ │ ├── named.ex │ │ │ │ ├── named │ │ │ │ │ └── use.ex │ │ │ │ └── spread.ex │ │ │ ├── operation.ex │ │ │ └── variable_definition.ex │ │ ├── draft.ex │ │ ├── execution.ex │ │ ├── input.ex │ │ ├── input │ │ │ ├── argument.ex │ │ │ ├── boolean.ex │ │ │ ├── enum.ex │ │ │ ├── field.ex │ │ │ ├── float.ex │ │ │ ├── generated.ex │ │ │ ├── integer.ex │ │ │ ├── list.ex │ │ │ ├── null.ex │ │ │ ├── object.ex │ │ │ ├── raw_value.ex │ │ │ ├── string.ex │ │ │ ├── value.ex │ │ │ ├── variable.ex │ │ │ └── variable │ │ │ │ └── use.ex │ │ ├── result │ │ │ ├── leaf.ex │ │ │ ├── list.ex │ │ │ └── object.ex │ │ ├── schema.ex │ │ ├── schema │ │ │ ├── deprecation.ex │ │ │ ├── directive_definition.ex │ │ │ ├── enum_type_definition.ex │ │ │ ├── enum_value_definition.ex │ │ │ ├── field_definition.ex │ │ │ ├── input_object_type_definition.ex │ │ │ ├── input_value_definition.ex │ │ │ ├── interface_type_definition.ex │ │ │ ├── object_type_definition.ex │ │ │ ├── scalar_type_definition.ex │ │ │ ├── schema_declaration.ex │ │ │ ├── schema_definition.ex │ │ │ ├── type_extension_definition.ex │ │ │ └── union_type_definition.ex │ │ ├── source_location.ex │ │ ├── transform.ex │ │ ├── type_reference.ex │ │ └── type_reference │ │ │ ├── identifier.ex │ │ │ ├── list.ex │ │ │ ├── name.ex │ │ │ └── non_null.ex │ ├── complexity.ex │ ├── formatter.ex │ ├── introspection.ex │ ├── introspection │ │ ├── directive_location.ex │ │ └── type_kind.ex │ ├── language.ex │ ├── language │ │ ├── argument.ex │ │ ├── boolean_value.ex │ │ ├── directive.ex │ │ ├── directive_definition.ex │ │ ├── document.ex │ │ ├── enum_type_definition.ex │ │ ├── enum_value.ex │ │ ├── enum_value_definition.ex │ │ ├── field.ex │ │ ├── field_definition.ex │ │ ├── float_value.ex │ │ ├── fragment.ex │ │ ├── fragment_spread.ex │ │ ├── inline_fragment.ex │ │ ├── input_object_type_definition.ex │ │ ├── input_value_definition.ex │ │ ├── int_value.ex │ │ ├── interface_type_definition.ex │ │ ├── list_type.ex │ │ ├── list_value.ex │ │ ├── named_type.ex │ │ ├── non_null_type.ex │ │ ├── null_value.ex │ │ ├── object_field.ex │ │ ├── object_type_definition.ex │ │ ├── object_value.ex │ │ ├── operation_definition.ex │ │ ├── render.ex │ │ ├── scalar_type_definition.ex │ │ ├── schema_declaration.ex │ │ ├── schema_definition.ex │ │ ├── selection_set.ex │ │ ├── source.ex │ │ ├── string_value.ex │ │ ├── type_extension_definition.ex │ │ ├── union_type_definition.ex │ │ ├── variable.ex │ │ └── variable_definition.ex │ ├── lexer.ex │ ├── logger.ex │ ├── middleware.ex │ ├── middleware │ │ ├── async.ex │ │ ├── batch.ex │ │ ├── dataloader.ex │ │ ├── map_get.ex │ │ ├── pass_parent.ex │ │ └── telemetry.ex │ ├── phase.ex │ ├── phase │ │ ├── blueprint.ex │ │ ├── debug.ex │ │ ├── document │ │ │ ├── arguments │ │ │ │ ├── coerce_enums.ex │ │ │ │ ├── coerce_lists.ex │ │ │ │ ├── data.ex │ │ │ │ ├── flag_invalid.ex │ │ │ │ ├── normalize.ex │ │ │ │ ├── parse.ex │ │ │ │ └── variable_types_match.ex │ │ │ ├── complexity │ │ │ │ ├── analysis.ex │ │ │ │ └── result.ex │ │ │ ├── context.ex │ │ │ ├── current_operation.ex │ │ │ ├── directives.ex │ │ │ ├── execution │ │ │ │ └── resolution.ex │ │ │ ├── missing_literals.ex │ │ │ ├── missing_variables.ex │ │ │ ├── override_root.ex │ │ │ ├── result.ex │ │ │ ├── uses.ex │ │ │ ├── validation │ │ │ │ ├── arguments_of_correct_type.ex │ │ │ │ ├── fields_on_correct_type.ex │ │ │ │ ├── known_argument_names.ex │ │ │ │ ├── known_directives.ex │ │ │ │ ├── known_fragment_names.ex │ │ │ │ ├── lone_anonymous_operation.ex │ │ │ │ ├── no_fragment_cycles.ex │ │ │ │ ├── no_undefined_variables.ex │ │ │ │ ├── no_unused_fragments.ex │ │ │ │ ├── no_unused_variables.ex │ │ │ │ ├── only_one_subscription.ex │ │ │ │ ├── provided_an_operation.ex │ │ │ │ ├── provided_non_null_arguments.ex │ │ │ │ ├── provided_non_null_variables.ex │ │ │ │ ├── repeatable_directives.ex │ │ │ │ ├── result.ex │ │ │ │ ├── scalar_leafs.ex │ │ │ │ ├── selected_current_operation.ex │ │ │ │ ├── unique_argument_names.ex │ │ │ │ ├── unique_fragment_names.ex │ │ │ │ ├── unique_input_field_names.ex │ │ │ │ ├── unique_operation_names.ex │ │ │ │ ├── unique_variable_names.ex │ │ │ │ ├── utils │ │ │ │ │ └── message_suggestions.ex │ │ │ │ └── variables_are_input_types.ex │ │ │ └── variables.ex │ │ ├── error.ex │ │ ├── init.ex │ │ ├── parse.ex │ │ ├── schema.ex │ │ ├── schema │ │ │ ├── apply_declaration.ex │ │ │ ├── apply_type_extensions.ex │ │ │ ├── arguments │ │ │ │ ├── data.ex │ │ │ │ ├── normalize.ex │ │ │ │ └── parse.ex │ │ │ ├── build.ex │ │ │ ├── compile.ex │ │ │ ├── directive_imports.ex │ │ │ ├── directives.ex │ │ │ ├── field_imports.ex │ │ │ ├── hydrate.ex │ │ │ ├── import_prototype_directives.ex │ │ │ ├── inline_functions.ex │ │ │ ├── introspection.ex │ │ │ ├── mark_referenced.ex │ │ │ ├── populate_persistent_term.ex │ │ │ ├── reformat_descriptions.ex │ │ │ ├── register_triggers.ex │ │ │ ├── spec_compliant_int.ex │ │ │ ├── type_extension_imports.ex │ │ │ ├── type_imports.ex │ │ │ └── validation │ │ │ │ ├── default_enum_value_present.ex │ │ │ │ ├── directives_must_be_valid.ex │ │ │ │ ├── input_output_types_correctly_placed.ex │ │ │ │ ├── interfaces_must_resolve_types.ex │ │ │ │ ├── known_directives.ex │ │ │ │ ├── names_must_be_valid.ex │ │ │ │ ├── no_circular_field_imports.ex │ │ │ │ ├── no_interface_cycles.ex │ │ │ │ ├── object_interfaces_must_be_valid.ex │ │ │ │ ├── object_must_define_fields.ex │ │ │ │ ├── object_must_implement_interfaces.ex │ │ │ │ ├── query_type_must_be_object.ex │ │ │ │ ├── result.ex │ │ │ │ ├── type_names_are_reserved.ex │ │ │ │ ├── type_names_are_unique.ex │ │ │ │ ├── type_references_exist.ex │ │ │ │ └── unique_field_names.ex │ │ ├── subscription │ │ │ ├── result.ex │ │ │ └── subscribe_self.ex │ │ ├── telemetry.ex │ │ └── validation │ │ │ └── known_type_names.ex │ ├── pipeline.ex │ ├── pipeline │ │ └── batch_resolver.ex │ ├── plugin.ex │ ├── resolution.ex │ ├── resolution │ │ ├── helpers.ex │ │ └── projector.ex │ ├── schema.ex │ ├── schema │ │ ├── compiled.ex │ │ ├── error.ex │ │ ├── hydrator.ex │ │ ├── manager.ex │ │ ├── notation.ex │ │ ├── notation │ │ │ ├── error.ex │ │ │ ├── sdl.ex │ │ │ └── sdl_render.ex │ │ ├── persistent_term.ex │ │ ├── prototype.ex │ │ ├── prototype │ │ │ └── notation.ex │ │ └── provider.ex │ ├── subscription.ex │ ├── subscription │ │ ├── local.ex │ │ ├── pipeline_serializer.ex │ │ ├── proxy.ex │ │ ├── proxy_supervisor.ex │ │ ├── pubsub.ex │ │ └── supervisor.ex │ ├── test.ex │ ├── type.ex │ ├── type │ │ ├── argument.ex │ │ ├── built_ins │ │ │ ├── deprecated_directive_fields.ex │ │ │ ├── directives.ex │ │ │ ├── introspection.ex │ │ │ ├── scalars.ex │ │ │ └── spec_compliant_int.ex │ │ ├── custom.ex │ │ ├── custom │ │ │ └── decimal.ex │ │ ├── deprecation.ex │ │ ├── directive.ex │ │ ├── enum.ex │ │ ├── enum │ │ │ └── value.ex │ │ ├── field.ex │ │ ├── input_object.ex │ │ ├── interface.ex │ │ ├── list.ex │ │ ├── non_null.ex │ │ ├── object.ex │ │ ├── reference.ex │ │ ├── scalar.ex │ │ └── union.ex │ ├── utils.ex │ └── utils │ │ ├── render.ex │ │ └── suggestion.ex └── mix │ └── tasks │ ├── absinthe.schema.json.ex │ └── absinthe.schema.sdl.ex ├── logo.png ├── mix.exs ├── mix.lock ├── priv └── graphql │ └── introspection.graphql ├── src └── absinthe_parser.yrl └── test ├── absinthe ├── adapters │ ├── language_conventions_test.exs │ └── strict_language_conventions_test.exs ├── blueprint │ └── type_reference_test.exs ├── custom_types_test.exs ├── execution │ ├── arguments │ │ ├── boolean_test.exs │ │ ├── enum_test.exs │ │ ├── input_object_test.exs │ │ ├── list_test.exs │ │ └── scalar_test.exs │ ├── arguments_test.exs │ ├── default_resolver_test.exs │ ├── fragment_spread_test.exs │ ├── inline_fragments_test.exs │ ├── list_test.exs │ └── subscription_test.exs ├── extensions_test.exs ├── formatter_test.exs ├── fragment_merge_test.exs ├── integration │ ├── execution │ │ ├── aliases │ │ │ ├── alias_test.exs │ │ │ ├── all_caps_alias_test.exs │ │ │ ├── different_selection_sets_test.exs │ │ │ ├── leading_underscore_test.exs │ │ │ ├── weird_test.exs │ │ │ └── with_errors_test.exs │ │ ├── context_test.exs │ │ ├── custom_types │ │ │ ├── basic_test.exs │ │ │ └── datetime │ │ │ │ └── input_object_test.exs │ │ ├── escape_sequence_test.exs │ │ ├── fragments │ │ │ ├── basic_root_type_test.exs │ │ │ ├── basic_test.exs │ │ │ └── introspection_test.exs │ │ ├── input_object_test.exs │ │ ├── input_types │ │ │ ├── enum │ │ │ │ ├── default_value_test.exs │ │ │ │ └── literal_test.exs │ │ │ ├── id │ │ │ │ └── literal_test.exs │ │ │ └── null │ │ │ │ ├── literal_to_element_of_type_list_of_T_test.exs │ │ │ │ ├── literal_to_element_of_type_non_null_list_of_T_test.exs │ │ │ │ ├── literal_to_type_T_overrides_default_value_test.exs │ │ │ │ ├── literal_to_type_list_of_T_test.exs │ │ │ │ ├── literal_to_type_non_null_T_test.exs │ │ │ │ ├── literal_to_type_non_null_list_of_T_element_test.exs │ │ │ │ ├── literal_to_type_non_null_list_of_T_test.exs │ │ │ │ ├── literal_to_type_non_null_list_of_non_null_T_element_test.exs │ │ │ │ ├── literal_to_type_non_null_list_of_non_null_T_test.exs │ │ │ │ ├── variable_to_type_T_overrides_default_value_test.exs │ │ │ │ ├── variable_to_type_T_test.exs │ │ │ │ ├── variable_to_type_list_of_T_element_test.exs │ │ │ │ ├── variable_to_type_list_of_T_test.exs │ │ │ │ ├── variable_to_type_non_null_T_test.exs │ │ │ │ ├── variable_to_type_non_null_list_of_T_element_test.exs │ │ │ │ ├── variable_to_type_non_null_list_of_T_test.exs │ │ │ │ ├── variable_to_type_non_null_list_of_non_null_T_element_test.exs │ │ │ │ ├── variable_to_type_non_null_list_of_non_null_T_test.exs │ │ │ │ ├── variable_to_variable_type_non_null_T_test.exs │ │ │ │ └── variable_to_variable_with_default_value_test.exs │ │ ├── introspection │ │ │ ├── default_value_enum_test.exs │ │ │ ├── directives_test.exs │ │ │ ├── full_test.exs │ │ │ ├── interface_typename_alias_test.exs │ │ │ ├── interface_typename_test.exs │ │ │ ├── mutation_type_test.exs │ │ │ ├── object_typename_alias_test.exs │ │ │ ├── object_typename_test.exs │ │ │ ├── object_with_list_test.exs │ │ │ ├── query_type_test.exs │ │ │ ├── schema_types_test.exs │ │ │ ├── subscription_type_test.exs │ │ │ ├── type_interface_test.exs │ │ │ ├── type_kind_test.exs │ │ │ ├── union_typename_test.exs │ │ │ └── union_wrapped_typename_test.exs │ │ ├── nested_objects_test.exs │ │ ├── operation_by_name_test.exs │ │ ├── resolution │ │ │ ├── errors_include_path_indices_test.exs │ │ │ ├── exceptions │ │ │ │ ├── bad_match_test.exs │ │ │ │ ├── missing_error_message_test.exs │ │ │ │ └── missing_error_message_when_returning_multiple_test.exs │ │ │ ├── extra_error_fields_test.exs │ │ │ ├── multiple_errors_test.exs │ │ │ └── multiple_errors_with_extra_fields_test.exs │ │ ├── root_value_test.exs │ │ ├── serialization_test.exs │ │ ├── simple_query_returning_list_test.exs │ │ ├── simple_query_test.exs │ │ ├── telemetry_test.exs │ │ ├── token_limit_enforcement_test.exs │ │ └── variables │ │ │ ├── basic_test.exs │ │ │ └── default_value_test.exs │ ├── parsing │ │ └── basic_error_test.exs │ └── validation │ │ ├── cycles_test.exs │ │ ├── error_result_when_bad_list_argument_test.exs │ │ ├── extra_arguments_test.exs │ │ ├── introspection_fields_ignored_in_input_objects_test.exs │ │ ├── invalid_argument_test.exs │ │ ├── invalid_nested_type_test.exs │ │ ├── missing_operation_test.exs │ │ ├── missing_selection_set_test.exs │ │ ├── object_spreads_in_object_scope_test.exs │ │ ├── required_arguments_test.exs │ │ ├── unknown_arg_for_list_member_field_test.exs │ │ ├── unknown_field_test.exs │ │ └── variables │ │ └── unused │ │ ├── with_operation_name_test.exs │ │ └── without_operation_name_test.exs ├── introspection_test.exs ├── language │ ├── directive_definition_test.exs │ ├── document_test.exs │ ├── enum_type_definition_test.exs │ ├── field_definition_test.exs │ ├── field_test.exs │ ├── fragment_test.exs │ ├── inline_fragment_test.exs │ ├── input_object_test.exs │ ├── input_object_type_definition_test.exs │ ├── interface_type_definition_test.exs │ ├── object_type_definition_test.exs │ ├── operation_definition_test.exs │ ├── render_test.exs │ ├── scalar_type_definition_test.exs │ ├── union_type_definition_test.exs │ ├── variable_definition_test.exs │ └── variable_test.exs ├── lexer_test.exs ├── logger_test.exs ├── middleware │ ├── async_test.exs │ ├── batch_test.exs │ └── dataloader_test.exs ├── phase │ ├── document │ │ ├── arguments │ │ │ ├── coerce_enums_test.exs │ │ │ ├── coerce_lists_test.exs │ │ │ └── normalize_test.exs │ │ ├── complexity_test.exs │ │ ├── context_test.exs │ │ ├── directives_test.exs │ │ ├── schema_test.exs │ │ ├── validation │ │ │ ├── arguments_of_correct_type_test.exs │ │ │ ├── fields_on_correct_type_test.exs │ │ │ ├── known_argument_names_test.exs │ │ │ ├── known_directives_test.exs │ │ │ ├── known_fragment_names_test.exs │ │ │ ├── lone_anonymous_operation_test.exs │ │ │ ├── no_fragment_cycles_test.exs │ │ │ ├── no_undefined_variables_test.exs │ │ │ ├── no_unused_fragments_test.exs │ │ │ ├── no_unused_variables_test.exs │ │ │ ├── provided_an_operation_test.exs │ │ │ ├── provided_non_null_arguments_test.exs │ │ │ ├── repeatable_directives_test.exs │ │ │ ├── scalar_leafs_test.exs │ │ │ ├── selected_current_operation_test.exs │ │ │ ├── unique_argument_names_test.exs │ │ │ ├── unique_input_field_names_test.exs │ │ │ ├── unique_operation_names_test.exs │ │ │ ├── unique_variable_names_test.exs │ │ │ ├── variables_are_input_types_test.exs │ │ │ └── variables_of_correct_type_test.exs │ │ └── variables_test.exs │ ├── execution │ │ └── non_null_test.exs │ ├── parse │ │ ├── block_strings_test.exs │ │ ├── const_usage_test.exs │ │ ├── descriptions_test.exs │ │ └── language_test.exs │ ├── parse_test.exs │ ├── schema │ │ ├── inline_functions_test.exs │ │ ├── reformat_description_test.exs │ │ ├── spec_compliant_int_test.exs │ │ └── type_references_exist_test.exs │ ├── schema_test.exs │ └── validation │ │ └── known_type_names_test.exs ├── pipeline_test.exs ├── resolution │ ├── middleware_test.exs │ └── projector_test.exs ├── resolution_test.exs ├── schema │ ├── experimental_test.exs │ ├── hydrate_builtins_test.exs │ ├── hydrate_dynamic_values_test.exs │ ├── manipulation_test.exs │ ├── notation │ │ ├── experimental │ │ │ ├── argument_test.exs │ │ │ ├── field_test.exs │ │ │ ├── import_directives_test.exs │ │ │ ├── import_fields_test.exs │ │ │ ├── import_sdl_test.exs │ │ │ ├── import_type_extensions_test.exs │ │ │ ├── import_types_test.exs │ │ │ ├── macro_extensions_test.exs │ │ │ ├── object_test.exs │ │ │ ├── resolve_test.exs │ │ │ └── sdl_extensions_test.exs │ │ └── import_test.exs │ ├── notation_test.exs │ ├── rule │ │ ├── default_enum_value_present_test.exs │ │ ├── directive_must_be_valid_test.exs │ │ ├── input_output_types_correctly_placed_test.exs │ │ ├── names_must_be_valid_test.exs │ │ ├── no_interface_cycles_test.exs │ │ ├── object_interfaces_must_be_valid_test.exs │ │ ├── object_must_define_fields_test.exs │ │ ├── object_must_implement_interfaces_test.exs │ │ ├── query_type_must_be_object_test.exs │ │ ├── repeatable_directives_test.exs │ │ ├── type_names_are_reserved_test.exs │ │ ├── type_names_are_valid_test.exs │ │ └── unique_field_names_test.exs │ ├── sdl_render_test.exs │ └── type_system_directive_test.exs ├── schema_test.exs ├── strict_schema_test.exs ├── subscription │ └── pipeline_serializer_test.exs ├── type │ ├── built_ins │ │ └── scalars_test.exs │ ├── custom_test.exs │ ├── deprecation_test.exs │ ├── directive_test.exs │ ├── enum_test.exs │ ├── import_types_test.exs │ ├── input_object_test.exs │ ├── interface_test.exs │ ├── mutation_test.exs │ ├── object_test.exs │ ├── query_test.exs │ └── union_test.exs ├── type_test.exs ├── union_fragment_test.exs └── utils_test.exs ├── mix └── tasks │ ├── absinthe.schema.json_test.exs │ └── absinthe.schema.sdl_test.exs ├── support ├── case.ex ├── case │ ├── assertions │ │ ├── result.ex │ │ └── schema.ex │ └── helpers │ │ ├── run.ex │ │ └── schema_implementations.ex ├── experimental_notation_helpers.ex ├── fixture.ex ├── fixtures │ ├── arguments_schema.ex │ ├── color_schema.ex │ ├── contact_schema.ex │ ├── custom_types_schema.ex │ ├── default_value_schema.ex │ ├── directive.ex │ ├── dynamic │ │ ├── bad_directives_schema.exs │ │ ├── bad_interface_schema.exs │ │ ├── bad_names_schema.exs │ │ ├── bad_types_schema.exs │ │ ├── empty_schema.exs │ │ ├── interface_cycle_schema.exs │ │ ├── invalid_input_types.exs │ │ ├── invalid_input_types_sdl.exs │ │ ├── invalid_interface_types.exs │ │ ├── invalid_output_types.exs │ │ ├── invalid_output_types_sdl.exs │ │ ├── prefix_schema.exs │ │ ├── schema_with_duplicate_identifiers.exs │ │ ├── schema_with_duplicate_names.exs │ │ └── unknown_import_schema.exs │ ├── enums.ex │ ├── fake_definition.graphql │ ├── function_evaluation_helpers.ex │ ├── id_test_schema.ex │ ├── import_sdl_binary_fn.graphql │ ├── import_sdl_path_option.graphql │ ├── import_sdl_path_option_fn.graphql │ ├── import_types.ex │ ├── input_object.ex │ ├── language │ │ ├── kitchen-sink.graphql │ │ ├── schema-kitchen-sink.graphql │ │ └── schema-with-emojis.graphql │ ├── null_lists_schema.ex │ ├── object.ex │ ├── object_times_schema.ex │ ├── only_query_schema.ex │ ├── pets_schema.ex │ ├── query.ex │ ├── scalar.ex │ ├── strict_schema.ex │ ├── things │ │ ├── macro_schema.ex │ │ └── sdl_schema.ex │ ├── times_schema.ex │ └── union.ex ├── integration_case │ └── definition.ex ├── phase_case.ex ├── test_telemetry_helper.ex └── validation_phase_case.ex └── test_helper.exs /.dialyzer_ignore.exs: -------------------------------------------------------------------------------- 1 | [ 2 | {":0:unknown_function Function :persistent_term.get/1 does not exist."}, 3 | {":0:unknown_function Function :persistent_term.put/2 does not exist."}, 4 | {"lib/absinthe/middleware/async.ex", :unknown_function, 117}, 5 | {"lib/absinthe/middleware/batch.ex", :unknown_function, 213}, 6 | {"lib/absinthe/utils/render.ex", :improper_list_constr, 51}, 7 | {":0:unknown_function Function OpentelemetryProcessPropagator.Task.async/1 does not exist."} 8 | ] 9 | -------------------------------------------------------------------------------- /.formatter.exs: -------------------------------------------------------------------------------- 1 | locals_without_parens = [ 2 | mutation: 2, 3 | query: 2, 4 | subscription: 2, 5 | arg: 2, 6 | arg: 3, 7 | complexity: 1, 8 | config: 1, 9 | deprecate: 1, 10 | description: 1, 11 | directive: 1, 12 | directive: 2, 13 | directive: 3, 14 | enum: 2, 15 | enum: 3, 16 | expand: 1, 17 | extend: 1, 18 | extend: 2, 19 | field: 2, 20 | field: 3, 21 | field: 4, 22 | import_directives: 1, 23 | import_directives: 2, 24 | import_fields: 2, 25 | import_fields: 1, 26 | import_types: 1, 27 | import_types: 2, 28 | import_type_extensions: 1, 29 | import_type_extensions: 2, 30 | import_sdl: 1, 31 | import_sdl: 2, 32 | input_object: 3, 33 | interface: 1, 34 | interface: 3, 35 | interfaces: 1, 36 | is_type_of: 1, 37 | meta: 1, 38 | meta: 2, 39 | middleware: 2, 40 | middleware: 1, 41 | object: 3, 42 | on: 1, 43 | parse: 1, 44 | repeatable: 1, 45 | resolve: 1, 46 | resolve_type: 1, 47 | scalar: 2, 48 | scalar: 3, 49 | serialize: 1, 50 | trigger: 2, 51 | types: 1, 52 | union: 3, 53 | value: 1, 54 | value: 2, 55 | values: 1 56 | ] 57 | 58 | [ 59 | inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"], 60 | locals_without_parens: locals_without_parens, 61 | export: [ 62 | locals_without_parens: locals_without_parens 63 | ] 64 | ] 65 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bench 2 | /_build 3 | /cover 4 | /deps 5 | /doc 6 | /.elixir_ls 7 | erl_crash.dump 8 | *.ez 9 | src/*.erl 10 | .tool-versions* 11 | missing_rules.rb 12 | .DS_Store 13 | /priv/plts/*.plt 14 | /priv/plts/*.plt.hash 15 | /tmp 16 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for contributing to the project. We'd love to see your 4 | issues and pull requests. 5 | 6 | If you're creating a pull request, please consider these suggestions: 7 | 8 | Fork, then clone the repo: 9 | 10 | git clone git@github.com:your-username/absinthe.git 11 | 12 | Install the dependencies: 13 | 14 | mix deps.get 15 | 16 | Make sure the tests pass: 17 | 18 | mix test 19 | 20 | Make your change. Add tests for your change. Make the tests pass: 21 | 22 | mix test 23 | 24 | Push to your fork (preferably to a non-`main` branch) and 25 | [submit a pull request][pr]. 26 | 27 | [pr]: https://github.com/absinthe-graphql/absinthe/compare/ 28 | 29 | We'll review and answer your pull request as soon as possible. We may suggest 30 | some changes, improvements, or alternatives. Let's work through it together. 31 | 32 | Some things that will increase the chance that your pull request is accepted: 33 | 34 | * Write tests. 35 | * Include `@typedoc`s, `@spec`s, and `@doc`s 36 | * Try to match the style conventions already present (and Elixir conventions, 37 | generally). 38 | * Write a [good commit message][commit]. 39 | 40 | Thanks again for helping! 41 | 42 | [commit]: https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html 43 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * Do not use the issues tracker for help or support (try Elixir Forum, Slack, IRC, etc.) 2 | * Questions about how to contribute are fine. 3 | 4 | ### Environment 5 | 6 | * Elixir & Erlang versions (elixir --version): 7 | * ExAws version `mix deps |grep ex_aws` 8 | * HTTP client version. IE for hackney do `mix deps | grep hackney` 9 | 10 | ### Current behavior 11 | 12 | Include code samples, errors and stacktraces if appropriate. 13 | 14 | ### Expected behavior -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Bruce Williams, Ben Wilson 4 | 5 | Parser derived from GraphQL Elixir, Copyright (c) Josh Price 6 | https://github.com/graphql-elixir/graphql-elixir 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining 9 | a copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to 13 | permit persons to whom the Software is furnished to do so, subject to 14 | the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | # This file is responsible for configuring your application 2 | # and its dependencies with the aid of the Mix.Config module. 3 | import Config 4 | 5 | config :logger, level: :debug 6 | 7 | # This configuration is loaded before any dependency and is restricted 8 | # to this project. If another project depends on this project, this 9 | # file won't be loaded nor affect the parent project. For this reason, 10 | # if you want to provide default values for your application for 11 | # 3rd-party users, it should be done in your "mix.exs" file. 12 | 13 | # You can configure for your application as: 14 | # 15 | # config :absinthe, key: :value 16 | # 17 | # And access this configuration in your application as: 18 | # 19 | # Application.get_env(:absinthe, :key) 20 | # 21 | # Or configure a 3rd-party app: 22 | # 23 | # config :logger, level: :info 24 | # 25 | 26 | # It is also possible to import configuration files, relative to this 27 | # directory. For example, you can emulate configuration per environment 28 | # by uncommenting the line below and defining dev.exs, test.exs and such. 29 | # Configuration from the imported file will override the ones defined 30 | # here (which is why it is important to import them last). 31 | # 32 | import_config "#{Mix.env()}.exs" 33 | -------------------------------------------------------------------------------- /config/dev.exs: -------------------------------------------------------------------------------- 1 | import Config 2 | -------------------------------------------------------------------------------- /config/test.exs: -------------------------------------------------------------------------------- 1 | import Config 2 | 3 | config :logger, level: :info 4 | -------------------------------------------------------------------------------- /guides/assets/tutorial/graphiql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/absinthe-graphql/absinthe/a605cba55eed7a4aa35667c942e8406941365d48/guides/assets/tutorial/graphiql.png -------------------------------------------------------------------------------- /guides/assets/tutorial/graphiql_blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/absinthe-graphql/absinthe/a605cba55eed7a4aa35667c942e8406941365d48/guides/assets/tutorial/graphiql_blank.png -------------------------------------------------------------------------------- /guides/assets/tutorial/graphiql_create_user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/absinthe-graphql/absinthe/a605cba55eed7a4aa35667c942e8406941365d48/guides/assets/tutorial/graphiql_create_user.png -------------------------------------------------------------------------------- /guides/assets/tutorial/graphiql_new_post_sub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/absinthe-graphql/absinthe/a605cba55eed7a4aa35667c942e8406941365d48/guides/assets/tutorial/graphiql_new_post_sub.png -------------------------------------------------------------------------------- /guides/assets/tutorial/graphiql_user_posts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/absinthe-graphql/absinthe/a605cba55eed7a4aa35667c942e8406941365d48/guides/assets/tutorial/graphiql_user_posts.png -------------------------------------------------------------------------------- /guides/client/javascript.md: -------------------------------------------------------------------------------- 1 | # Using with JavaScript 2 | 3 | You can interact with an Absinthe GraphQL server via HTTP (thanks to 4 | `absinthe_plug`) and websockets (thanks to `absinthe_phoenix`): 5 | 6 | We also have special support for configuring and working with specific 7 | JavaScript frameworks. You can see the guides here: 8 | 9 | - [Apollo Client](apollo.md) 10 | - [Relay](relay.md) 11 | 12 | ## Over HTTP 13 | 14 | To integrate a JavaScript application via HTTP, any standard GraphQL 15 | HTTP request (GET/POST) will do. 16 | 17 | Here's an example using 18 | [isomorphic-fetch](https://www.npmjs.com/package/isomorphic-fetch): 19 | 20 | ``` javascript 21 | require('isomorphic-fetch'); 22 | 23 | fetch('http://localhost:4000/graphql', { 24 | method: 'POST', 25 | headers: { 'Content-Type': 'application/json' }, 26 | body: JSON.stringify({ query: '{ posts { title } }' }), 27 | }) 28 | .then(res => res.json()) 29 | .then(res => console.log(res.data)); 30 | ``` 31 | 32 | ## Over Websockets 33 | 34 | See the [@absinthe/socket](https://github.com/absinthe-graphql/absinthe-socket/tree/master/packages/socket) NPM package 35 | for special support for Absinthe's use of Phoenix channels for GraphQL over websockets, including support for 36 | [subscriptions](subscriptions.md). 37 | -------------------------------------------------------------------------------- /guides/deprecation.md: -------------------------------------------------------------------------------- 1 | # Schema Deprecation 2 | 3 | Use the `deprecate` option when defining any field or enum value. 4 | 5 | - Provide a binary value to give a deprecation reason 6 | - Provide `true` to just mark it as deprecated 7 | 8 | An example: 9 | 10 | ```elixir 11 | query do 12 | field :old_item, :item, deprecate: true 13 | field :another_old_item, :item, deprecate: "still too old" 14 | end 15 | ``` 16 | 17 | You can also use the `deprecate` as a macro inside a block, for instance: 18 | 19 | ```elixir 20 | field :age, :integer do 21 | deprecate 22 | arg :user_id, non_null(:id) 23 | end 24 | ``` 25 | 26 | With a reason: 27 | 28 | ```elixir 29 | field :ssn, :string do 30 | deprecate "Privacy concerns" 31 | end 32 | ``` 33 | 34 | > #### Warning {: .warning} 35 | > 36 | > Warning: Deprecated fields and enum values are not reported by default during [introspection](introspection.md). 37 | -------------------------------------------------------------------------------- /guides/introduction/community.md: -------------------------------------------------------------------------------- 1 | # Community 2 | 3 | ## Twitter 4 | 5 | Follow the project on Twitter as [`@absinthegraphql`](https://twitter.com/absinthegraphql) for news and additional resources. 6 | 7 | ## Chat 8 | 9 | You can find the maintainers and an active community of users and contributors in the `#absinthe-graphql` channel in the [Elixir](https://elixir-slackin.herokuapp.com/) Slack. 10 | 11 | ## Forum 12 | 13 | Questions and suggestions can be submitted on the [Elixir Forum](https://elixirforum.com). Please categorize/tag as `Absinthe`. 14 | -------------------------------------------------------------------------------- /guides/introduction/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | To install Absinthe, just add an entry to your `mix.exs`: 4 | 5 | ```elixir 6 | def deps do 7 | [ 8 | # ... 9 | {:absinthe, "~> 1.7"} 10 | ] 11 | end 12 | ``` 13 | 14 | (Check [Hex](https://hex.pm/packages/absinthe) to make sure you're using an up-to-date version number.) 15 | 16 | ## Overriding Dependencies 17 | 18 | Because the Absinthe project is made up of a large number of related packages to support integrations with other tools, sometimes you may want to update only part of your absinthe-related dependencies. 19 | 20 | Don't forget you can use the [:override](https://hexdocs.pm/mix/Mix.Tasks.Deps.html#module-dependency-definition-options) option for your Mix dependencies if you'd like to ensure a specific package is at a specific version number. For example, If you wanted to try a new version of Absinthe without updating something that depends on it (which is locked to an older version): 21 | 22 | ```elixir 23 | def deps do 24 | [ 25 | # ... 26 | {:absinthe, "~> 1.7", override: true} 27 | ] 28 | end 29 | ``` 30 | 31 | ## Plug, Phoenix, and GraphiQL 32 | 33 | Most people use Absinthe to support an HTTP API. 34 | 35 | You'll want to read the [Plug and Phoenix](plug-phoenix.md) for specific installation and configuration options, including how you can run the handy, included GraphiQL tool directly from your application. 36 | -------------------------------------------------------------------------------- /guides/introduction/learning.md: -------------------------------------------------------------------------------- 1 | # Learning 2 | 3 | The following are some Absinthe-specific educational resources that are available. 4 | 5 | ## Books 6 | 7 | * [Craft GraphQL APIs in Elixir with Absinthe](https://pragprog.com/titles/wwgraphql/craft-graphql-apis-in-elixir-with-absinthe/) by the creators of Absinthe. 8 | 9 | ## Online Resources 10 | 11 | * [Website](https://absinthe-graphql.org) (mostly just links elsewhere) 12 | * [Documentation](https://hexdocs.pm/absinthe) (current stable release) 13 | * [How to GraphQL (with Absinthe)](https://www.howtographql.com/graphql-elixir/0-introduction/) 14 | 15 | ## Videos 16 | 17 | * [Live APIs with GraphQL Subscriptions](https://www.youtube.com/watch?v=PEckzwggd78), ElixirConf 2017 (Bellevue) 18 | * [GraphQL in Practice](https://www.youtube.com/watch?v=d2qNlXtpWXM), ElixirConf EU 2017 (Barcelona) 19 | 20 | ## General GraphQL Information 21 | 22 | There's a ton of GraphQL resources on the web. 23 | 24 | The [official website](https://graphql.org/) and [How to GraphQL](https://www.howtographql.com) are good places to start. 25 | -------------------------------------------------------------------------------- /guides/tutorial/conclusion.md: -------------------------------------------------------------------------------- 1 | # Conclusion 2 | 3 | With this we have a basic GraphQL based API for a blog. Head on over 4 | to [the github page](https://github.com/absinthe-graphql/absinthe_tutorial) if 5 | you want the final code. 6 | 7 | We hope to expand this tutorial to include a comment system that will 8 | acquaint you with Union types and Fragments in the coming days. 9 | 10 | Head on over to the topic guides for further reading, and see 11 | the [community page](community.md) for information 12 | on how to get help, ask questions, or contribute! 13 | 14 | ## Please Help! 15 | 16 | This tutorial is a work in progress, and while it covers the basics of 17 | using Absinthe, there is plenty more that can be added and improved 18 | upon. It's important that it's kept up-to-date, too, so if you notice 19 | something that's slipped by us, please help us fix it! 20 | 21 | Please contribute your GitHub issues (and pull requests!): 22 | 23 | - The tutorial text is under `guides/tutorial` in the [absinthe](https://github.com/absinthe-graphql/absinthe) 24 | repository. It's in Markdown and easy to edit! 25 | - The tutorial code located in the [absinthe_tutorial](https://github.com/absinthe-graphql/absinthe_tutorial) repository. 26 | -------------------------------------------------------------------------------- /guides/tutorial/start.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | We'll be building a very basic GraphQL API for a blog, written in Elixir using 4 | Absinthe. 5 | 6 | ## Background 7 | 8 | Before you start, it's a good idea to have some background into GraphQL in general. Here are a few resources that might be helpful: 9 | 10 | - The official [GraphQL](https://graphql.org/) website 11 | - [How to GraphQL](https://www.howtographql.com/) (this includes another [brief tutorial](https://www.howtographql.com/graphql-elixir/0-introduction/) using Absinthe) 12 | 13 | ## The Example 14 | 15 | The tutorial expects you to have a properly set-up [Phoenix application](https://hexdocs.pm/phoenix/installation.html) with [absinthe](https://hex.pm/packages/absinthe) and [absinthe_plug](https://hex.pm/packages/absinthe_plug) added to the dependencies. 16 | 17 | > If you'd like to cheat, you can find the finished code for the tutorial 18 | > in the [Absinthe Example](https://github.com/absinthe-graphql/absinthe_tutorial) 19 | > project on GitHub. 20 | 21 | ## First Step 22 | 23 | Let's get started with [our first query](our-first-query.md)! 24 | -------------------------------------------------------------------------------- /guides/variables.md: -------------------------------------------------------------------------------- 1 | # Using Document Variables 2 | 3 | GraphQL supports query documents that declare variables that can be accepted to fill-in values. This is a useful mechanism for reusing GraphQL documents---instead of attempting to interpolate values yourself. 4 | 5 | - To support variables, simply define them for your query document [as the specification expects](https://spec.graphql.org/October2021/#sec-Language.Variables), and pass in a `variables` option to `Absinthe.run`. 6 | - If you're using [absinthe_plug](https://github.com/absinthe-graphql/absinthe_plug), variables are passed in for you automatically after being parsed 7 | from the query parameters or `POST` body. 8 | 9 | Here's an example of defining a non-nullable variable, `id`, in a document and then executing the document with a value for the variable: 10 | 11 | ```elixir 12 | """ 13 | query GetItem($id: ID!) { 14 | item(id: $id) { 15 | name 16 | } 17 | } 18 | """ 19 | |> Absinthe.run(MyAppWeb.Schema, variables: %{"id" => "bar"}) 20 | 21 | # Result 22 | {:ok, %{data: %{"item" => %{"name" => "Bar"}}}} 23 | ``` 24 | -------------------------------------------------------------------------------- /lib/absinthe/adapter/passthrough.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Adapter.Passthrough do 2 | @moduledoc """ 3 | The default adapter, which makes no changes to incoming query document 4 | ASTs or outgoing results. 5 | """ 6 | 7 | use Absinthe.Adapter 8 | 9 | def load_document(doc), do: doc 10 | 11 | def dump_results(results), do: results 12 | end 13 | -------------------------------------------------------------------------------- /lib/absinthe/adapter/underscore.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Adapter.Underscore do 2 | @moduledoc """ 3 | Underscores external input and leaves external input alone. Unlike the 4 | `Absinthe.Adapter.Passthrough` this does not break introspection (because 5 | introspection relies on underscoring incoming introspection queries which we 6 | still do). 7 | """ 8 | 9 | use Absinthe.Adapter 10 | 11 | def to_internal_name(nil, _role) do 12 | nil 13 | end 14 | 15 | def to_internal_name("__" <> camelized_name, role) do 16 | "__" <> to_internal_name(camelized_name, role) 17 | end 18 | 19 | def to_internal_name(camelized_name, _role) do 20 | camelized_name 21 | |> Macro.underscore() 22 | end 23 | 24 | def to_external_name(name, _role) do 25 | name 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/document.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Document do 2 | @moduledoc false 3 | alias Absinthe.Blueprint 4 | 5 | @type t :: 6 | Blueprint.Document.Field.t() 7 | | Blueprint.Document.Fragment.t() 8 | | Blueprint.Document.Operation.t() 9 | | Blueprint.Document.VariableDefinition.t() 10 | 11 | @type selection_t :: 12 | Blueprint.Document.Field.t() 13 | | Blueprint.Document.Fragment.Inline.t() 14 | | Blueprint.Document.Fragment.Spread.t() 15 | end 16 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/document/field.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Document.Field do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Phase, Type} 5 | 6 | @enforce_keys [:name] 7 | defstruct [ 8 | :name, 9 | alias: nil, 10 | selections: [], 11 | arguments: [], 12 | argument_data: %{}, 13 | directives: [], 14 | # Added by phases 15 | flags: %{}, 16 | errors: [], 17 | source_location: nil, 18 | type_conditions: [], 19 | schema_node: nil, 20 | complexity: nil, 21 | # Set during resolution, this holds the concrete parent type 22 | # as determined by the resolution phase. 23 | parent_type: nil 24 | ] 25 | 26 | @type t :: %__MODULE__{ 27 | name: String.t(), 28 | selections: [Blueprint.Document.selection_t()], 29 | arguments: [Blueprint.Input.Argument.t()], 30 | directives: [Blueprint.Directive.t()], 31 | flags: Blueprint.flags_t(), 32 | errors: [Phase.Error.t()], 33 | source_location: nil | Blueprint.SourceLocation.t(), 34 | type_conditions: [Blueprint.TypeReference.Name], 35 | schema_node: Type.t(), 36 | complexity: nil | non_neg_integer 37 | } 38 | end 39 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/document/fragment.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Document.Fragment do 2 | @moduledoc false 3 | 4 | alias __MODULE__ 5 | 6 | @type t :: 7 | Fragment.Inline.t() 8 | | Fragment.Named.t() 9 | | Fragment.Spread.t() 10 | end 11 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/document/fragment/inline.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Document.Fragment.Inline do 2 | @moduledoc false 3 | 4 | alias Absinthe.Blueprint 5 | 6 | @enforce_keys [:type_condition] 7 | defstruct [ 8 | :type_condition, 9 | selections: [], 10 | directives: [], 11 | source_location: nil, 12 | # Populated by phases 13 | schema_node: nil, 14 | complexity: nil, 15 | flags: %{}, 16 | errors: [] 17 | ] 18 | 19 | @type t :: %__MODULE__{ 20 | directives: [Blueprint.Directive.t()], 21 | errors: [Absinthe.Phase.Error.t()], 22 | flags: Blueprint.flags_t(), 23 | selections: [Blueprint.Document.selection_t()], 24 | schema_node: nil | Absinthe.Type.t(), 25 | source_location: nil | Blueprint.SourceLocation.t(), 26 | type_condition: Blueprint.TypeReference.Name.t() 27 | } 28 | end 29 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/document/fragment/named.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Document.Fragment.Named do 2 | @moduledoc false 3 | 4 | alias Absinthe.Blueprint 5 | alias __MODULE__ 6 | 7 | @enforce_keys [:name, :type_condition] 8 | defstruct [ 9 | :name, 10 | :type_condition, 11 | selections: [], 12 | directives: [], 13 | source_location: nil, 14 | # Populated by phases 15 | schema_node: nil, 16 | complexity: nil, 17 | flags: %{}, 18 | errors: [] 19 | ] 20 | 21 | @type t :: %__MODULE__{ 22 | directives: [Blueprint.Directive.t()], 23 | errors: [Absinthe.Phase.Error.t()], 24 | name: String.t(), 25 | selections: [Blueprint.Document.selection_t()], 26 | schema_node: nil | Absinthe.Type.t(), 27 | source_location: nil | Blueprint.SourceLocation.t(), 28 | flags: Blueprint.flags_t(), 29 | type_condition: Blueprint.TypeReference.Name.t() 30 | } 31 | 32 | @doc """ 33 | Generate a use reference for a fragment. 34 | """ 35 | @spec to_use(t) :: Named.Use.t() 36 | def to_use(%__MODULE__{} = node) do 37 | %Named.Use{ 38 | name: node.name, 39 | source_location: node.source_location 40 | } 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/document/fragment/named/use.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Document.Fragment.Named.Use do 2 | @moduledoc false 3 | 4 | alias Absinthe.Blueprint 5 | 6 | @enforce_keys [:name, :source_location] 7 | defstruct [ 8 | :name, 9 | :source_location 10 | ] 11 | 12 | @type t :: %__MODULE__{ 13 | name: String.t(), 14 | source_location: nil | Blueprint.SourceLocation.t() 15 | } 16 | end 17 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/document/fragment/spread.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Document.Fragment.Spread do 2 | @moduledoc false 3 | 4 | alias Absinthe.Blueprint 5 | 6 | @enforce_keys [:name] 7 | defstruct [ 8 | :name, 9 | directives: [], 10 | source_location: nil, 11 | # Populated by phases 12 | complexity: nil, 13 | flags: %{}, 14 | errors: [] 15 | ] 16 | 17 | @type t :: %__MODULE__{ 18 | directives: [Blueprint.Directive.t()], 19 | errors: [Absinthe.Phase.Error.t()], 20 | name: String.t(), 21 | flags: Blueprint.flags_t(), 22 | source_location: nil | Blueprint.SourceLocation.t() 23 | } 24 | end 25 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/document/variable_definition.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Document.VariableDefinition do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Type} 5 | 6 | @enforce_keys [:name, :type] 7 | defstruct [ 8 | :name, 9 | :type, 10 | directives: [], 11 | default_value: nil, 12 | source_location: nil, 13 | # Added by phases 14 | flags: %{}, 15 | provided_value: nil, 16 | errors: [], 17 | schema_node: nil 18 | ] 19 | 20 | @type t :: %__MODULE__{ 21 | name: String.t(), 22 | type: Blueprint.TypeReference.t(), 23 | directives: [Blueprint.Directive.t()], 24 | default_value: Blueprint.Input.t(), 25 | source_location: nil | Blueprint.SourceLocation.t(), 26 | provided_value: nil | Blueprint.Input.t(), 27 | errors: [Absinthe.Phase.Error.t()], 28 | flags: Blueprint.flags_t(), 29 | schema_node: Type.t() 30 | } 31 | end 32 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/draft.ex: -------------------------------------------------------------------------------- 1 | defprotocol Absinthe.Blueprint.Draft do 2 | @moduledoc false 3 | 4 | def convert(node, root) 5 | end 6 | 7 | defimpl Absinthe.Blueprint.Draft, for: List do 8 | def convert(nodes, root) do 9 | Enum.map(nodes, &Absinthe.Blueprint.Draft.convert(&1, root)) 10 | end 11 | end 12 | 13 | defimpl Absinthe.Blueprint.Draft, for: Atom do 14 | def convert(atom, _) do 15 | atom 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/input/argument.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Input.Argument do 2 | @moduledoc false 3 | 4 | alias Absinthe.Blueprint 5 | 6 | @enforce_keys [:name, :source_location, :input_value] 7 | defstruct [ 8 | :name, 9 | :input_value, 10 | :source_location, 11 | # Added by phases 12 | schema_node: nil, 13 | # Value converted to native elixir value 14 | value: nil, 15 | flags: %{}, 16 | errors: [] 17 | ] 18 | 19 | @type t :: %__MODULE__{ 20 | name: String.t(), 21 | input_value: Blueprint.Input.Value.t(), 22 | source_location: Blueprint.SourceLocation.t(), 23 | schema_node: nil | Absinthe.Type.Argument.t(), 24 | value: any, 25 | flags: Blueprint.flags_t(), 26 | errors: [Absinthe.Phase.Error.t()] 27 | } 28 | 29 | @spec value_map([t]) :: %{atom => any} 30 | def value_map(arguments) do 31 | arguments 32 | |> Enum.filter(fn 33 | %__MODULE__{schema_node: nil} -> 34 | false 35 | 36 | %__MODULE__{input_value: %{normalized: %Blueprint.Input.Null{}}, value: nil} -> 37 | true 38 | 39 | %__MODULE__{value: nil} -> 40 | false 41 | 42 | arg -> 43 | arg 44 | end) 45 | |> Map.new(&{&1.schema_node.identifier, &1.value}) 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/input/boolean.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Input.Boolean do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Phase} 5 | 6 | @enforce_keys [:value] 7 | defstruct [ 8 | :value, 9 | :source_location, 10 | # Added by phases 11 | flags: %{}, 12 | schema_node: nil, 13 | errors: [] 14 | ] 15 | 16 | @type t :: %__MODULE__{ 17 | value: true | false, 18 | flags: Blueprint.flags_t(), 19 | schema_node: nil | Absinthe.Type.t(), 20 | source_location: Blueprint.SourceLocation.t(), 21 | errors: [Phase.Error.t()] 22 | } 23 | end 24 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/input/enum.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Input.Enum do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Phase} 5 | 6 | @enforce_keys [:value, :source_location] 7 | defstruct [ 8 | :value, 9 | :source_location, 10 | # Added by phases 11 | flags: %{}, 12 | schema_node: nil, 13 | errors: [] 14 | ] 15 | 16 | @type t :: %__MODULE__{ 17 | value: String.t(), 18 | flags: Blueprint.flags_t(), 19 | schema_node: nil | Absinthe.Type.t(), 20 | source_location: Blueprint.SourceLocation.t(), 21 | errors: [Phase.Error.t()] 22 | } 23 | end 24 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/input/field.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Input.Field do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Type} 5 | 6 | @enforce_keys [:name, :input_value] 7 | defstruct [ 8 | :name, 9 | :input_value, 10 | # Added by phases 11 | flags: %{}, 12 | source_location: nil, 13 | schema_node: nil, 14 | errors: [] 15 | ] 16 | 17 | @type t :: %__MODULE__{ 18 | name: String.t(), 19 | input_value: Blueprint.Input.Value.t(), 20 | flags: Blueprint.flags_t(), 21 | schema_node: nil | Type.Field.t(), 22 | source_location: Blueprint.SourceLocation.t(), 23 | errors: [Absinthe.Phase.Error.t()] 24 | } 25 | end 26 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/input/float.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Input.Float do 2 | @moduledoc false 3 | 4 | alias Absinthe.Blueprint 5 | 6 | @enforce_keys [:value] 7 | defstruct [ 8 | :value, 9 | :source_location, 10 | # Added by phases 11 | flags: %{}, 12 | schema_node: nil, 13 | errors: [] 14 | ] 15 | 16 | @type t :: %__MODULE__{ 17 | value: float, 18 | flags: Blueprint.flags_t(), 19 | source_location: Blueprint.SourceLocation.t(), 20 | schema_node: nil | Absinthe.Type.t(), 21 | errors: [Absinthe.Phase.Error.t()] 22 | } 23 | end 24 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/input/generated.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Input.Generated do 2 | @enforce_keys [:by] 3 | defstruct [:by] 4 | 5 | @moduledoc false 6 | 7 | # A number of phases need to check for `nil` normalized values. This is problematic 8 | # for situations where a value has been generated from a default value. This struct 9 | # can be placed on the normalized value to indicate that it is not null, but also 10 | # that it is not a proper blueprint input. 11 | end 12 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/input/integer.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Input.Integer do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Phase} 5 | 6 | @enforce_keys [:value] 7 | defstruct [ 8 | :value, 9 | :source_location, 10 | # Added by phases 11 | flags: %{}, 12 | schema_node: nil, 13 | errors: [] 14 | ] 15 | 16 | @type t :: %__MODULE__{ 17 | value: integer, 18 | flags: Blueprint.flags_t(), 19 | source_location: Blueprint.SourceLocation.t(), 20 | schema_node: nil | Absinthe.Type.t(), 21 | errors: [Phase.Error.t()] 22 | } 23 | end 24 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/input/null.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Input.Null do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Phase} 5 | 6 | defstruct [ 7 | :source_location, 8 | # Added by phases 9 | flags: %{}, 10 | schema_node: nil, 11 | errors: [] 12 | ] 13 | 14 | @type t :: %__MODULE__{ 15 | flags: Blueprint.flags_t(), 16 | schema_node: nil | Absinthe.Type.t(), 17 | source_location: Blueprint.SourceLocation.t(), 18 | errors: [Phase.Error.t()] 19 | } 20 | end 21 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/input/object.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Input.Object do 2 | @moduledoc false 3 | 4 | alias Absinthe.Blueprint 5 | 6 | @enforce_keys [:fields] 7 | defstruct [ 8 | :source_location, 9 | fields: [], 10 | # Added by phases 11 | flags: %{}, 12 | schema_node: nil, 13 | errors: [] 14 | ] 15 | 16 | @type t :: %__MODULE__{ 17 | fields: [Blueprint.Input.Field.t()], 18 | flags: Blueprint.flags_t(), 19 | schema_node: 20 | nil 21 | | Absinthe.Type.InputObject.t() 22 | | Absinthe.Type.NonNull.t(Absinthe.Type.InputObject.t()), 23 | source_location: Blueprint.SourceLocation.t(), 24 | errors: [Absinthe.Phase.Error.t()] 25 | } 26 | end 27 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/input/raw_value.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Input.RawValue do 2 | @moduledoc false 3 | 4 | alias Absinthe.Blueprint.Input.Object 5 | 6 | @enforce_keys [:content] 7 | defstruct [ 8 | :content 9 | ] 10 | 11 | @type t :: %__MODULE__{ 12 | content: Object.t() 13 | } 14 | end 15 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/input/string.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Input.String do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Phase} 5 | 6 | @enforce_keys [:value] 7 | defstruct [ 8 | :value, 9 | :source_location, 10 | # Added by phases 11 | flags: %{}, 12 | schema_node: nil, 13 | errors: [] 14 | ] 15 | 16 | @type t :: %__MODULE__{ 17 | value: String.t(), 18 | flags: Blueprint.flags_t(), 19 | schema_node: nil | Absinthe.Type.t(), 20 | source_location: Blueprint.SourceLocation.t(), 21 | errors: [Phase.Error.t()] 22 | } 23 | end 24 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/input/variable.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Input.Variable do 2 | @moduledoc false 3 | 4 | alias __MODULE__ 5 | alias Absinthe.{Blueprint, Phase} 6 | 7 | @enforce_keys [:name] 8 | defstruct [ 9 | :name, 10 | source_location: nil, 11 | # Added by phases 12 | flags: %{}, 13 | errors: [] 14 | ] 15 | 16 | @type t :: %__MODULE__{ 17 | name: String.t(), 18 | source_location: nil | Blueprint.SourceLocation.t(), 19 | # Added by phases 20 | flags: Blueprint.flags_t(), 21 | errors: [Phase.Error.t()] 22 | } 23 | 24 | @doc """ 25 | Generate a use reference for a variable. 26 | """ 27 | @spec to_use(t) :: Variable.Use.t() 28 | def to_use(%__MODULE__{} = node) do 29 | %Variable.Use{ 30 | name: node.name, 31 | source_location: node.source_location 32 | } 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/input/variable/use.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Input.Variable.Use do 2 | @moduledoc false 3 | 4 | alias Absinthe.Blueprint 5 | 6 | @enforce_keys [:name, :source_location] 7 | defstruct [ 8 | :name, 9 | :source_location 10 | ] 11 | 12 | @type t :: %__MODULE__{ 13 | name: String.t(), 14 | source_location: nil | Blueprint.SourceLocation.t() 15 | } 16 | end 17 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/result/leaf.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Result.Leaf do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Phase} 5 | 6 | @enforce_keys [:emitter, :value] 7 | defstruct [ 8 | :emitter, 9 | :value, 10 | errors: [], 11 | flags: %{}, 12 | extensions: %{} 13 | ] 14 | 15 | @type t :: %__MODULE__{ 16 | emitter: Blueprint.Document.Field.t(), 17 | value: Blueprint.Execution.node_t(), 18 | errors: [Phase.Error.t()], 19 | flags: Blueprint.flags_t(), 20 | extensions: %{any => any} 21 | } 22 | end 23 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/result/list.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Result.List do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Phase} 5 | 6 | @enforce_keys [:emitter, :values] 7 | defstruct [ 8 | :emitter, 9 | :values, 10 | errors: [], 11 | flags: %{}, 12 | extensions: %{} 13 | ] 14 | 15 | @type t :: %__MODULE__{ 16 | emitter: Blueprint.Document.Field.t(), 17 | values: [Blueprint.Execution.node_t()], 18 | errors: [Phase.Error.t()], 19 | flags: Blueprint.flags_t(), 20 | extensions: %{any => any} 21 | } 22 | end 23 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/result/object.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Result.Object do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Phase} 5 | 6 | @enforce_keys [:emitter, :root_value] 7 | defstruct [ 8 | :root_value, 9 | :emitter, 10 | :fields, 11 | errors: [], 12 | flags: %{}, 13 | extensions: %{} 14 | ] 15 | 16 | @type t :: %__MODULE__{ 17 | emitter: Blueprint.Document.Field.t(), 18 | fields: [Blueprint.Execution.node_t()], 19 | errors: [Phase.Error.t()], 20 | flags: Blueprint.flags_t(), 21 | extensions: %{any => any} 22 | } 23 | end 24 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/schema/deprecation.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Schema.Deprecation do 2 | @moduledoc false 3 | 4 | defstruct reason: nil 5 | 6 | @type t :: %__MODULE__{ 7 | reason: nil | String.t() 8 | } 9 | end 10 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/schema/enum_value_definition.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Schema.EnumValueDefinition do 2 | @moduledoc false 3 | 4 | alias Absinthe.Blueprint 5 | 6 | @enforce_keys [:value] 7 | defstruct [ 8 | :value, 9 | :name, 10 | :identifier, 11 | deprecation: nil, 12 | directives: [], 13 | source_location: nil, 14 | description: nil, 15 | # Added by phases 16 | flags: %{}, 17 | module: nil, 18 | errors: [], 19 | __reference__: nil, 20 | __private__: [] 21 | ] 22 | 23 | @type t :: %__MODULE__{ 24 | value: String.t(), 25 | description: nil | String.t(), 26 | deprecation: nil | Blueprint.Schema.Deprecation.t(), 27 | directives: [Blueprint.Directive.t()], 28 | source_location: nil | Blueprint.SourceLocation.t(), 29 | # Added by phases 30 | flags: Blueprint.flags_t(), 31 | errors: [Absinthe.Phase.Error.t()] 32 | } 33 | end 34 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/schema/schema_declaration.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Schema.SchemaDeclaration do 2 | @moduledoc false 3 | 4 | alias Absinthe.Blueprint 5 | 6 | defstruct description: nil, 7 | module: nil, 8 | field_definitions: [], 9 | directives: [], 10 | source_location: nil, 11 | # Added by phases 12 | flags: %{}, 13 | errors: [], 14 | __reference__: nil, 15 | __private__: [] 16 | 17 | @type t :: %__MODULE__{ 18 | description: nil | String.t(), 19 | module: nil | module(), 20 | directives: [Blueprint.Directive.t()], 21 | field_definitions: [Blueprint.Schema.FieldDefinition.t()], 22 | source_location: nil | Blueprint.SourceLocation.t(), 23 | # Added by phases 24 | flags: Blueprint.flags_t(), 25 | errors: [Absinthe.Phase.Error.t()] 26 | } 27 | 28 | defimpl Inspect do 29 | defdelegate inspect(term, options), 30 | to: Absinthe.Schema.Notation.SDL.Render 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/schema/schema_definition.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Schema.SchemaDefinition do 2 | @moduledoc false 3 | 4 | alias Absinthe.Blueprint 5 | 6 | defstruct description: nil, 7 | module: nil, 8 | type_definitions: [], 9 | directive_definitions: [], 10 | type_artifacts: [], 11 | directive_artifacts: [], 12 | type_extensions: [], 13 | directives: [], 14 | source_location: nil, 15 | type_extension_imports: [], 16 | # Added by phases 17 | schema_declaration: nil, 18 | flags: %{}, 19 | imports: [], 20 | directive_imports: [], 21 | errors: [], 22 | __private__: [], 23 | __reference__: nil 24 | 25 | @type t :: %__MODULE__{ 26 | description: nil | String.t(), 27 | # types: [Blueprint.Schema.FieldDefinition.t], 28 | directives: [Blueprint.Directive.t()], 29 | source_location: nil | Blueprint.SourceLocation.t(), 30 | # Added by phases 31 | flags: Blueprint.flags_t(), 32 | errors: [Absinthe.Phase.Error.t()] 33 | } 34 | end 35 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/schema/type_extension_definition.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.Schema.TypeExtensionDefinition do 2 | @moduledoc false 3 | defstruct definition: nil, 4 | module: nil, 5 | source_location: nil, 6 | # # Added by phases 7 | flags: %{}, 8 | errors: [], 9 | __private__: [], 10 | __reference__: nil 11 | end 12 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/source_location.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.SourceLocation do 2 | @moduledoc false 3 | 4 | @enforce_keys [:line, :column] 5 | defstruct [ 6 | :line, 7 | :column 8 | ] 9 | 10 | @type t :: %__MODULE__{ 11 | line: pos_integer, 12 | column: pos_integer 13 | } 14 | 15 | @doc """ 16 | Generate a `SourceLocation.t()` given a location 17 | """ 18 | @spec at(loc :: Absinthe.Language.loc_t()) :: t 19 | def at(loc) do 20 | %__MODULE__{line: loc.line, column: loc.column} 21 | end 22 | 23 | @doc """ 24 | Generate a `SourceLocation.t()` given line and column numbers 25 | """ 26 | @spec at(line :: pos_integer, column :: non_neg_integer) :: t 27 | def at(line, column) do 28 | %__MODULE__{line: line, column: column} 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/type_reference/identifier.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.TypeReference.Identifier do 2 | @moduledoc false 3 | 4 | alias Absinthe.Phase 5 | 6 | @enforce_keys [:id] 7 | defstruct [ 8 | :id, 9 | :schema_node, 10 | errors: [] 11 | ] 12 | 13 | @type t :: %__MODULE__{ 14 | id: any(), 15 | errors: [Phase.Error.t()] 16 | } 17 | end 18 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/type_reference/list.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.TypeReference.List do 2 | @moduledoc false 3 | 4 | alias Absinthe.Blueprint 5 | 6 | @enforce_keys [:of_type] 7 | defstruct [ 8 | :of_type, 9 | errors: [] 10 | ] 11 | 12 | @type t :: %__MODULE__{ 13 | of_type: Blueprint.TypeReference.t(), 14 | errors: [Absinthe.Phase.Error.t()] 15 | } 16 | end 17 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/type_reference/name.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.TypeReference.Name do 2 | @moduledoc false 3 | 4 | alias Absinthe.Phase 5 | 6 | @enforce_keys [:name] 7 | defstruct [ 8 | :name, 9 | :schema_node, 10 | errors: [] 11 | ] 12 | 13 | @type t :: %__MODULE__{ 14 | name: String.t(), 15 | errors: [Phase.Error.t()] 16 | } 17 | end 18 | -------------------------------------------------------------------------------- /lib/absinthe/blueprint/type_reference/non_null.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Blueprint.TypeReference.NonNull do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Phase} 5 | 6 | @enforce_keys [:of_type] 7 | defstruct [ 8 | :of_type, 9 | errors: [] 10 | ] 11 | 12 | @type t :: %__MODULE__{ 13 | of_type: Blueprint.TypeReference.t(), 14 | errors: [Phase.Error.t()] 15 | } 16 | end 17 | -------------------------------------------------------------------------------- /lib/absinthe/complexity.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Complexity do 2 | @moduledoc """ 3 | Extra metadata passed to aid complexity analysis functions, describing the 4 | current field's environment. 5 | """ 6 | alias Absinthe.{Blueprint, Schema} 7 | 8 | @enforce_keys [:context, :root_value, :schema, :definition] 9 | defstruct [:context, :root_value, :schema, :definition] 10 | 11 | @typedoc """ 12 | - `:definition` - The Blueprint definition for this field. 13 | - `:context` - The context passed to `Absinthe.run`. 14 | - `:root_value` - The root value passed to `Absinthe.run`, if any. 15 | - `:schema` - The current schema. 16 | """ 17 | @type t :: %__MODULE__{ 18 | definition: Blueprint.node_t(), 19 | context: map, 20 | root_value: any, 21 | schema: Schema.t() 22 | } 23 | end 24 | -------------------------------------------------------------------------------- /lib/absinthe/formatter.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Formatter do 2 | @moduledoc """ 3 | Formatter task for graphql 4 | 5 | Will format files with the extensions .graphql or .gql 6 | 7 | ## Example 8 | ```elixir 9 | Absinthe.Formatter.format("{ version }") 10 | "{\n version\n}\n" 11 | ``` 12 | 13 | 14 | From Elixir 1.13 onwards the Absinthe.Formatter can be added to 15 | the formatter as a plugin: 16 | 17 | ```elixir 18 | # .formatter.exs 19 | [ 20 | # Define the desired plugins 21 | plugins: [Absinthe.Formatter], 22 | # Remember to update the inputs list to include the new extensions 23 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}", "{lib,priv}/**/*.{gql,graphql}"] 24 | ] 25 | ``` 26 | """ 27 | 28 | def features(_opts) do 29 | [sigils: [], extensions: [".graphql", ".gql"]] 30 | end 31 | 32 | def format(contents, _opts \\ []) do 33 | {:ok, blueprint} = Absinthe.Phase.Parse.run(contents, []) 34 | inspect(blueprint.input, pretty: true) 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/absinthe/introspection/directive_location.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Introspection.DirectiveLocation do 2 | @moduledoc false 3 | 4 | # https://spec.graphql.org/draft/#sec-Schema-Introspection 5 | 6 | @executable_directive_locations [ 7 | :query, 8 | :mutation, 9 | :subscription, 10 | :field, 11 | :fragment_definition, 12 | :fragment_spread, 13 | :inline_fragment, 14 | :variable_definition 15 | ] 16 | @type_system_directive_locations [ 17 | :schema, 18 | :scalar, 19 | :object, 20 | :field_definition, 21 | :argument_definition, 22 | :interface, 23 | :union, 24 | :enum, 25 | :enum_value, 26 | :input_object, 27 | :input_field_definition 28 | ] 29 | 30 | def values do 31 | @executable_directive_locations ++ 32 | @type_system_directive_locations 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/absinthe/introspection/type_kind.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Introspection.TypeKind do 2 | @moduledoc false 3 | 4 | # https://spec.graphql.org/draft/#sec-Type-Kinds 5 | 6 | defmacro __using__(kind) do 7 | quote do 8 | @behaviour unquote(__MODULE__) 9 | def kind, do: unquote(kind) 10 | end 11 | end 12 | 13 | @type type_kind :: 14 | :scalar 15 | | :object 16 | | :interface 17 | | :union 18 | | :enum 19 | | :input_object 20 | | :list 21 | | :non_null 22 | 23 | @callback kind() :: type_kind() 24 | 25 | def values do 26 | [ 27 | :scalar, 28 | :object, 29 | :interface, 30 | :union, 31 | :enum, 32 | :input_object, 33 | :list, 34 | :non_null 35 | ] 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/absinthe/language/argument.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.Argument do 2 | @moduledoc false 3 | 4 | alias Absinthe.Blueprint 5 | 6 | defstruct name: nil, 7 | value: nil, 8 | loc: %{} 9 | 10 | @type t :: %__MODULE__{ 11 | name: String.t(), 12 | value: %{value: any}, 13 | loc: Absinthe.Language.loc_t() 14 | } 15 | 16 | defimpl Blueprint.Draft do 17 | def convert(node, doc) do 18 | %Blueprint.Input.Argument{ 19 | name: node.name, 20 | input_value: %Blueprint.Input.RawValue{ 21 | content: Absinthe.Blueprint.Draft.convert(node.value, doc) 22 | }, 23 | source_location: source_location(node) 24 | } 25 | end 26 | 27 | defp source_location(%{loc: nil}), do: nil 28 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/absinthe/language/boolean_value.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.BooleanValue do 2 | @moduledoc false 3 | 4 | alias Absinthe.Blueprint 5 | 6 | defstruct [ 7 | :value, 8 | :loc 9 | ] 10 | 11 | @type t :: %__MODULE__{ 12 | value: boolean, 13 | loc: Absinthe.Language.loc_t() 14 | } 15 | 16 | defimpl Blueprint.Draft do 17 | def convert(node, doc) do 18 | %Blueprint.Input.Boolean{ 19 | value: Absinthe.Blueprint.Draft.convert(node.value, doc), 20 | source_location: source_location(node) 21 | } 22 | end 23 | 24 | defp source_location(%{loc: nil}), do: nil 25 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/absinthe/language/directive.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.Directive do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct name: nil, 7 | arguments: [], 8 | loc: nil 9 | 10 | @type t :: %__MODULE__{ 11 | name: String.t(), 12 | arguments: [Language.Argument], 13 | loc: Language.loc_t() 14 | } 15 | 16 | defimpl Blueprint.Draft do 17 | def convert(node, doc) do 18 | %Blueprint.Directive{ 19 | name: node.name, 20 | arguments: Absinthe.Blueprint.Draft.convert(node.arguments, doc), 21 | source_location: source_location(node) 22 | } 23 | end 24 | 25 | defp source_location(%{loc: nil}), do: nil 26 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/absinthe/language/enum_type_definition.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.EnumTypeDefinition do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct name: nil, 7 | description: nil, 8 | values: [], 9 | directives: [], 10 | loc: %{line: nil} 11 | 12 | @type t :: %__MODULE__{ 13 | name: String.t(), 14 | description: nil | String.t(), 15 | values: [String.t()], 16 | directives: [Language.Directive.t()], 17 | loc: Language.loc_t() 18 | } 19 | 20 | defimpl Blueprint.Draft do 21 | def convert(node, doc) do 22 | %Blueprint.Schema.EnumTypeDefinition{ 23 | name: node.name, 24 | description: node.description, 25 | identifier: Macro.underscore(node.name) |> String.to_atom(), 26 | values: Absinthe.Blueprint.Draft.convert(node.values, doc), 27 | directives: Absinthe.Blueprint.Draft.convert(node.directives, doc), 28 | source_location: source_location(node) 29 | } 30 | end 31 | 32 | defp source_location(%{loc: nil}), do: nil 33 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/absinthe/language/enum_value.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.EnumValue do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct value: nil, 7 | loc: %{line: nil} 8 | 9 | @type t :: %__MODULE__{ 10 | value: any, 11 | loc: Language.loc_t() 12 | } 13 | 14 | defimpl Blueprint.Draft do 15 | def convert(node, _doc) do 16 | %Blueprint.Input.Enum{ 17 | value: node.value, 18 | source_location: source_location(node) 19 | } 20 | end 21 | 22 | defp source_location(%{loc: nil}), do: nil 23 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/absinthe/language/enum_value_definition.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.EnumValueDefinition do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | @enforce_keys [:value] 7 | defstruct [ 8 | :value, 9 | description: nil, 10 | directives: [], 11 | loc: %{line: nil, column: nil} 12 | ] 13 | 14 | @type t :: %__MODULE__{ 15 | value: String.t(), 16 | description: nil | String.t(), 17 | directives: [Language.Directive.t()], 18 | loc: Language.loc_t() 19 | } 20 | 21 | defimpl Blueprint.Draft do 22 | def convert(node, doc) do 23 | %Blueprint.Schema.EnumValueDefinition{ 24 | value: node.value |> Macro.underscore() |> String.to_atom(), 25 | name: node.value, 26 | identifier: node.value |> Macro.underscore() |> String.to_atom(), 27 | description: node.description, 28 | directives: Absinthe.Blueprint.Draft.convert(node.directives, doc), 29 | source_location: source_location(node) 30 | } 31 | end 32 | 33 | defp source_location(%{loc: nil}), do: nil 34 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/absinthe/language/float_value.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.FloatValue do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct [ 7 | :value, 8 | :loc 9 | ] 10 | 11 | @type t :: %__MODULE__{ 12 | value: float, 13 | loc: Language.loc_t() 14 | } 15 | 16 | defimpl Blueprint.Draft do 17 | def convert(node, _doc) do 18 | %Blueprint.Input.Float{ 19 | value: node.value, 20 | source_location: source_location(node) 21 | } 22 | end 23 | 24 | defp source_location(%{loc: nil}), do: nil 25 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/absinthe/language/fragment.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.Fragment do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct name: nil, 7 | type_condition: nil, 8 | directives: [], 9 | selection_set: nil, 10 | loc: %{line: nil} 11 | 12 | @type t :: %__MODULE__{ 13 | name: String.t(), 14 | type_condition: nil | Language.NamedType.t(), 15 | directives: [Language.Directive.t()], 16 | selection_set: Language.SelectionSet.t(), 17 | loc: Language.loc_t() 18 | } 19 | 20 | defimpl Blueprint.Draft do 21 | def convert(node, doc) do 22 | %Blueprint.Document.Fragment.Named{ 23 | name: node.name, 24 | type_condition: Blueprint.Draft.convert(node.type_condition, doc), 25 | selections: Blueprint.Draft.convert(node.selection_set.selections, doc), 26 | directives: Blueprint.Draft.convert(node.directives, doc), 27 | source_location: source_location(node) 28 | } 29 | end 30 | 31 | defp source_location(%{loc: nil}) do 32 | nil 33 | end 34 | 35 | defp source_location(%{loc: loc}) do 36 | Blueprint.SourceLocation.at(loc) 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/absinthe/language/fragment_spread.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.FragmentSpread do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct name: nil, 7 | directives: [], 8 | loc: %{line: nil} 9 | 10 | @type t :: %__MODULE__{ 11 | name: String.t(), 12 | directives: [Language.Directive.t()] 13 | } 14 | 15 | defimpl Blueprint.Draft do 16 | def convert(node, doc) do 17 | %Blueprint.Document.Fragment.Spread{ 18 | name: node.name, 19 | directives: Blueprint.Draft.convert(node.directives, doc), 20 | source_location: source_location(node) 21 | } 22 | end 23 | 24 | defp source_location(%{loc: nil}) do 25 | nil 26 | end 27 | 28 | defp source_location(%{loc: loc}) do 29 | Blueprint.SourceLocation.at(loc) 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/absinthe/language/inline_fragment.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.InlineFragment do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct type_condition: nil, 7 | directives: [], 8 | selection_set: nil, 9 | loc: %{line: nil} 10 | 11 | @type t :: %__MODULE__{ 12 | type_condition: nil | Language.NamedType.t(), 13 | directives: [Language.Directive.t()], 14 | selection_set: Language.SelectionSet.t(), 15 | loc: Language.loc_t() 16 | } 17 | 18 | defimpl Blueprint.Draft do 19 | def convert(node, doc) do 20 | %Blueprint.Document.Fragment.Inline{ 21 | type_condition: Blueprint.Draft.convert(node.type_condition, doc), 22 | selections: Blueprint.Draft.convert(node.selection_set.selections, doc), 23 | directives: Blueprint.Draft.convert(node.directives, doc), 24 | source_location: source_location(node) 25 | } 26 | end 27 | 28 | defp source_location(%{loc: nil}) do 29 | nil 30 | end 31 | 32 | defp source_location(%{loc: loc}) do 33 | Blueprint.SourceLocation.at(loc) 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/absinthe/language/input_object_type_definition.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.InputObjectTypeDefinition do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct name: nil, 7 | description: nil, 8 | fields: [], 9 | directives: [], 10 | loc: %{line: nil}, 11 | errors: [] 12 | 13 | @type t :: %__MODULE__{ 14 | name: String.t(), 15 | description: nil | String.t(), 16 | fields: [Language.InputValueDefinition.t()], 17 | directives: [Language.Directive.t()], 18 | loc: Language.loc_t() 19 | } 20 | 21 | defimpl Blueprint.Draft do 22 | def convert(node, doc) do 23 | %Blueprint.Schema.InputObjectTypeDefinition{ 24 | identifier: node.name |> Macro.underscore() |> String.to_atom(), 25 | name: node.name, 26 | description: node.description, 27 | fields: 28 | for value <- Absinthe.Blueprint.Draft.convert(node.fields, doc) do 29 | %{value | placement: :input_field_definition} 30 | end, 31 | directives: Absinthe.Blueprint.Draft.convert(node.directives, doc), 32 | source_location: source_location(node) 33 | } 34 | end 35 | 36 | defp source_location(%{loc: nil}), do: nil 37 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/absinthe/language/int_value.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.IntValue do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct [ 7 | :value, 8 | :loc 9 | ] 10 | 11 | @type t :: %__MODULE__{ 12 | value: integer, 13 | loc: Language.loc_t() 14 | } 15 | 16 | defimpl Blueprint.Draft do 17 | def convert(node, _doc) do 18 | %Blueprint.Input.Integer{ 19 | value: node.value, 20 | source_location: source_location(node) 21 | } 22 | end 23 | 24 | defp source_location(%{loc: nil}), do: nil 25 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/absinthe/language/list_type.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.ListType do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct type: nil, 7 | loc: %{line: nil} 8 | 9 | @type t :: %__MODULE__{ 10 | type: Language.type_reference_t(), 11 | loc: Language.loc_t() 12 | } 13 | 14 | defimpl Blueprint.Draft do 15 | def convert(node, doc) do 16 | %Blueprint.TypeReference.List{ 17 | of_type: Absinthe.Blueprint.Draft.convert(node.type, doc) 18 | } 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/absinthe/language/list_value.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.ListValue do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct values: [], 7 | loc: nil 8 | 9 | @type t :: %__MODULE__{ 10 | values: [Language.value_t()], 11 | loc: Language.loc_t() 12 | } 13 | 14 | defimpl Blueprint.Draft do 15 | def convert(node, doc) do 16 | %Blueprint.Input.List{ 17 | items: 18 | node.values 19 | |> Enum.map(fn value -> 20 | %Blueprint.Input.RawValue{content: Blueprint.Draft.convert(value, doc)} 21 | end), 22 | source_location: source_location(node) 23 | } 24 | end 25 | 26 | defp source_location(%{loc: nil}), do: nil 27 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/absinthe/language/named_type.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.NamedType do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct name: nil, 7 | loc: %{line: nil} 8 | 9 | @type t :: %__MODULE__{ 10 | name: String.t(), 11 | loc: Language.loc_t() 12 | } 13 | 14 | defimpl Blueprint.Draft do 15 | def convert(node, _doc) do 16 | %Blueprint.TypeReference.Name{ 17 | name: node.name 18 | } 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/absinthe/language/non_null_type.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.NonNullType do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct type: nil, 7 | loc: %{line: nil} 8 | 9 | @type t :: %__MODULE__{ 10 | type: Language.type_reference_t(), 11 | loc: Language.t() 12 | } 13 | 14 | defimpl Blueprint.Draft do 15 | def convert(node, doc) do 16 | %Blueprint.TypeReference.NonNull{ 17 | of_type: Blueprint.Draft.convert(node.type, doc) 18 | } 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/absinthe/language/null_value.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.NullValue do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct [ 7 | :loc 8 | ] 9 | 10 | @type t :: %__MODULE__{ 11 | loc: Language.loc_t() 12 | } 13 | 14 | defimpl Blueprint.Draft do 15 | def convert(node, _doc) do 16 | %Blueprint.Input.Null{ 17 | source_location: source_location(node) 18 | } 19 | end 20 | 21 | defp source_location(%{loc: nil}), do: nil 22 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/absinthe/language/object_field.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.ObjectField do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct name: nil, 7 | value: nil, 8 | loc: %{line: nil} 9 | 10 | @type t :: %__MODULE__{ 11 | name: String.t(), 12 | value: Language.value_t(), 13 | loc: Language.loc_t() 14 | } 15 | 16 | defimpl Blueprint.Draft do 17 | def convert(node, doc) do 18 | %Blueprint.Input.Field{ 19 | name: node.name, 20 | input_value: %Blueprint.Input.RawValue{content: Blueprint.Draft.convert(node.value, doc)}, 21 | source_location: source_location(node) 22 | } 23 | end 24 | 25 | defp source_location(%{loc: nil}), do: nil 26 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/absinthe/language/object_value.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.ObjectValue do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct fields: [], 7 | loc: nil 8 | 9 | @type t :: %__MODULE__{ 10 | fields: [Language.ObjectField.t()], 11 | loc: Language.loc_t() 12 | } 13 | 14 | defimpl Blueprint.Draft do 15 | def convert(node, doc) do 16 | %Blueprint.Input.Object{ 17 | fields: Absinthe.Blueprint.Draft.convert(node.fields, doc), 18 | source_location: source_location(node) 19 | } 20 | end 21 | 22 | defp source_location(%{loc: nil}), do: nil 23 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/absinthe/language/scalar_type_definition.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.ScalarTypeDefinition do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct name: nil, 7 | description: nil, 8 | directives: [], 9 | loc: %{line: nil} 10 | 11 | @type t :: %__MODULE__{ 12 | name: String.t(), 13 | description: nil | String.t(), 14 | directives: [Language.Directive.t()], 15 | loc: Language.t() 16 | } 17 | 18 | defimpl Blueprint.Draft do 19 | def convert(node, doc) do 20 | %Blueprint.Schema.ScalarTypeDefinition{ 21 | name: node.name, 22 | description: node.description, 23 | identifier: Macro.underscore(node.name) |> String.to_atom(), 24 | directives: Absinthe.Blueprint.Draft.convert(node.directives, doc), 25 | source_location: source_location(node) 26 | } 27 | end 28 | 29 | defp source_location(%{loc: nil}), do: nil 30 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/absinthe/language/schema_declaration.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.SchemaDeclaration do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct description: nil, 7 | directives: [], 8 | fields: [], 9 | loc: %{line: nil} 10 | 11 | @type t :: %__MODULE__{ 12 | description: nil | String.t(), 13 | directives: [Language.Directive.t()], 14 | fields: [Language.FieldDefinition.t()], 15 | loc: Language.loc_t() 16 | } 17 | 18 | defimpl Blueprint.Draft do 19 | def convert(node, doc) do 20 | %Blueprint.Schema.SchemaDeclaration{ 21 | description: node.description, 22 | field_definitions: Absinthe.Blueprint.Draft.convert(node.fields, doc), 23 | directives: Absinthe.Blueprint.Draft.convert(node.directives, doc), 24 | source_location: source_location(node) 25 | } 26 | end 27 | 28 | defp source_location(%{loc: nil}), do: nil 29 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/absinthe/language/schema_definition.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.SchemaDefinition do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct description: nil, 7 | directives: [], 8 | fields: [], 9 | loc: %{line: nil} 10 | 11 | @type t :: %__MODULE__{ 12 | description: nil | String.t(), 13 | directives: [Language.Directive.t()], 14 | fields: [Language.FieldDefinition.t()], 15 | loc: Language.loc_t() 16 | } 17 | 18 | defimpl Blueprint.Draft do 19 | def convert(node, doc) do 20 | %Blueprint.Schema.SchemaDefinition{ 21 | description: node.description, 22 | type_definitions: Absinthe.Blueprint.Draft.convert(node.fields, doc), 23 | directives: Absinthe.Blueprint.Draft.convert(node.directives, doc), 24 | source_location: source_location(node) 25 | } 26 | end 27 | 28 | defp source_location(%{loc: nil}), do: nil 29 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/absinthe/language/selection_set.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.SelectionSet do 2 | @moduledoc false 3 | 4 | alias Absinthe.Language 5 | 6 | defstruct selections: [], 7 | loc: %{line: nil} 8 | 9 | @type t :: %__MODULE__{ 10 | selections: [ 11 | Language.FragmentSpread.t() | Language.InlineFragment.t() | Language.Field.t() 12 | ], 13 | loc: Language.loc_t() 14 | } 15 | end 16 | -------------------------------------------------------------------------------- /lib/absinthe/language/source.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.Source do 2 | @moduledoc false 3 | 4 | # A representation of source input to GraphQL, mostly useful for clients 5 | # who store GraphQL documents in source files; for example, if the GraphQL 6 | # input is in a file `Foo.graphql`, it might be useful for name to be 7 | # `"Foo.graphql"`. 8 | # 9 | # ## Examples 10 | # 11 | # @filename "Foo.graphql" 12 | # # ... 13 | # {:ok, data} = File.read(@filename) 14 | # %Absinthe.Language.Source{body: body, name: @filename} 15 | # |> Absinthe.run(App.Schema) 16 | defstruct body: "", 17 | name: "GraphQL" 18 | 19 | @type t :: %__MODULE__{ 20 | body: String.t(), 21 | name: String.t() 22 | } 23 | end 24 | -------------------------------------------------------------------------------- /lib/absinthe/language/string_value.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.StringValue do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct [ 7 | :value, 8 | :loc 9 | ] 10 | 11 | @type t :: %__MODULE__{ 12 | value: String.t(), 13 | loc: Language.loc_t() 14 | } 15 | 16 | defimpl Blueprint.Draft do 17 | def convert(node, _doc) do 18 | %Blueprint.Input.String{ 19 | value: node.value, 20 | source_location: source_location(node) 21 | } 22 | end 23 | 24 | defp source_location(%{loc: nil}), do: nil 25 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/absinthe/language/type_extension_definition.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.TypeExtensionDefinition do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Language, Blueprint} 5 | 6 | defstruct definition: nil, 7 | loc: %{line: nil} 8 | 9 | @type t :: %__MODULE__{ 10 | definition: Language.ObjectTypeDefinition.t(), 11 | loc: Language.loc_t() 12 | } 13 | 14 | defimpl Blueprint.Draft do 15 | def convert(node, doc) do 16 | %Absinthe.Blueprint.Schema.TypeExtensionDefinition{ 17 | definition: Blueprint.Draft.convert(node.definition, doc), 18 | source_location: source_location(node) 19 | } 20 | end 21 | 22 | defp source_location(%{loc: nil}), do: nil 23 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/absinthe/language/union_type_definition.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.UnionTypeDefinition do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct name: nil, 7 | description: nil, 8 | directives: [], 9 | types: [], 10 | loc: %{line: nil} 11 | 12 | @type t :: %__MODULE__{ 13 | name: String.t(), 14 | description: nil | String.t(), 15 | directives: [Language.Directive.t()], 16 | types: [Language.NamedType.t()], 17 | loc: Language.loc_t() 18 | } 19 | 20 | defimpl Blueprint.Draft do 21 | def convert(node, doc) do 22 | %Blueprint.Schema.UnionTypeDefinition{ 23 | name: node.name, 24 | description: node.description, 25 | identifier: Macro.underscore(node.name) |> String.to_atom(), 26 | types: Absinthe.Blueprint.Draft.convert(node.types, doc), 27 | directives: Absinthe.Blueprint.Draft.convert(node.directives, doc), 28 | source_location: source_location(node) 29 | } 30 | end 31 | 32 | defp source_location(%{loc: nil}), do: nil 33 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/absinthe/language/variable.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.Variable do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct name: nil, 7 | loc: %{line: nil} 8 | 9 | @type t :: %__MODULE__{ 10 | name: String.t(), 11 | loc: Language.loc_t() 12 | } 13 | 14 | defimpl Blueprint.Draft do 15 | def convert(node, _doc) do 16 | %Blueprint.Input.Variable{ 17 | name: node.name, 18 | source_location: source_location(node) 19 | } 20 | end 21 | 22 | defp source_location(%{loc: nil}), do: nil 23 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/absinthe/language/variable_definition.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.VariableDefinition do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | defstruct variable: nil, 7 | type: nil, 8 | directives: [], 9 | default_value: nil, 10 | loc: %{line: nil} 11 | 12 | @type t :: %__MODULE__{ 13 | variable: Language.Variable.t(), 14 | type: Language.type_reference_t(), 15 | default_value: any, 16 | loc: Language.loc_t() 17 | } 18 | 19 | defimpl Blueprint.Draft do 20 | def convert(node, doc) do 21 | %Blueprint.Document.VariableDefinition{ 22 | name: node.variable.name, 23 | type: Blueprint.Draft.convert(node.type, doc), 24 | directives: Absinthe.Blueprint.Draft.convert(node.directives, doc), 25 | default_value: Blueprint.Draft.convert(node.default_value, doc), 26 | source_location: source_location(node) 27 | } 28 | end 29 | 30 | defp source_location(%{loc: nil}), do: nil 31 | defp source_location(%{loc: loc}), do: Blueprint.SourceLocation.at(loc) 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/absinthe/middleware/map_get.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Middleware.MapGet do 2 | @moduledoc """ 3 | This is the default middleware. It assumes the the object it receives is a map 4 | and uses `Map.get/2` to get the value for this field. If this field is already 5 | marked as resolved, then this middleware does not touch it. 6 | 7 | If you want to replace this middleware you should use 8 | `Absinthe.Schema.replace_default/4` 9 | """ 10 | 11 | @behaviour Absinthe.Middleware 12 | 13 | def call(%{state: :unresolved, source: source} = res, key) do 14 | %{res | state: :resolved, value: Map.get(source, key)} 15 | end 16 | 17 | def call(res, _key), do: res 18 | end 19 | -------------------------------------------------------------------------------- /lib/absinthe/middleware/pass_parent.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Middleware.PassParent do 2 | @moduledoc """ 3 | Middleware that just passes the parent down to the children. 4 | 5 | This is the default resolver for subscription fields. 6 | """ 7 | 8 | @behaviour Absinthe.Middleware 9 | 10 | def call(%{source: parent} = res, _) do 11 | %{res | state: :resolved, value: parent} 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/absinthe/phase/blueprint.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Blueprint do 2 | use Absinthe.Phase 3 | 4 | @moduledoc false 5 | 6 | alias Absinthe.Blueprint 7 | 8 | @spec run(any, Keyword.t()) :: {:ok, Blueprint.t()} 9 | def run(blueprint, _options \\ []) do 10 | input = blueprint.input 11 | blueprint = Blueprint.Draft.convert(input, blueprint) 12 | 13 | {:ok, blueprint} 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/absinthe/phase/debug.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Debug do 2 | use Absinthe.Phase 3 | 4 | @moduledoc false 5 | 6 | alias Absinthe.Blueprint 7 | 8 | @spec run(any, Keyword.t()) :: {:ok, Blueprint.t()} 9 | def run(input, _options \\ []) do 10 | if System.get_env("DEBUG") do 11 | IO.inspect(input, label: :debug_blueprint_output) 12 | end 13 | 14 | {:ok, input} 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/absinthe/phase/document/arguments/coerce_lists.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Document.Arguments.CoerceLists do 2 | @moduledoc false 3 | 4 | # Coerce non-list inputs to lists when appropriate. 5 | # 6 | # IE 7 | # ``` 8 | # foo(ids: 1) 9 | # ``` 10 | # becomes 11 | # ``` 12 | # foo(ids: [1]) 13 | # ``` 14 | # 15 | # if `ids` is a list type. 16 | 17 | use Absinthe.Phase 18 | alias Absinthe.{Blueprint, Type} 19 | alias Absinthe.Blueprint.Input 20 | 21 | @spec run(Blueprint.t(), Keyword.t()) :: {:ok, Blueprint.t()} 22 | def run(input, _options \\ []) do 23 | node = Blueprint.prewalk(input, &coerce_node/1) 24 | {:ok, node} 25 | end 26 | 27 | defp coerce_node(%Input.Value{normalized: nil} = node), do: node 28 | 29 | defp coerce_node(%Input.Value{normalized: %Input.Null{}} = node) do 30 | node 31 | end 32 | 33 | defp coerce_node(%Input.Value{} = node) do 34 | case Type.unwrap_non_null(node.schema_node) do 35 | %Type.List{} -> 36 | %{node | normalized: Input.List.wrap(node.normalized, node.schema_node)} 37 | 38 | _ -> 39 | node 40 | end 41 | end 42 | 43 | defp coerce_node(node), do: node 44 | end 45 | -------------------------------------------------------------------------------- /lib/absinthe/phase/document/context.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Document.Context do 2 | @moduledoc "Pass on context and root value to document." 3 | 4 | use Absinthe.Phase 5 | alias Absinthe.Blueprint 6 | 7 | @spec run(Blueprint.t(), Keyword.t()) :: {:ok, Blueprint.t()} 8 | def run(blueprint, options \\ []) do 9 | context = Map.merge(blueprint.execution.context, options[:context] || %{}) 10 | blueprint = put_in(blueprint.execution.context, context) 11 | 12 | root_value = Map.merge(blueprint.execution.root_value, options[:root_value] || %{}) 13 | blueprint = put_in(blueprint.execution.root_value, root_value) 14 | 15 | {:ok, blueprint} 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/absinthe/phase/document/current_operation.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Document.CurrentOperation do 2 | @moduledoc false 3 | 4 | # Selects the current operation. 5 | # 6 | # - If an operation name is given, the matching operation is marked as current. 7 | # - If no operation name is provided and the there is only one operation, 8 | # it is set as current. 9 | # 10 | # Note that no validation occurs in this phase. 11 | 12 | use Absinthe.Phase 13 | alias Absinthe.Blueprint 14 | 15 | @spec run(Blueprint.t(), Keyword.t()) :: {:ok, Blueprint.t()} 16 | def run(input, options \\ []) do 17 | operations = process(input.operations, Map.new(options)) 18 | result = %{input | operations: operations} 19 | {:ok, result} 20 | end 21 | 22 | defp process([op], %{operation_name: nil}) do 23 | [%{op | current: true}] 24 | end 25 | 26 | defp process([%{name: name} = op], %{operation_name: name}) do 27 | [%{op | current: true}] 28 | end 29 | 30 | defp process(ops, %{operation_name: name}) do 31 | Enum.map(ops, fn 32 | %{name: ^name} = op -> 33 | %{op | current: true} 34 | 35 | op -> 36 | op 37 | end) 38 | end 39 | 40 | defp process(ops, _) do 41 | ops 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/absinthe/phase/document/directives.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Document.Directives do 2 | @moduledoc false 3 | 4 | # Expand all directives in the document. 5 | # 6 | # Note that no validation occurs in this phase. 7 | 8 | use Absinthe.Phase 9 | alias Absinthe.Blueprint 10 | 11 | @spec run(Blueprint.t(), Keyword.t()) :: {:ok, Blueprint.t()} 12 | def run(input, _options \\ []) do 13 | node = Blueprint.prewalk(input, &handle_node/1) 14 | {:ok, node} 15 | end 16 | 17 | @spec handle_node(Blueprint.node_t()) :: Blueprint.node_t() 18 | defp handle_node(%{directives: directives} = node) do 19 | Enum.reduce(directives, node, fn directive, acc -> 20 | Blueprint.Directive.expand(directive, acc) 21 | end) 22 | end 23 | 24 | defp handle_node(node) do 25 | node 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/absinthe/phase/document/override_root.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Document.OverrideRoot do 2 | @moduledoc false 3 | 4 | @behaviour Absinthe.Phase 5 | 6 | def run(blueprint, root_value: new_root) do 7 | {:ok, put_in(blueprint.execution.root_value, new_root)} 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/absinthe/phase/document/validation/only_one_subscription.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Document.Validation.OnlyOneSubscription do 2 | @moduledoc false 3 | 4 | # Validates document to ensure that the only variables that are used in a 5 | # document are defined on the operation. 6 | 7 | alias Absinthe.{Blueprint, Phase} 8 | 9 | use Absinthe.Phase 10 | 11 | @doc """ 12 | Run the validation. 13 | """ 14 | @spec run(Blueprint.t(), Keyword.t()) :: Phase.result_t() 15 | def run(input, _options \\ []) do 16 | bp = 17 | Blueprint.update_current(input, fn 18 | %{type: :subscription} = op -> 19 | check_op(op) 20 | 21 | op -> 22 | op 23 | end) 24 | 25 | {:ok, bp} 26 | end 27 | 28 | defp check_op(%{selections: [_, _ | _]} = op) do 29 | error = %Phase.Error{ 30 | phase: __MODULE__, 31 | message: "Only one field is permitted on the root object when subscribing", 32 | locations: [op.source_location] 33 | } 34 | 35 | op 36 | |> flag_invalid(:too_many_fields) 37 | |> put_error(error) 38 | end 39 | 40 | defp check_op(op), do: op 41 | end 42 | -------------------------------------------------------------------------------- /lib/absinthe/phase/document/validation/utils/message_suggestions.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Document.Validation.Utils.MessageSuggestions do 2 | @moduledoc false 3 | @suggest 5 4 | 5 | @doc """ 6 | Generate an suggestions message for a incorrect field 7 | """ 8 | def suggest_message(suggestions) do 9 | " Did you mean " <> to_quoted_or_list(suggestions |> Enum.take(@suggest)) <> "?" 10 | end 11 | 12 | def suggest_fragment_message(suggestions) do 13 | " Did you mean to use an inline fragment on " <> 14 | to_quoted_or_list(suggestions |> Enum.take(@suggest)) <> "?" 15 | end 16 | 17 | defp to_quoted_or_list([a]), do: ~s("#{a}") 18 | defp to_quoted_or_list([a, b]), do: ~s("#{a}" or "#{b}") 19 | defp to_quoted_or_list(other), do: to_longer_quoted_or_list(other) 20 | 21 | defp to_longer_quoted_or_list(list, acc \\ "") 22 | defp to_longer_quoted_or_list([word], acc), do: acc <> ~s(, or "#{word}") 23 | 24 | defp to_longer_quoted_or_list([word | rest], "") do 25 | rest 26 | |> to_longer_quoted_or_list(~s("#{word}")) 27 | end 28 | 29 | defp to_longer_quoted_or_list([word | rest], acc) do 30 | rest 31 | |> to_longer_quoted_or_list(acc <> ~s(, "#{word}")) 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/absinthe/phase/error.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Error do 2 | @moduledoc false 3 | 4 | @enforce_keys [:message, :phase] 5 | defstruct [ 6 | :message, 7 | :phase, 8 | locations: [], 9 | extra: %{}, 10 | path: [] 11 | ] 12 | 13 | @type loc_t :: %{optional(any) => any, line: pos_integer, column: pos_integer} 14 | 15 | @type t :: %__MODULE__{ 16 | message: String.t(), 17 | phase: module, 18 | locations: [loc_t], 19 | path: [], 20 | extra: map 21 | } 22 | end 23 | -------------------------------------------------------------------------------- /lib/absinthe/phase/init.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Init do 2 | @moduledoc false 3 | 4 | use Absinthe.Phase 5 | 6 | alias Absinthe.{Blueprint, Language, Phase} 7 | 8 | @spec run(String.t() | Language.Source.t() | Blueprint.t(), Keyword.t()) :: Phase.result_t() 9 | def run(input, _options \\ []) do 10 | {:record_phases, make_blueprint(input), 11 | fn bp, phases -> 12 | %{bp | initial_phases: phases} 13 | end} 14 | end 15 | 16 | defp make_blueprint(%Absinthe.Blueprint{} = blueprint) do 17 | blueprint 18 | end 19 | 20 | defp make_blueprint(input) do 21 | %Blueprint{input: input} 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/absinthe/phase/schema/arguments/normalize.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Schema.Arguments.Normalize do 2 | @moduledoc false 3 | 4 | # Populate all arguments in the document with their provided values: 5 | # 6 | # - If a literal value is provided for an argument, set the `Argument.t`'s 7 | # `normalized_value` field to that value. 8 | # 9 | # Note that no validation occurs in this phase. 10 | 11 | use Absinthe.Phase 12 | alias Absinthe.Blueprint 13 | alias Absinthe.Blueprint.Input 14 | 15 | @spec run(Blueprint.t(), Keyword.t()) :: {:ok, Blueprint.t()} 16 | def run(input, _options \\ []) do 17 | node = Blueprint.prewalk(input, &handle_node/1) 18 | {:ok, node} 19 | end 20 | 21 | # Set provided value from the raw value 22 | defp handle_node(%Input.RawValue{} = node) do 23 | %Input.Value{ 24 | normalized: node.content, 25 | raw: node 26 | } 27 | end 28 | 29 | defp handle_node(node) do 30 | node 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/absinthe/phase/schema/build.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Schema.Build do 2 | @moduledoc false 3 | 4 | def run(blueprint, _opts) do 5 | %{schema_definitions: [schema]} = blueprint 6 | 7 | types = build_types(blueprint) 8 | directive_artifacts = build_directives(blueprint) 9 | 10 | schema = %{ 11 | schema 12 | | type_artifacts: types, 13 | directive_artifacts: schema.directive_artifacts ++ directive_artifacts 14 | } 15 | 16 | blueprint = %{blueprint | schema_definitions: [schema]} 17 | 18 | {:ok, blueprint} 19 | end 20 | 21 | def build_types(%{schema_definitions: [schema]}) do 22 | for %module{} = type_def <- schema.type_definitions do 23 | type = module.build(type_def, schema) 24 | 25 | %{ 26 | type 27 | | __reference__: type_def.__reference__, 28 | __private__: type_def.__private__ 29 | } 30 | end 31 | end 32 | 33 | def build_directives(%{schema_definitions: [schema]}) do 34 | for %module{} = type_def <- schema.directive_definitions do 35 | type = module.build(type_def, schema) 36 | 37 | %{ 38 | type 39 | | definition: type_def.module, 40 | __reference__: type_def.__reference__, 41 | __private__: type_def.__private__ 42 | } 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/absinthe/phase/schema/directives.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Schema.Directives do 2 | @moduledoc false 3 | 4 | # Expand all directives in the document. 5 | # 6 | # Note that no validation occurs in this phase. 7 | 8 | use Absinthe.Phase 9 | alias Absinthe.Blueprint 10 | 11 | @spec run(Blueprint.t(), Keyword.t()) :: {:ok, Blueprint.t()} 12 | def run(input, _options \\ []) do 13 | node = Blueprint.prewalk(input, &handle_node/1) 14 | {:ok, node} 15 | end 16 | 17 | @spec handle_node(Blueprint.node_t()) :: Blueprint.node_t() 18 | defp handle_node(%{directives: directives} = node) do 19 | Enum.reduce(directives, node, fn directive, acc -> 20 | Blueprint.Directive.expand(directive, acc) 21 | end) 22 | end 23 | 24 | defp handle_node(node) do 25 | node 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/absinthe/phase/schema/import_prototype_directives.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Schema.ImportPrototypeDirectives do 2 | @moduledoc false 3 | 4 | # Imports directives from the prototype schema into the current schema. 5 | # This ensures the type system directives such as `deprecated` are available 6 | # for introspection as per the spec. 7 | # 8 | # Note that this does import the (type system) directives themselves, this 9 | # is already done in an earlier phase. 10 | 11 | @behaviour Absinthe.Phase 12 | alias Absinthe.Blueprint 13 | 14 | @spec run(Blueprint.t(), Keyword.t()) :: {:ok, Blueprint.t()} 15 | def run(blueprint, _options \\ []) do 16 | prototype_directives = Absinthe.Schema.directives(blueprint.prototype_schema) 17 | 18 | %{schema_definitions: [schema]} = blueprint 19 | 20 | schema = %{schema | directive_artifacts: prototype_directives} 21 | 22 | blueprint = %{blueprint | schema_definitions: [schema]} 23 | 24 | {:ok, blueprint} 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/absinthe/phase/schema/reformat_descriptions.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Schema.ReformatDescriptions do 2 | @moduledoc false 3 | 4 | # Trim all Descriptions 5 | 6 | use Absinthe.Phase 7 | alias Absinthe.Blueprint 8 | 9 | @spec run(Blueprint.t(), Keyword.t()) :: {:ok, Blueprint.t()} 10 | def run(input, _options \\ []) do 11 | node = Blueprint.prewalk(input, &handle_node/1) 12 | {:ok, node} 13 | end 14 | 15 | @spec handle_node(Blueprint.node_t()) :: Blueprint.node_t() 16 | defp handle_node(%{description: description} = node) 17 | when is_binary(description) do 18 | %{node | description: String.trim(description)} 19 | end 20 | 21 | defp handle_node(node) do 22 | node 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/absinthe/phase/schema/validation/result.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Schema.Validation.Result do 2 | @moduledoc false 3 | 4 | alias Absinthe.{Blueprint, Phase} 5 | 6 | use Absinthe.Phase 7 | 8 | @doc """ 9 | Run the validation. 10 | """ 11 | @spec run(Blueprint.t(), Keyword.t()) :: Phase.result_t() 12 | def run(input, _opts) do 13 | {input, errors} = Blueprint.prewalk(input, [], &handle_node/2) 14 | errors = errors |> :lists.reverse() |> Enum.uniq() 15 | 16 | case errors do 17 | [] -> 18 | {:ok, input} 19 | 20 | _ -> 21 | {:error, errors} 22 | end 23 | end 24 | 25 | # Collect the validation errors from nodes 26 | @spec handle_node(Blueprint.node_t(), [Phase.Error.t()]) :: 27 | {Blueprint.node_t(), [Phase.Error.t()]} 28 | defp handle_node(%{errors: errs} = node, errors) do 29 | {node, :lists.reverse(errs) ++ errors} 30 | end 31 | 32 | defp handle_node(%{raw: raw} = node, errors) do 33 | {_, errors} = Blueprint.prewalk(raw, errors, &handle_node/2) 34 | {node, errors} 35 | end 36 | 37 | defp handle_node(node, acc), do: {node, acc} 38 | end 39 | -------------------------------------------------------------------------------- /lib/absinthe/phase/subscription/result.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Subscription.Result do 2 | @moduledoc false 3 | 4 | # This runs instead of resolution and the normal result phase after a successful 5 | # subscription 6 | 7 | alias Absinthe.Blueprint 8 | 9 | @spec run(any, Keyword.t()) :: {:ok, Blueprint.t()} 10 | def run(blueprint, topic: topic) do 11 | result = %{"subscribed" => topic} 12 | {:ok, put_in(blueprint.result, result)} 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/absinthe/schema/compiled.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Schema.Compiled do 2 | @moduledoc false 3 | 4 | @behaviour Absinthe.Schema.Provider 5 | 6 | def pipeline(pipeline) do 7 | pipeline 8 | end 9 | 10 | def __absinthe_type__(schema_mod, name) do 11 | Module.concat([schema_mod, Compiled]).__absinthe_type__(name) 12 | end 13 | 14 | def __absinthe_directive__(schema_mod, name) do 15 | Module.concat([schema_mod, Compiled]).__absinthe_directive__(name) 16 | end 17 | 18 | def __absinthe_types__(schema_mod) do 19 | Module.concat([schema_mod, Compiled]).__absinthe_types__() 20 | end 21 | 22 | def __absinthe_types__(schema_mod, group) do 23 | Module.concat([schema_mod, Compiled]).__absinthe_types__(group) 24 | end 25 | 26 | def __absinthe_directives__(schema_mod) do 27 | Module.concat([schema_mod, Compiled]).__absinthe_directives__() 28 | end 29 | 30 | def __absinthe_interface_implementors__(schema_mod) do 31 | Module.concat([schema_mod, Compiled]).__absinthe_interface_implementors__() 32 | end 33 | 34 | def __absinthe_schema_declaration__(schema_mod) do 35 | Module.concat([schema_mod, Compiled]).__absinthe_schema_declaration__() 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/absinthe/schema/error.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Schema.Error do 2 | @moduledoc """ 3 | Exception raised when a schema is invalid 4 | """ 5 | defexception phase_errors: [] 6 | 7 | def message(error) do 8 | details = 9 | error.phase_errors 10 | |> Enum.map(fn %{message: message, locations: locations} -> 11 | locations = 12 | locations 13 | |> Enum.map(fn 14 | %{line: line, file: file} -> "#{file}:#{line}" 15 | %{column: column, line: line} -> "Column #{column}, Line #{line}" 16 | end) 17 | |> Enum.sort() 18 | |> Enum.join("\n") 19 | 20 | message = String.trim(message) 21 | 22 | """ 23 | --------------------------------------- 24 | ## Locations 25 | #{locations} 26 | 27 | #{message} 28 | """ 29 | end) 30 | |> Enum.join() 31 | 32 | """ 33 | Compilation failed: 34 | #{details} 35 | """ 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/absinthe/schema/hydrator.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Schema.Hydrator do 2 | @type hydration :: any 3 | 4 | @callback apply_hydration( 5 | node :: Absinthe.Blueprint.Schema.t(), 6 | hydration :: hydration 7 | ) :: Absinthe.Blueprint.Schema.t() 8 | end 9 | -------------------------------------------------------------------------------- /lib/absinthe/schema/manager.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Schema.Manager do 2 | use GenServer 3 | 4 | def start_link(schema) do 5 | GenServer.start_link(__MODULE__, schema, []) 6 | end 7 | 8 | def init(schema_module) do 9 | prototype_schema = schema_module.__absinthe_prototype_schema__() 10 | 11 | pipeline = 12 | schema_module 13 | |> Absinthe.Pipeline.for_schema(prototype_schema: prototype_schema) 14 | |> Absinthe.Schema.apply_modifiers(schema_module) 15 | 16 | schema_module.__absinthe_blueprint__() 17 | |> Absinthe.Pipeline.run(pipeline) 18 | |> case do 19 | {:ok, _, _} -> 20 | [] 21 | 22 | {:error, errors, _} -> 23 | raise Absinthe.Schema.Error, phase_errors: List.wrap(errors) 24 | end 25 | 26 | {:ok, schema_module, :hibernate} 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/absinthe/schema/notation/error.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Schema.Notation.Error do 2 | @moduledoc """ 3 | Exception raised when a schema is invalid 4 | """ 5 | defexception message: "Invalid notation schema" 6 | 7 | def exception(message) do 8 | %__MODULE__{message: message} 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/absinthe/schema/prototype.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Schema.Prototype do 2 | @moduledoc """ 3 | Provides the directives available for SDL schema definitions. 4 | 5 | By default, the only directive provided is `@deprecated`, which supports 6 | a `reason` argument (of GraphQL type `String`). This can be used to 7 | mark a field 8 | 9 | To add additional schema directives, define your own prototype schema, e.g.: 10 | 11 | ``` 12 | defmodule MyAppWeb.SchemaPrototype do 13 | use Absinthe.Schema.Prototype 14 | 15 | directive :feature do 16 | arg :name, non_null(:string) 17 | on [:interface] 18 | # Define `expand`, etc. 19 | end 20 | 21 | # More directives... 22 | end 23 | ``` 24 | 25 | Then, set it as the prototype for your schema: 26 | 27 | ``` 28 | defmodule MyAppWeb.Schema do 29 | use Absinthe.Schema 30 | 31 | @prototype_schema MyAppWeb.SchemaPrototype 32 | 33 | # Use `import_sdl`, etc... 34 | end 35 | ``` 36 | """ 37 | use __MODULE__.Notation 38 | 39 | defmacro __using__(opts \\ []) do 40 | __MODULE__.Notation.content(opts) 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/absinthe/schema/provider.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Schema.Provider do 2 | @moduledoc """ 3 | Experimental: Behaviour for providing schema data 4 | 5 | This behaviour is experimental and may change significantly in patch releases. 6 | """ 7 | 8 | @type schema_identifier :: term 9 | @type type_group :: :all | :referenced 10 | 11 | @callback pipeline(Absinthe.Pipeline.t()) :: Absinthe.Pipeline.t() 12 | 13 | @callback __absinthe_type__(schema_identifier, Absinthe.Type.identifier_t()) :: 14 | Absinthe.Type.custom_t() 15 | 16 | @callback __absinthe_directive__(schema_identifier, Absinthe.Type.identifier_t()) :: 17 | Absinthe.Type.custom_t() 18 | 19 | @callback __absinthe_types__(schema_identifier) :: [{atom, binary}] 20 | 21 | @callback __absinthe_types__(schema_identifier, type_group) :: [ 22 | {Absinthe.Type.identifier_t(), Absinthe.Type.identifier_t()} 23 | ] 24 | 25 | @callback __absinthe_directives__(schema_identifier) :: Absinthe.Type.Directive.t() 26 | 27 | @callback __absinthe_interface_implementors__(schema_identifier) :: term 28 | end 29 | -------------------------------------------------------------------------------- /lib/absinthe/subscription/proxy_supervisor.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Subscription.ProxySupervisor do 2 | @moduledoc false 3 | 4 | use Supervisor 5 | 6 | def start_link([pubsub, registry, pool_size, async]) do 7 | Supervisor.start_link(__MODULE__, {pubsub, registry, pool_size, async}) 8 | end 9 | 10 | def init({pubsub, registry, pool_size, async}) do 11 | task_super_name = Module.concat(registry, TaskSuper) 12 | task_super = {Task.Supervisor, name: task_super_name} 13 | 14 | # Shard numbers are generated by phash2 which is 0-based: 15 | proxies = 16 | for shard <- 0..(pool_size - 1) do 17 | {Absinthe.Subscription.Proxy, [task_super_name, pubsub, shard, async]} 18 | end 19 | 20 | Supervisor.init([task_super | proxies], strategy: :one_for_one) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/absinthe/test.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Test do 2 | @doc """ 3 | Run the introspection query on a schema. 4 | 5 | In your `test_helper.exs` file add 6 | ``` 7 | Absinthe.Test.prime(MyApp.Schema) 8 | ``` 9 | 10 | ## Explanation 11 | 12 | In the test environment mix loads code lazily, which means that it isn't until 13 | the first GraphQL query in your test suite runs that Absinthe's code base is 14 | actually loaded. Absinthe is a lot of code, and so this can take several 15 | milliseconds. This can be a problem for tests using message passing that expect 16 | messages to happen within a certain amount of time. 17 | 18 | By running the introspection query on your schema this function will cause mix 19 | to load the majority of the Absinthe code base. 20 | """ 21 | def prime(schema_name) do 22 | {:ok, %{data: _}} = Absinthe.Schema.introspect(schema_name) 23 | :ok 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/absinthe/type/built_ins/deprecated_directive_fields.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Type.BuiltIns.DeprecatedDirectiveFields do 2 | @moduledoc false 3 | # The spec of Oct 2015 has the onOperation, onFragment and onField 4 | # fields for directives (https://spec.graphql.org/October2015/#sec-Schema-Introspection) 5 | # See https://github.com/graphql/graphql-spec/pull/152 for the rationale. 6 | # These fields are deprecated and can be removed in the future. 7 | 8 | use Absinthe.Schema.Notation 9 | 10 | extend object(:__directive) do 11 | field :on_operation, :boolean do 12 | deprecate "Check `locations` field for enum value OPERATION" 13 | 14 | resolve fn _, %{source: source} -> 15 | {:ok, Enum.any?(source.locations, &Enum.member?([:query, :mutation, :subscription], &1))} 16 | end 17 | end 18 | 19 | field :on_fragment, :boolean do 20 | deprecate "Check `locations` field for enum value FRAGMENT_SPREAD" 21 | 22 | resolve fn _, %{source: source} -> 23 | {:ok, Enum.member?(source.locations, :fragment_spread)} 24 | end 25 | end 26 | 27 | field :on_field, :boolean do 28 | deprecate "Check `locations` field for enum value FIELD" 29 | 30 | resolve fn _, %{source: source} -> 31 | {:ok, Enum.member?(source.locations, :field)} 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/absinthe/type/built_ins/directives.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Type.BuiltIns.Directives do 2 | @moduledoc false 3 | use Absinthe.Schema.Notation 4 | 5 | alias Absinthe.Blueprint 6 | 7 | directive :include do 8 | description """ 9 | Directs the executor to include this field or fragment only when the `if` argument is true. 10 | """ 11 | 12 | arg :if, non_null(:boolean), description: "Included when true." 13 | 14 | on [:field, :fragment_spread, :inline_fragment] 15 | 16 | repeatable false 17 | 18 | expand fn 19 | %{if: true}, node -> 20 | Blueprint.put_flag(node, :include, __MODULE__) 21 | 22 | _, node -> 23 | Blueprint.put_flag(node, :skip, __MODULE__) 24 | end 25 | end 26 | 27 | directive :skip do 28 | description """ 29 | Directs the executor to skip this field or fragment when the `if` argument is true. 30 | """ 31 | 32 | repeatable false 33 | 34 | arg :if, non_null(:boolean), description: "Skipped when true." 35 | 36 | on [:field, :fragment_spread, :inline_fragment] 37 | 38 | expand fn 39 | %{if: true}, node -> 40 | Blueprint.put_flag(node, :skip, __MODULE__) 41 | 42 | _, node -> 43 | Blueprint.put_flag(node, :include, __MODULE__) 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/absinthe/type/custom/decimal.ex: -------------------------------------------------------------------------------- 1 | if Code.ensure_loaded?(Decimal) do 2 | defmodule Absinthe.Type.Custom.Decimal do 3 | @moduledoc false 4 | 5 | defdelegate serialize(value), to: Decimal, as: :to_string 6 | 7 | alias Absinthe.Blueprint.Input 8 | 9 | @dialyzer {:no_match, parse: 1} 10 | @spec parse(any) :: {:ok, Decimal.t()} | {:ok, nil} | :error 11 | def parse(%Input.String{value: value}) when is_binary(value) do 12 | case Decimal.parse(value) do 13 | {decimal, ""} -> {:ok, decimal} 14 | _ -> :error 15 | end 16 | end 17 | 18 | def parse(%Input.Float{value: value}) when is_float(value) do 19 | decimal = Decimal.from_float(value) 20 | if Decimal.nan?(decimal), do: :error, else: {:ok, decimal} 21 | end 22 | 23 | def parse(%Input.Integer{value: value}) when is_integer(value) do 24 | decimal = Decimal.new(value) 25 | if Decimal.nan?(decimal), do: :error, else: {:ok, decimal} 26 | end 27 | 28 | def parse(%Input.Null{}) do 29 | {:ok, nil} 30 | end 31 | 32 | def parse(_) do 33 | :error 34 | end 35 | end 36 | else 37 | defmodule Absinthe.Type.Custom.Decimal do 38 | @moduledoc false 39 | 40 | @spec parse(any) :: :error 41 | def parse(_), do: :error 42 | 43 | @spec serialize(any) :: nil 44 | def serialize(_), do: nil 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/absinthe/type/deprecation.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Type.Deprecation do 2 | @moduledoc false 3 | 4 | @type t :: %{reason: binary} 5 | defstruct reason: nil 6 | end 7 | -------------------------------------------------------------------------------- /lib/absinthe/type/list.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Type.List do 2 | @moduledoc """ 3 | A wrapping type which declares the type of each item in the list. 4 | 5 | ## Examples 6 | 7 | Given a type, `:item`, to declare the type of a field/argument as a list of 8 | `:item`-typed values, you could do: 9 | 10 | ``` 11 | type: %Absinthe.Type.List{of_type: :item} 12 | ``` 13 | 14 | But normally this would be done using `Absinthe.Schema.Notation.list_of/1`. 15 | 16 | ``` 17 | type: list_of(:item) 18 | ``` 19 | """ 20 | 21 | use Absinthe.Introspection.TypeKind, :list 22 | 23 | @typedoc " 24 | A defined list type. 25 | 26 | ## Options 27 | 28 | * `:of_type` - The underlying, wrapped type. 29 | " 30 | @type t :: %__MODULE__{of_type: Absinthe.Type.t()} 31 | defstruct of_type: nil 32 | end 33 | -------------------------------------------------------------------------------- /lib/absinthe/type/non_null.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Type.NonNull do 2 | @moduledoc """ 3 | A type that wraps an underlying type, acting identically to that type but 4 | adding a non-null constraint. 5 | 6 | By default, all types in GraphQL are nullable. To declare a type that 7 | disallows null, wrap it in a `Absinthe.Type.NonNull` struct. 8 | 9 | Adding non_null/1 to a type is a breaking change, removing it is not. Client documents that specify non null on a 10 | variable eg `query ($id: ID!)` are allowed to be passed to arguments which allow null. If the argument however is 11 | non_null, then the variable type MUST be non null as well. 12 | 13 | ## Examples 14 | 15 | Given a type, `:item`, to declare it as non-null, you could do the following: 16 | 17 | ``` 18 | type: %Absinthe.Type.NonNull{of_type: :item} 19 | ``` 20 | 21 | But normally this would be done using `Absinthe.Schema.Notation.non_null/1`. 22 | 23 | ``` 24 | type: non_null(:item) 25 | ``` 26 | """ 27 | 28 | use Absinthe.Introspection.TypeKind, :non_null 29 | 30 | @typedoc """ 31 | A defined non-null type. 32 | 33 | ## Options 34 | 35 | * `:of_type` -- the underlying type to wrap 36 | """ 37 | defstruct of_type: nil 38 | 39 | @type t :: %__MODULE__{of_type: Absinthe.Type.nullable_t()} 40 | @type t(x) :: %__MODULE__{of_type: x} 41 | end 42 | -------------------------------------------------------------------------------- /lib/absinthe/type/reference.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Type.Reference do 2 | @moduledoc false 3 | 4 | @typedoc false 5 | @type t :: %__MODULE__{module: atom, identifier: atom, name: binary} 6 | 7 | defstruct module: nil, identifier: nil, name: nil 8 | end 9 | -------------------------------------------------------------------------------- /lib/absinthe/utils/suggestion.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Utils.Suggestion do 2 | @jaro_threshold 0.68 3 | 4 | @doc """ 5 | Sort a list of suggestions by Jaro distance to a target string, 6 | supporting a cut-off threshold. 7 | """ 8 | @spec sort_list([String.t()], String.t(), float) :: [String.t()] 9 | def sort_list(suggestions, target, threshold \\ @jaro_threshold) 10 | 11 | def sort_list(suggestions, target, threshold) do 12 | Enum.map(suggestions, fn s -> {s, String.jaro_distance(s, target)} end) 13 | |> Enum.filter(fn {_, x} -> x >= threshold end) 14 | |> Enum.sort_by(fn {_, x} -> x end) 15 | |> Enum.map(fn {s, _} -> s end) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/absinthe-graphql/absinthe/a605cba55eed7a4aa35667c942e8406941365d48/logo.png -------------------------------------------------------------------------------- /test/absinthe/adapters/language_conventions_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Adapter.LanguageConventionsTest do 2 | use Absinthe.Case, async: true 3 | 4 | alias Absinthe.Adapter.LanguageConventions 5 | 6 | describe "to_internal_name/2" do 7 | test "converts external camelcase field names to underscore" do 8 | assert "foo_bar" = LanguageConventions.to_internal_name("fooBar", :field) 9 | end 10 | 11 | test "converts external camelcase variable names to underscore" do 12 | assert "foo_bar" = LanguageConventions.to_internal_name("fooBar", :variable) 13 | end 14 | 15 | test "converts external field names that do not match internal name" do 16 | assert "foo_bar" = LanguageConventions.to_internal_name("foo_bar", :field) 17 | assert "foo_bar" = LanguageConventions.to_internal_name("FooBar", :field) 18 | assert "foo_bar" = LanguageConventions.to_internal_name("FOO_BAR", :field) 19 | end 20 | end 21 | 22 | describe "to_external_name/2" do 23 | test "converts internal underscored field names to camelcase external field names" do 24 | assert "fooBar" = LanguageConventions.to_external_name("foo_bar", :field) 25 | end 26 | 27 | test "converts internal underscored variable names to camelcase external variable names" do 28 | assert "fooBar" = LanguageConventions.to_external_name("foo_bar", :variable) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /test/absinthe/formatter_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.FormatterTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | { 6 | version 7 | } 8 | """ 9 | test "formats a document" do 10 | assert Absinthe.Formatter.format(@query) == "{\n version\n}\n" 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/aliases/alias_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Aliases.AliasTest do 2 | use Absinthe.Case, async: true 3 | 4 | # LEAVE ME 5 | 6 | @query """ 7 | query { 8 | widget: thing(id: "foo") { 9 | name 10 | } 11 | } 12 | """ 13 | 14 | test "scenario #1" do 15 | assert {:ok, %{data: %{"widget" => %{"name" => "Foo"}}}} == 16 | Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/aliases/all_caps_alias_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Aliases.AllCapsAliasTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | thing(id: "foo") { 7 | FOO: name 8 | } 9 | } 10 | """ 11 | 12 | test "scenario #1" do 13 | assert {:ok, %{data: %{"thing" => %{"FOO" => "Foo"}}}} == 14 | Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/aliases/different_selection_sets_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Aliases.DifferentSelectionSetsTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | thing1: thing(id: "foo") { 7 | id 8 | } 9 | thing2: thing(id: "bar") { 10 | name 11 | } 12 | } 13 | """ 14 | 15 | test "scenario #1" do 16 | assert {:ok, %{data: %{"thing1" => %{"id" => "foo"}, "thing2" => %{"name" => "Bar"}}}} == 17 | Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/aliases/leading_underscore_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Aliases.LeadingUnderscoreTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | _thing123: thing(id: "foo") { 7 | name 8 | } 9 | } 10 | """ 11 | 12 | test "scenario #1" do 13 | assert {:ok, %{data: %{"_thing123" => %{"name" => "Foo"}}}} == 14 | Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/aliases/weird_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Aliases.WeirdTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | thing(id: "foo") { 7 | fOO_Bar_baz: name 8 | } 9 | } 10 | """ 11 | 12 | test "scenario #1" do 13 | assert {:ok, %{data: %{"thing" => %{"fOO_Bar_baz" => "Foo"}}}} == 14 | Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/context_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.ContextTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | thingByContext { 7 | name 8 | } 9 | } 10 | """ 11 | 12 | test "scenario #1" do 13 | assert {:ok, %{data: %{"thingByContext" => %{"name" => "Bar"}}}} == 14 | Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, context: %{thing: "bar"}) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/custom_types/basic_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.CustomTypes.BasicTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | customTypesQuery { datetime } 7 | } 8 | """ 9 | 10 | test "scenario #1" do 11 | assert {:ok, %{data: %{"customTypesQuery" => %{"datetime" => "2017-01-27T20:31:55Z"}}}} == 12 | Absinthe.run(@query, Absinthe.Fixtures.CustomTypesSchema, []) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/custom_types/datetime/input_object_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.CustomTypes.Datetime.InputObjectTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | mutation { 6 | customTypesMutation(args: { datetime: "2017-01-27T20:31:55Z" }) { 7 | message 8 | } 9 | } 10 | """ 11 | 12 | test "scenario #1" do 13 | assert {:ok, %{data: %{"customTypesMutation" => %{"message" => "ok"}}}} == 14 | Absinthe.run(@query, Absinthe.Fixtures.CustomTypesSchema, []) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/fragments/basic_root_type_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Fragments.BasicRootTypeTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | ... Fields 7 | } 8 | 9 | fragment Fields on RootQueryType { 10 | thing(id: "foo") { 11 | name 12 | } 13 | } 14 | """ 15 | 16 | test "scenario #1" do 17 | assert {:ok, %{data: %{"thing" => %{"name" => "Foo"}}}} == 18 | Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/fragments/basic_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Fragments.BasicTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query Q { 6 | person { 7 | ...NamedPerson 8 | } 9 | } 10 | fragment NamedPerson on Person { 11 | name 12 | } 13 | """ 14 | 15 | test "scenario #1" do 16 | assert {:ok, %{data: %{"person" => %{"name" => "Bruce"}}}} == 17 | Absinthe.run(@query, Absinthe.Fixtures.ContactSchema, []) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/fragments/introspection_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Fragments.IntrospectionTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query Q { 6 | __type(name: "ProfileInput") { 7 | name 8 | kind 9 | fields { 10 | name 11 | } 12 | ...Inputs 13 | } 14 | } 15 | 16 | fragment Inputs on __Type { 17 | inputFields { name } 18 | } 19 | """ 20 | 21 | test "scenario #1" do 22 | result = Absinthe.run(@query, Absinthe.Fixtures.ContactSchema, []) 23 | 24 | assert {:ok, 25 | %{ 26 | data: %{ 27 | "__type" => %{ 28 | "name" => "ProfileInput", 29 | "kind" => "INPUT_OBJECT", 30 | "fields" => nil, 31 | "inputFields" => input_fields 32 | } 33 | } 34 | }} = result 35 | 36 | correct = [%{"name" => "code"}, %{"name" => "name"}, %{"name" => "age"}] 37 | sort = & &1["name"] 38 | assert Enum.sort_by(input_fields, sort) == Enum.sort_by(correct, sort) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/enum/literal_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Enum.LiteralTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | red: info(channel: RED) { 7 | name 8 | value 9 | } 10 | green: info(channel: GREEN) { 11 | name 12 | value 13 | } 14 | blue: info(channel: BLUE) { 15 | name 16 | value 17 | } 18 | puce: info(channel: PUCE) { 19 | name 20 | value 21 | } 22 | } 23 | """ 24 | 25 | test "scenario #1" do 26 | assert {:ok, 27 | %{ 28 | data: %{ 29 | "blue" => %{"name" => "BLUE", "value" => 300}, 30 | "green" => %{"name" => "GREEN", "value" => 200}, 31 | "puce" => %{"name" => "PUCE", "value" => -100}, 32 | "red" => %{"name" => "RED", "value" => 100} 33 | } 34 | }} == Absinthe.run(@query, Absinthe.Fixtures.ColorSchema, []) 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/id/literal_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Id.LiteralTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | { 6 | item(id: "foo") { 7 | id 8 | name 9 | } 10 | } 11 | """ 12 | 13 | test "scenario #1" do 14 | assert {:ok, %{data: %{"item" => %{"id" => "foo", "name" => "Foo"}}}} == 15 | Absinthe.run(@query, Absinthe.Fixtures.IdTestSchema, []) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/literal_to_element_of_type_list_of_T_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.LiteralToElementOfTypeListOfTTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | nullableList(input: [null, 1]) { 7 | length 8 | content 9 | nonNullCount 10 | nullCount 11 | } 12 | } 13 | """ 14 | 15 | test "scenario #1" do 16 | assert {:ok, 17 | %{ 18 | data: %{ 19 | "nullableList" => %{ 20 | "content" => [nil, 1], 21 | "length" => 2, 22 | "nonNullCount" => 1, 23 | "nullCount" => 1 24 | } 25 | } 26 | }} == Absinthe.run(@query, Absinthe.Fixtures.NullListsSchema, []) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/literal_to_element_of_type_non_null_list_of_T_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.LiteralToElementOfTypeNonNullListOfTTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | nonNullableList(input: [null, 1]) { 7 | length 8 | content 9 | nonNullCount 10 | nullCount 11 | } 12 | } 13 | """ 14 | 15 | test "scenario #1" do 16 | assert {:ok, 17 | %{ 18 | data: %{ 19 | "nonNullableList" => %{ 20 | "content" => [nil, 1], 21 | "length" => 2, 22 | "nonNullCount" => 1, 23 | "nullCount" => 1 24 | } 25 | } 26 | }} == Absinthe.run(@query, Absinthe.Fixtures.NullListsSchema, []) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/literal_to_type_T_overrides_default_value_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.LiteralToTypeTOverridesDefaultValueTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | times: objTimes(input: {base: 4, multiplier: null}) 7 | } 8 | """ 9 | 10 | test "scenario #1" do 11 | assert {:ok, %{data: %{"times" => 4}}} == 12 | Absinthe.run(@query, Absinthe.Fixtures.ObjectTimesSchema, []) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/literal_to_type_list_of_T_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.LiteralToTypeListOfTTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | nullableList(input: null) { 7 | length 8 | content 9 | nonNullCount 10 | nullCount 11 | } 12 | } 13 | """ 14 | 15 | test "scenario #1" do 16 | assert {:ok, %{data: %{"nullableList" => nil}}} == 17 | Absinthe.run(@query, Absinthe.Fixtures.NullListsSchema, []) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/literal_to_type_non_null_T_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.LiteralToTypeNonNullTTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | times: objTimes(input: {base: null}) 7 | } 8 | """ 9 | 10 | test "scenario #1" do 11 | assert {:ok, 12 | %{ 13 | errors: [ 14 | %{ 15 | message: 16 | "Argument \"input\" has invalid value {base: null}.\nIn field \"base\": Expected type \"Int!\", found null.", 17 | locations: [%{column: 19, line: 2}] 18 | } 19 | ] 20 | }} == Absinthe.run(@query, Absinthe.Fixtures.ObjectTimesSchema, []) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/literal_to_type_non_null_list_of_T_element_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.LiteralToTypeNonNullListOfTElementTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | { 6 | nullableListOfNonNullableType(input: [null, 1]) { 7 | length 8 | content 9 | nonNullCount 10 | nullCount 11 | } 12 | } 13 | """ 14 | 15 | test "scenario #1" do 16 | assert {:ok, 17 | %{ 18 | errors: [ 19 | %{ 20 | message: 21 | "Argument \"input\" has invalid value [null, 1].\nIn element #1: Expected type \"Int!\", found null.", 22 | locations: [%{column: 33, line: 2}] 23 | } 24 | ] 25 | }} == Absinthe.run(@query, Absinthe.Fixtures.NullListsSchema, []) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/literal_to_type_non_null_list_of_T_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.LiteralToTypeNonNullListOfTTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | nullableListOfNonNullableType(input: null) { 7 | length 8 | content 9 | nonNullCount 10 | nullCount 11 | } 12 | } 13 | """ 14 | 15 | test "scenario #1" do 16 | assert {:ok, %{data: %{"nullableListOfNonNullableType" => nil}}} == 17 | Absinthe.run(@query, Absinthe.Fixtures.NullListsSchema, []) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/literal_to_type_non_null_list_of_non_null_T_element_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.LiteralToTypeNonNullListOfNonNullTElementTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | nonNullableListOfNonNullableType(input: [null, 1]) { 7 | length 8 | content 9 | nonNullCount 10 | nullCount 11 | } 12 | } 13 | """ 14 | 15 | test "scenario #1" do 16 | assert {:ok, 17 | %{ 18 | errors: [ 19 | %{ 20 | message: 21 | "Argument \"input\" has invalid value [null, 1].\nIn element #1: Expected type \"Int!\", found null.", 22 | locations: [%{column: 36, line: 2}] 23 | } 24 | ] 25 | }} == Absinthe.run(@query, Absinthe.Fixtures.NullListsSchema, []) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/literal_to_type_non_null_list_of_non_null_T_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.LiteralToTypeNonNullListOfNonNullTTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | nonNullableListOfNonNullableType(input: null) { 7 | length 8 | content 9 | nonNullCount 10 | nullCount 11 | } 12 | } 13 | """ 14 | 15 | test "scenario #1" do 16 | assert {:ok, 17 | %{ 18 | errors: [ 19 | %{ 20 | message: "Argument \"input\" has invalid value null.", 21 | locations: [%{column: 36, line: 2}] 22 | } 23 | ] 24 | }} == Absinthe.run(@query, Absinthe.Fixtures.NullListsSchema, []) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/variable_to_type_T_overrides_default_value_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.VariableToTypeTOverridesDefaultValueTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query ($multiplier: Int) { 6 | times: objTimes(input: {base: 4, multiplier: $multiplier}) 7 | } 8 | """ 9 | 10 | test "scenario #1" do 11 | assert {:ok, %{data: %{"times" => 4}}} == 12 | Absinthe.run( 13 | @query, 14 | Absinthe.Fixtures.ObjectTimesSchema, 15 | variables: %{"multiplier" => nil} 16 | ) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/variable_to_type_T_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.VariableToTypeTTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query ($value: Int) { 6 | times: objTimes(input: {base: 4, multiplier: $value}) 7 | } 8 | """ 9 | 10 | test "scenario #1" do 11 | assert {:ok, %{data: %{"times" => 4}}} == 12 | Absinthe.run( 13 | @query, 14 | Absinthe.Fixtures.ObjectTimesSchema, 15 | variables: %{"value" => nil} 16 | ) 17 | end 18 | 19 | test "scenario #2" do 20 | assert {:ok, %{data: %{"times" => 32}}} == 21 | Absinthe.run(@query, Absinthe.Fixtures.ObjectTimesSchema, variables: %{"value" => 8}) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/variable_to_type_list_of_T_element_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.VariableToTypeListOfTElementTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query ($value: [Int]) { 6 | nullableList(input: $value) { 7 | length 8 | content 9 | nonNullCount 10 | nullCount 11 | } 12 | } 13 | """ 14 | 15 | test "scenario #1" do 16 | assert {:ok, 17 | %{ 18 | data: %{ 19 | "nullableList" => %{ 20 | "content" => [nil, 1], 21 | "length" => 2, 22 | "nonNullCount" => 1, 23 | "nullCount" => 1 24 | } 25 | } 26 | }} == 27 | Absinthe.run( 28 | @query, 29 | Absinthe.Fixtures.NullListsSchema, 30 | variables: %{"value" => [nil, 1]} 31 | ) 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/variable_to_type_list_of_T_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.VariableToTypeListOfTTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query ($value: [Int]) { 6 | nullableList(input: $value) { 7 | length 8 | content 9 | nonNullCount 10 | nullCount 11 | } 12 | } 13 | """ 14 | 15 | test "scenario #1" do 16 | assert {:ok, %{data: %{"nullableList" => nil}}} == 17 | Absinthe.run(@query, Absinthe.Fixtures.NullListsSchema, variables: %{"value" => nil}) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/variable_to_type_non_null_T_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.VariableToTypeNonNullTTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query ($value: Int!) { times: objTimes(input: {base: $value}) } 6 | """ 7 | 8 | test "scenario #1" do 9 | assert {:ok, 10 | %{ 11 | errors: [ 12 | %{ 13 | message: 14 | "Argument \"input\" has invalid value {base: $value}.\nIn field \"base\": Expected type \"Int!\", found $value.", 15 | locations: [%{column: 40, line: 1}] 16 | }, 17 | %{ 18 | message: "Variable \"value\": Expected non-null, found null.", 19 | locations: [%{column: 8, line: 1}] 20 | } 21 | ] 22 | }} == 23 | Absinthe.run( 24 | @query, 25 | Absinthe.Fixtures.ObjectTimesSchema, 26 | variables: %{"value" => nil} 27 | ) 28 | end 29 | 30 | test "scenario #2" do 31 | assert {:ok, %{data: %{"times" => 16}}} == 32 | Absinthe.run(@query, Absinthe.Fixtures.ObjectTimesSchema, variables: %{"value" => 8}) 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/variable_to_type_non_null_list_of_T_element_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.VariableToTypeNonNullListOfTElementTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query ($value: [Int!]) { 6 | nullableListOfNonNullableType(input: $value) { 7 | length 8 | content 9 | nonNullCount 10 | nullCount 11 | } 12 | } 13 | """ 14 | 15 | test "scenario #1" do 16 | assert {:ok, 17 | %{ 18 | errors: [ 19 | %{ 20 | message: 21 | "Argument \"input\" has invalid value $value.\nIn element #1: Expected type \"Int!\", found null.", 22 | locations: [%{column: 33, line: 2}] 23 | } 24 | ] 25 | }} == 26 | Absinthe.run( 27 | @query, 28 | Absinthe.Fixtures.NullListsSchema, 29 | variables: %{"value" => [nil, 1]} 30 | ) 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/variable_to_type_non_null_list_of_T_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.VariableToTypeNonNullListOfTTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query ($value: [Int!]) { 6 | nullableListOfNonNullableType(input: $value) { 7 | length 8 | content 9 | nonNullCount 10 | nullCount 11 | } 12 | } 13 | """ 14 | 15 | test "scenario #1" do 16 | assert {:ok, %{data: %{"nullableListOfNonNullableType" => nil}}} == 17 | Absinthe.run(@query, Absinthe.Fixtures.NullListsSchema, variables: %{"value" => nil}) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/variable_to_type_non_null_list_of_non_null_T_element_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.VariableToTypeNonNullListOfNonNullTElementTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query ($value: [Int!]!) { 6 | nonNullableListOfNonNullableType(input: $value) { 7 | length 8 | content 9 | nonNullCount 10 | nullCount 11 | } 12 | } 13 | """ 14 | 15 | test "scenario #1" do 16 | assert {:ok, 17 | %{ 18 | errors: [ 19 | %{ 20 | message: 21 | "Argument \"input\" has invalid value $value.\nIn element #1: Expected type \"Int!\", found null.", 22 | locations: [%{column: 36, line: 2}] 23 | } 24 | ] 25 | }} == 26 | Absinthe.run( 27 | @query, 28 | Absinthe.Fixtures.NullListsSchema, 29 | variables: %{"value" => [nil, 1]} 30 | ) 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/variable_to_type_non_null_list_of_non_null_T_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.VariableToTypeNonNullListOfNonNullTTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query ($value: [Int!]!) { 6 | nonNullableListOfNonNullableType(input: $value) { 7 | length 8 | content 9 | nonNullCount 10 | nullCount 11 | } 12 | } 13 | """ 14 | 15 | test "scenario #1" do 16 | assert {:ok, 17 | %{ 18 | errors: [ 19 | %{ 20 | message: "Argument \"input\" has invalid value $value.", 21 | locations: [%{column: 36, line: 2}] 22 | }, 23 | %{ 24 | message: "Variable \"value\": Expected non-null, found null.", 25 | locations: [%{column: 8, line: 1}] 26 | } 27 | ] 28 | }} == 29 | Absinthe.run(@query, Absinthe.Fixtures.NullListsSchema, variables: %{"value" => nil}) 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/variable_to_variable_type_non_null_T_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.VariableToVariableTypeNonNullTTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query ($mult: Int!) { 6 | times(base: 4, multiplier: $mult) 7 | } 8 | """ 9 | 10 | test "scenario #1" do 11 | assert {:ok, 12 | %{ 13 | errors: [ 14 | %{ 15 | message: "Variable \"mult\": Expected non-null, found null.", 16 | locations: [%{column: 8, line: 1}] 17 | } 18 | ] 19 | }} == Absinthe.run(@query, Absinthe.Fixtures.TimesSchema, variables: %{"mult" => nil}) 20 | end 21 | 22 | test "scenario #2" do 23 | assert {:ok, 24 | %{ 25 | errors: [ 26 | %{ 27 | message: "Variable \"mult\": Expected non-null, found null.", 28 | locations: [%{column: 8, line: 1}] 29 | } 30 | ] 31 | }} == Absinthe.run(@query, Absinthe.Fixtures.TimesSchema, []) 32 | end 33 | 34 | test "scenario #3" do 35 | assert {:ok, %{data: %{"times" => 8}}} == 36 | Absinthe.run(@query, Absinthe.Fixtures.TimesSchema, variables: %{"mult" => 2}) 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/input_types/null/variable_to_variable_with_default_value_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.InputTypes.Null.VariableToVariableWithDefaultValueTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query ($mult: Int = 6) { 6 | times(base: 4, multiplier: $mult) 7 | } 8 | """ 9 | 10 | test "scenario #1" do 11 | assert {:ok, %{data: %{"times" => 24}}} == 12 | Absinthe.run(@query, Absinthe.Fixtures.TimesSchema, []) 13 | end 14 | 15 | test "scenario #2" do 16 | assert {:ok, %{data: %{"times" => 4}}} == 17 | Absinthe.run(@query, Absinthe.Fixtures.TimesSchema, variables: %{"mult" => nil}) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/introspection/default_value_enum_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Introspection.DefaultValueEnumTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | __type(name: "ChannelInput") { 7 | name 8 | inputFields { 9 | name 10 | defaultValue 11 | } 12 | } 13 | } 14 | """ 15 | 16 | test "scenario #1" do 17 | assert {:ok, 18 | %{ 19 | data: %{ 20 | "__type" => %{ 21 | "inputFields" => [%{"defaultValue" => "RED", "name" => "channel"}], 22 | "name" => "ChannelInput" 23 | } 24 | } 25 | }} == Absinthe.run(@query, Absinthe.Fixtures.ColorSchema, []) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/introspection/full_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Introspection.FullTest do 2 | use Absinthe.Case, async: true 3 | 4 | test "scenario #1" do 5 | result = Absinthe.Schema.introspect(Absinthe.Fixtures.ContactSchema) 6 | {:ok, %{data: %{"__schema" => schema}}} = result 7 | 8 | assert schema["queryType"] 9 | assert schema["mutationType"] 10 | assert schema["subscriptionType"] 11 | assert schema["types"] 12 | assert schema["directives"] 13 | end 14 | 15 | defmodule MiddlewareSchema do 16 | use Absinthe.Schema 17 | 18 | query do 19 | field :foo, :string 20 | end 21 | 22 | def middleware(middleware, %{identifier: :foo}, _) do 23 | middleware 24 | end 25 | 26 | def middleware(_, _, _) do 27 | raise "this should not be called when introspecting" 28 | end 29 | end 30 | 31 | test "middleware callback does not apply to introspection fields" do 32 | assert Absinthe.Schema.introspect(MiddlewareSchema) 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/introspection/interface_typename_alias_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Introspection.InterfaceTypenameAliasTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { contact { entity { kind: __typename name } } } 6 | """ 7 | 8 | test "scenario #1" do 9 | assert {:ok, %{data: %{"contact" => %{"entity" => %{"kind" => "Person", "name" => "Bruce"}}}}} == 10 | Absinthe.run(@query, Absinthe.Fixtures.ContactSchema, []) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/introspection/interface_typename_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Introspection.InterfaceTypenameTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { contact { entity { __typename name } } } 6 | """ 7 | 8 | test "scenario #1" do 9 | assert {:ok, 10 | %{data: %{"contact" => %{"entity" => %{"__typename" => "Person", "name" => "Bruce"}}}}} == 11 | Absinthe.run(@query, Absinthe.Fixtures.ContactSchema, []) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/introspection/mutation_type_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Introspection.MutationTypeTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { __schema { mutationType { name kind } } } 6 | """ 7 | 8 | test "scenario #1" do 9 | assert {:ok, 10 | %{ 11 | data: %{ 12 | "__schema" => %{ 13 | "mutationType" => %{"kind" => "OBJECT", "name" => "RootMutationType"} 14 | } 15 | } 16 | }} == Absinthe.run(@query, Absinthe.Fixtures.ContactSchema, []) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/introspection/object_typename_alias_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Introspection.ObjectTypenameAliasTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | person { 7 | kind: __typename 8 | name 9 | } 10 | } 11 | """ 12 | 13 | test "scenario #1" do 14 | assert {:ok, %{data: %{"person" => %{"kind" => "Person", "name" => "Bruce"}}}} == 15 | Absinthe.run(@query, Absinthe.Fixtures.ContactSchema, []) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/introspection/object_typename_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Introspection.ObjectTypenameTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | person { 7 | __typename 8 | name 9 | } 10 | } 11 | """ 12 | 13 | test "scenario #1" do 14 | assert {:ok, %{data: %{"person" => %{"__typename" => "Person", "name" => "Bruce"}}}} == 15 | Absinthe.run(@query, Absinthe.Fixtures.ContactSchema, []) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/introspection/query_type_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Introspection.QueryTypeTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { __schema { queryType { name kind } } } 6 | """ 7 | 8 | test "scenario #1" do 9 | assert {:ok, 10 | %{ 11 | data: %{ 12 | "__schema" => %{"queryType" => %{"kind" => "OBJECT", "name" => "RootQueryType"}} 13 | } 14 | }} == Absinthe.run(@query, Absinthe.Fixtures.ContactSchema, []) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/introspection/schema_types_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Introspection.SchemaTypesTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { __schema { types { name } } } 6 | """ 7 | 8 | @expected [ 9 | "__Directive", 10 | "__DirectiveLocation", 11 | "__EnumValue", 12 | "__Field", 13 | "__InputValue", 14 | "__Schema", 15 | "__Type", 16 | "__TypeKind", 17 | "Boolean", 18 | "Business", 19 | "Contact", 20 | "Int", 21 | "RootMutationType", 22 | "NamedEntity", 23 | "Person", 24 | "ProfileInput", 25 | "RootQueryType", 26 | "SearchResult", 27 | "String", 28 | "RootSubscriptionType" 29 | ] 30 | 31 | test "scenario #1" do 32 | result = Absinthe.run(@query, Absinthe.Fixtures.ContactSchema, []) 33 | assert {:ok, %{data: %{"__schema" => %{"types" => types}}}} = result 34 | names = types |> Enum.map(& &1["name"]) 35 | 36 | assert @expected == names 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/introspection/subscription_type_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Introspection.SubscriptionTypeTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { __schema { subscriptionType { name kind } } } 6 | """ 7 | 8 | test "scenario #1" do 9 | assert {:ok, 10 | %{ 11 | data: %{ 12 | "__schema" => %{ 13 | "subscriptionType" => %{"kind" => "OBJECT", "name" => "RootSubscriptionType"} 14 | } 15 | } 16 | }} == Absinthe.run(@query, Absinthe.Fixtures.ContactSchema, []) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/introspection/type_interface_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Introspection.TypeInterfaceTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | __type(name: "NamedEntity") { 7 | kind 8 | name 9 | description 10 | possibleTypes { 11 | name 12 | } 13 | } 14 | } 15 | """ 16 | 17 | test "scenario #1" do 18 | assert {:ok, 19 | %{ 20 | data: %{ 21 | "__type" => %{ 22 | "description" => "A named entity", 23 | "kind" => "INTERFACE", 24 | "name" => "NamedEntity", 25 | "possibleTypes" => [%{"name" => "Business"}, %{"name" => "Person"}] 26 | } 27 | } 28 | }} == Absinthe.run(@query, Absinthe.Fixtures.ContactSchema, []) 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/introspection/type_kind_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Introspection.TypeKindTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | __type(name: "__TypeKind") { 7 | name 8 | enumValues { 9 | name 10 | } 11 | } 12 | } 13 | """ 14 | 15 | # https://spec.graphql.org/draft/#sel-HAJbLA6GABABKwzN 16 | # 17 | # enum __TypeKind { 18 | # SCALAR 19 | # OBJECT 20 | # INTERFACE 21 | # UNION 22 | # ENUM 23 | # INPUT_OBJECT 24 | # LIST 25 | # NON_NULL 26 | # } 27 | 28 | @expected [ 29 | "SCALAR", 30 | "OBJECT", 31 | "INTERFACE", 32 | "UNION", 33 | "ENUM", 34 | "INPUT_OBJECT", 35 | "LIST", 36 | "NON_NULL" 37 | ] 38 | 39 | test "Contains expected values" do 40 | {:ok, 41 | %{ 42 | data: %{ 43 | "__type" => %{ 44 | "name" => "__TypeKind", 45 | "enumValues" => enum_values 46 | } 47 | } 48 | }} = Absinthe.run(@query, Absinthe.Fixtures.ContactSchema, []) 49 | 50 | type_kind_values = enum_values |> Enum.map(& &1["name"]) 51 | 52 | assert Enum.sort(type_kind_values) == Enum.sort(@expected) 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/introspection/union_typename_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Introspection.UnionTypenameTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { firstSearchResult { __typename } } 6 | """ 7 | 8 | test "scenario #1" do 9 | assert {:ok, %{data: %{"firstSearchResult" => %{"__typename" => "Person"}}}} == 10 | Absinthe.run(@query, Absinthe.Fixtures.ContactSchema, []) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/introspection/union_wrapped_typename_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Introspection.UnionWrappedTypenameTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { searchResults { __typename } } 6 | """ 7 | 8 | test "scenario #1" do 9 | assert {:ok, 10 | %{ 11 | data: %{ 12 | "searchResults" => [%{"__typename" => "Person"}, %{"__typename" => "Business"}] 13 | } 14 | }} == Absinthe.run(@query, Absinthe.Fixtures.ContactSchema, []) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/nested_objects_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.NestedObjectsTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | thing(id: "foo") { 7 | name 8 | otherThing { 9 | name 10 | } 11 | } 12 | } 13 | """ 14 | 15 | test "scenario #1" do 16 | assert {:ok, %{data: %{"thing" => %{"name" => "Foo", "otherThing" => %{"name" => "Bar"}}}}} == 17 | Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/resolution/errors_include_path_indices_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Resolution.ErrorsIncludePathIndicesTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | things { 7 | id 8 | fail(id: "foo") 9 | } 10 | } 11 | """ 12 | 13 | test "scenario #1" do 14 | assert {:ok, 15 | %{ 16 | data: %{ 17 | "things" => [%{"fail" => "bar", "id" => "bar"}, %{"fail" => nil, "id" => "foo"}] 18 | }, 19 | errors: [ 20 | %{ 21 | message: "fail", 22 | path: ["things", 1, "fail"], 23 | locations: [%{column: 5, line: 4}] 24 | } 25 | ] 26 | }} == Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/resolution/exceptions/bad_match_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Resolution.Exceptions.BadMatchTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | badResolution { 7 | name 8 | } 9 | } 10 | """ 11 | 12 | test "scenario #1" do 13 | assert_raise(Absinthe.ExecutionError, fn -> 14 | Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 15 | end) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/resolution/exceptions/missing_error_message_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Resolution.Exceptions.MissingErrorMessageTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | mutation { failingThing(type: WITHOUT_MESSAGE) { name } } 6 | """ 7 | 8 | test "scenario #1" do 9 | assert_raise(Absinthe.ExecutionError, fn -> 10 | Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 11 | end) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/resolution/exceptions/missing_error_message_when_returning_multiple_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Resolution.Exceptions.MissingErrorMessageWhenReturningMultipleTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | mutation { failingThing(type: MULTIPLE_WITHOUT_MESSAGE) { name } } 6 | """ 7 | 8 | test "scenario #1" do 9 | assert_raise(Absinthe.ExecutionError, fn -> 10 | Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 11 | end) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/resolution/multiple_errors_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Resolution.MultipleErrorsTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | mutation { failingThing(type: MULTIPLE) { name } } 6 | """ 7 | 8 | test "scenario #1" do 9 | assert {:ok, 10 | %{ 11 | data: %{"failingThing" => nil}, 12 | errors: [ 13 | %{message: "one", path: ["failingThing"], locations: [%{column: 12, line: 1}]}, 14 | %{message: "two", path: ["failingThing"], locations: [%{column: 12, line: 1}]} 15 | ] 16 | }} == Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/resolution/multiple_errors_with_extra_fields_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Resolution.MultipleErrorsWithExtraFieldsTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | mutation { failingThing(type: MULTIPLE_WITH_CODE) { name } } 6 | """ 7 | 8 | test "scenario #1" do 9 | assert {:ok, 10 | %{ 11 | data: %{"failingThing" => nil}, 12 | errors: [ 13 | %{ 14 | code: 1, 15 | message: "Custom Error 1", 16 | path: ["failingThing"], 17 | locations: [%{column: 12, line: 1}] 18 | }, 19 | %{ 20 | code: 2, 21 | message: "Custom Error 2", 22 | path: ["failingThing"], 23 | locations: [%{column: 12, line: 1}] 24 | } 25 | ] 26 | }} == Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/root_value_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.RootValueTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { version } 6 | """ 7 | 8 | test "scenario #1" do 9 | assert {:ok, %{data: %{"version" => "0.0.1"}}} == 10 | Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, 11 | root_value: %{version: "0.0.1"} 12 | ) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/simple_query_returning_list_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.SimpleQueryReturningListTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | things { 7 | id 8 | name 9 | } 10 | } 11 | """ 12 | 13 | test "scenario #1" do 14 | assert {:ok, 15 | %{ 16 | data: %{ 17 | "things" => [%{"id" => "bar", "name" => "Bar"}, %{"id" => "foo", "name" => "Foo"}] 18 | } 19 | }} == Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/simple_query_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.SimpleQueryTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { thing(id: "foo") { name } } 6 | """ 7 | 8 | test "scenario #1" do 9 | assert {:ok, %{data: %{"thing" => %{"name" => "Foo"}}}} == 10 | Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/variables/basic_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Variables.BasicTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query ($thingId: String!) { 6 | thing(id: $thingId) { 7 | name 8 | } 9 | } 10 | """ 11 | 12 | test "scenario #1" do 13 | for schema <- schema_implementations(Absinthe.Fixtures.Things) do 14 | assert {:ok, %{data: %{"thing" => %{"name" => "Bar"}}}} == 15 | Absinthe.run( 16 | @query, 17 | schema, 18 | variables: %{"thingId" => "bar"} 19 | ) 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/absinthe/integration/execution/variables/default_value_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Execution.Variables.DefaultValueTest do 2 | use Absinthe.Case, async: true 3 | 4 | @times_query """ 5 | query ($mult: Int = 6) { 6 | times(base: 4, multiplier: $mult) 7 | } 8 | """ 9 | 10 | @default_value_query """ 11 | query { 12 | microsecond 13 | } 14 | """ 15 | 16 | test "query field arg default_value and resolve execution" do 17 | assert {:ok, %{data: %{"times" => 24}}} == 18 | Absinthe.run(@times_query, Absinthe.Fixtures.TimesSchema, []) 19 | end 20 | 21 | test "query field default is evaluated only once" do 22 | {:ok, %{data: %{"microsecond" => first_current_microsecond}}} = 23 | Absinthe.run(@default_value_query, Absinthe.Fixtures.DefaultValueSchema, []) 24 | 25 | Process.sleep(5) 26 | 27 | {:ok, %{data: %{"microsecond" => second_current_microsecond}}} = 28 | Absinthe.run(@default_value_query, Absinthe.Fixtures.DefaultValueSchema, []) 29 | 30 | # If the code to grab the default_value was executed twice, this would be different 31 | assert first_current_microsecond == second_current_microsecond 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /test/absinthe/integration/parsing/basic_error_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Parsing.BasicErrorTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | { 6 | thing(id: "foo") {}{ name } 7 | } 8 | """ 9 | 10 | test "scenario #1" do 11 | assert {:ok, 12 | %{ 13 | errors: [ 14 | %{message: "syntax error before: '}'", locations: [%{column: 21, line: 2}]} 15 | ] 16 | }} == Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/absinthe/integration/validation/error_result_when_bad_list_argument_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Validation.ErrorResultWhenBadListArgumentTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | thing(id: ["foo"]) { 7 | name 8 | } 9 | } 10 | """ 11 | 12 | test "scenario #1" do 13 | assert {:ok, 14 | %{ 15 | errors: [ 16 | %{ 17 | message: "Argument \"id\" has invalid value [\"foo\"].", 18 | locations: [%{column: 9, line: 2}] 19 | } 20 | ] 21 | }} == Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/absinthe/integration/validation/extra_arguments_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Validation.ExtraArgumentsTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | thing(id: "foo", extra: "dunno") { 7 | name 8 | } 9 | } 10 | """ 11 | 12 | test "scenario #1" do 13 | assert {:ok, 14 | %{ 15 | errors: [ 16 | %{ 17 | message: 18 | "Unknown argument \"extra\" on field \"thing\" of type \"RootQueryType\".", 19 | locations: [%{column: 20, line: 2}] 20 | } 21 | ] 22 | }} == Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /test/absinthe/integration/validation/introspection_fields_ignored_in_input_objects_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Validation.IntrospectionFieldsIgnoredInInputObjectsTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | mutation ($input: InputThing!) { 6 | thing: updateThing(id: "foo", thing: $input) { 7 | name 8 | value 9 | } 10 | } 11 | """ 12 | 13 | test "scenario #1" do 14 | assert {:ok, 15 | %{ 16 | errors: [ 17 | %{ 18 | message: 19 | "Argument \"thing\" has invalid value $input.\nIn field \"__typename\": Unknown field.", 20 | locations: [%{column: 33, line: 2}] 21 | } 22 | ] 23 | }} == 24 | Absinthe.run( 25 | @query, 26 | Absinthe.Fixtures.Things.MacroSchema, 27 | variables: %{"input" => %{"__typename" => "foo", "value" => 100}} 28 | ) 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /test/absinthe/integration/validation/invalid_argument_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Validation.InvalidArgumentTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { number(val: "AAA") } 6 | """ 7 | 8 | test "scenario #1" do 9 | assert {:ok, 10 | %{ 11 | errors: [ 12 | %{ 13 | message: "Argument \"val\" has invalid value \"AAA\".", 14 | locations: [%{column: 16, line: 1}] 15 | } 16 | ] 17 | }} == Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/absinthe/integration/validation/invalid_nested_type_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Validation.InvalidNestedTypeTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | mutation UpdateThingValueBadly { 6 | thing: updateThing(id: "foo", thing: {value: "BAD"}) { 7 | name 8 | value 9 | } 10 | } 11 | """ 12 | 13 | test "scenario #1" do 14 | assert {:ok, 15 | %{ 16 | errors: [ 17 | %{ 18 | message: 19 | "Argument \"thing\" has invalid value {value: \"BAD\"}.\nIn field \"value\": Expected type \"Int\", found \"BAD\".", 20 | locations: [%{column: 33, line: 2}] 21 | } 22 | ] 23 | }} == Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /test/absinthe/integration/validation/missing_operation_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Validation.MissingOperationTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | mutation { foo } 6 | """ 7 | 8 | test "scenario #1" do 9 | assert {:ok, 10 | %{ 11 | errors: [ 12 | %{ 13 | message: "Operation \"mutation\" not supported", 14 | locations: [%{column: 1, line: 1}] 15 | } 16 | ] 17 | }} == Absinthe.run(@query, Absinthe.Fixtures.OnlyQuerySchema, []) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/absinthe/integration/validation/missing_selection_set_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Validation.MissingSelectionSetTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | things 7 | } 8 | """ 9 | 10 | test "scenario #1" do 11 | assert {:ok, 12 | %{ 13 | errors: [ 14 | %{ 15 | message: 16 | "Field \"things\" of type \"[Thing]\" must have a selection of subfields. Did you mean \"things { ... }\"?", 17 | locations: [%{column: 3, line: 2}] 18 | } 19 | ] 20 | }} == Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/absinthe/integration/validation/object_spreads_in_object_scope_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Validation.ObjectSpreadsInObjectScopeTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query Q { 6 | person { 7 | name 8 | ...NamedBusiness 9 | } 10 | } 11 | fragment NamedBusiness on Business { 12 | employee_count 13 | } 14 | """ 15 | 16 | test "scenario #1" do 17 | assert {:ok, 18 | %{ 19 | errors: [ 20 | %{ 21 | message: 22 | "Fragment spread has no type overlap with parent.\nParent possible types: [\"Person\"]\nSpread possible types: [\"Business\"]\n", 23 | locations: [%{column: 5, line: 4}] 24 | } 25 | ] 26 | }} == Absinthe.run(@query, Absinthe.Fixtures.ContactSchema, []) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/absinthe/integration/validation/required_arguments_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Validation.RequiredArgumentsTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { thing { name } } 6 | """ 7 | 8 | test "scenario #1" do 9 | assert {:ok, 10 | %{ 11 | errors: [ 12 | %{ 13 | message: "In argument \"id\": Expected type \"String!\", found null.", 14 | locations: [%{column: 9, line: 1}] 15 | } 16 | ] 17 | }} == Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/absinthe/integration/validation/unknown_arg_for_list_member_field_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Validation.UnknownArgForListMemberFieldTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query { 6 | things { 7 | id(x: 1) 8 | name 9 | } 10 | } 11 | """ 12 | 13 | test "scenario #1" do 14 | assert {:ok, 15 | %{ 16 | errors: [ 17 | %{ 18 | message: "Unknown argument \"x\" on field \"id\" of type \"Thing\".", 19 | locations: [%{column: 8, line: 3}] 20 | } 21 | ] 22 | }} == Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /test/absinthe/integration/validation/unknown_field_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Validation.UnknownFieldTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | { 6 | thing(id: "foo") { 7 | name 8 | bad 9 | } 10 | } 11 | """ 12 | 13 | test "scenario #1" do 14 | assert {:ok, 15 | %{ 16 | errors: [ 17 | %{ 18 | message: "Cannot query field \"bad\" on type \"Thing\".", 19 | locations: [%{column: 5, line: 4}] 20 | } 21 | ] 22 | }} == Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /test/absinthe/integration/validation/variables/unused/with_operation_name_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Validation.Variables.Unused.WithOperationNameTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query AnOperationName($test: String) { 6 | thing(id: "foo") { 7 | name 8 | } 9 | } 10 | """ 11 | 12 | test "scenario #1" do 13 | assert {:ok, 14 | %{ 15 | errors: [ 16 | %{ 17 | message: "Variable \"test\" is never used in operation \"AnOperationName\".", 18 | locations: [%{column: 23, line: 1}, %{column: 1, line: 1}] 19 | } 20 | ] 21 | }} == Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/absinthe/integration/validation/variables/unused/without_operation_name_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Elixir.Absinthe.Integration.Validation.Variables.Unused.WithoutOperationNameTest do 2 | use Absinthe.Case, async: true 3 | 4 | @query """ 5 | query ($test: String) { 6 | thing(id: "foo") { 7 | name 8 | } 9 | } 10 | """ 11 | 12 | test "scenario #1" do 13 | assert {:ok, 14 | %{ 15 | errors: [ 16 | %{message: "Variable \"test\" is never used.", locations: [%{column: 8, line: 1}]} 17 | ] 18 | }} == Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/absinthe/language/fragment_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.FragmentTest do 2 | use Absinthe.Case, async: true 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | @query """ 7 | fragment FooFields on Foo { 8 | foo 9 | bar 10 | } 11 | """ 12 | 13 | describe "converting to Blueprint" do 14 | test "builds a Document.Fragment.Named.t" do 15 | assert %Blueprint.Document.Fragment.Named{ 16 | name: "FooFields", 17 | type_condition: %Blueprint.TypeReference.Name{name: "Foo"}, 18 | selections: [ 19 | %Blueprint.Document.Field{name: "foo"}, 20 | %Blueprint.Document.Field{name: "bar"} 21 | ] 22 | } = from_input(@query) 23 | end 24 | end 25 | 26 | defp from_input(text) do 27 | {:ok, %{input: doc}} = Absinthe.Phase.Parse.run(text) 28 | 29 | doc 30 | |> extract_ast_node 31 | |> Blueprint.Draft.convert(doc) 32 | end 33 | 34 | defp extract_ast_node(%Language.Document{definitions: nodes}) do 35 | nodes 36 | |> List.first() 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /test/absinthe/language/inline_fragment_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.InlineFragmentTest do 2 | use Absinthe.Case, async: true 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | @query """ 7 | { 8 | ... on RootQueryType { 9 | foo 10 | bar 11 | } 12 | } 13 | """ 14 | 15 | describe "converting to Blueprint" do 16 | test "builds a Document.Fragment.Inline.t" do 17 | assert %Blueprint.Document.Fragment.Inline{ 18 | type_condition: %Blueprint.TypeReference.Name{name: "RootQueryType"}, 19 | selections: [ 20 | %Blueprint.Document.Field{name: "foo"}, 21 | %Blueprint.Document.Field{name: "bar"} 22 | ] 23 | } = from_input(@query) 24 | end 25 | end 26 | 27 | defp from_input(text) do 28 | {:ok, %{input: doc}} = Absinthe.Phase.Parse.run(text) 29 | 30 | doc 31 | |> extract_ast_node 32 | |> Blueprint.Draft.convert(doc) 33 | end 34 | 35 | defp extract_ast_node(%Language.Document{definitions: nodes}) do 36 | op = 37 | nodes 38 | |> List.first() 39 | 40 | op.selection_set.selections 41 | |> List.first() 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /test/absinthe/language/input_object_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.ObjectValueTest do 2 | use Absinthe.Case, async: true 3 | 4 | alias Absinthe.Blueprint 5 | 6 | @query """ 7 | { 8 | foo(input: {foo: 2}) { 9 | baz 10 | } 11 | } 12 | """ 13 | 14 | describe "converting to Blueprint" do 15 | test "builds an Input.Object.t" do 16 | assert %Blueprint.Input.Object{ 17 | fields: [ 18 | %Blueprint.Input.Field{ 19 | name: "foo", 20 | input_value: %Blueprint.Input.RawValue{ 21 | content: %Blueprint.Input.Integer{value: 2} 22 | } 23 | } 24 | ] 25 | } = from_input(@query) 26 | end 27 | end 28 | 29 | defp from_input(text) do 30 | {:ok, %{input: doc}} = Absinthe.Phase.Parse.run(text) 31 | 32 | doc 33 | |> extract_ast_node 34 | |> Blueprint.Draft.convert(doc) 35 | end 36 | 37 | defp extract_ast_node(%Absinthe.Language.Document{definitions: [node]}) do 38 | node.selection_set.selections 39 | |> List.first() 40 | |> Map.get(:arguments) 41 | |> List.first() 42 | |> Map.get(:value) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /test/absinthe/language/interface_type_definition_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.InterfaceTypeDefinitionTest do 2 | use Absinthe.Case, async: true 3 | 4 | alias Absinthe.Blueprint 5 | 6 | @text "An Entity" 7 | @idl """ 8 | interface Entity 9 | @description(text: "#{@text}") 10 | { 11 | name: String! 12 | } 13 | type Person implements Entity { 14 | name: String! 15 | } 16 | type Business implements Entity { 17 | name: String! 18 | } 19 | """ 20 | 21 | describe "converting to Blueprint" do 22 | test "works, given a Blueprint Schema 'interface' definition" do 23 | assert %Blueprint.Schema.InterfaceTypeDefinition{ 24 | name: "Entity", 25 | directives: [%{name: "description"}] 26 | } = from_input(@idl) 27 | end 28 | end 29 | 30 | defp from_input(text) do 31 | {:ok, %{input: doc}} = Absinthe.Phase.Parse.run(text) 32 | 33 | doc 34 | |> extract_ast_node 35 | |> Blueprint.Draft.convert(doc) 36 | end 37 | 38 | defp extract_ast_node(%Absinthe.Language.Document{definitions: definitions}) do 39 | definitions |> List.first() 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /test/absinthe/language/scalar_type_definition_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.ScalarTypeDefinitionTest do 2 | use Absinthe.Case, async: true 3 | 4 | alias Absinthe.Blueprint 5 | 6 | describe "converting to Blueprint" do 7 | test "works, given a Blueprint Schema 'scalar' definition" do 8 | assert %Blueprint.Schema.ScalarTypeDefinition{name: "Time"} = from_input("scalar Time") 9 | end 10 | 11 | test "works, given a Blueprint Schema 'scalar' definition with a directive" do 12 | rep = 13 | """ 14 | scalar Time @description(text: "A datetime with a timezone") 15 | """ 16 | |> from_input 17 | 18 | assert %Blueprint.Schema.ScalarTypeDefinition{ 19 | name: "Time", 20 | directives: [%{name: "description"}] 21 | } = rep 22 | end 23 | end 24 | 25 | defp from_input(text) do 26 | {:ok, %{input: doc}} = Absinthe.Phase.Parse.run(text) 27 | 28 | doc 29 | |> extract_ast_node 30 | |> Blueprint.Draft.convert(doc) 31 | end 32 | 33 | defp extract_ast_node(%Absinthe.Language.Document{definitions: [node]}) do 34 | node 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /test/absinthe/language/union_type_definition_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.UnionTypeDefinitionTest do 2 | use Absinthe.Case, async: true 3 | 4 | alias Absinthe.Blueprint 5 | 6 | @text "A metasyntactic variable" 7 | @idl """ 8 | type Foo { 9 | name: String 10 | } 11 | 12 | type Bar { 13 | name: String 14 | } 15 | 16 | union Baz @description(text: "#{@text}") = 17 | Foo 18 | | Bar 19 | 20 | """ 21 | 22 | describe "converting to Blueprint" do 23 | test "works, given a Blueprint Schema 'union' definition" do 24 | assert %Blueprint.Schema.UnionTypeDefinition{ 25 | name: "Baz", 26 | types: [ 27 | %Blueprint.TypeReference.Name{name: "Foo"}, 28 | %Blueprint.TypeReference.Name{name: "Bar"} 29 | ], 30 | directives: [%{name: "description"}] 31 | } = from_input(@idl) 32 | end 33 | end 34 | 35 | defp from_input(text) do 36 | {:ok, %{input: doc}} = Absinthe.Phase.Parse.run(text) 37 | 38 | doc 39 | |> extract_ast_node 40 | |> Blueprint.Draft.convert(doc) 41 | end 42 | 43 | defp extract_ast_node(%Absinthe.Language.Document{definitions: definitions}) do 44 | definitions |> List.last() 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /test/absinthe/language/variable_definition_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.VariableDefinitionTest do 2 | use Absinthe.Case, async: true 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | @query """ 7 | query Foo($showFoo: Boolean = true @bar(a: 1)) { 8 | foo @include(if: $showFoo) 9 | } 10 | """ 11 | 12 | describe "converting to Blueprint" do 13 | test "builds a VariableDefinition.t" do 14 | assert %Blueprint.Document.VariableDefinition{ 15 | name: "showFoo", 16 | directives: [%Blueprint.Directive{name: "bar"}], 17 | type: %Blueprint.TypeReference.Name{name: "Boolean"}, 18 | default_value: %Blueprint.Input.Boolean{value: true}, 19 | source_location: %Blueprint.SourceLocation{line: 1} 20 | } = from_input(@query) 21 | end 22 | end 23 | 24 | defp from_input(text) do 25 | {:ok, %{input: doc}} = Absinthe.Phase.Parse.run(text) 26 | 27 | doc 28 | |> extract_ast_node 29 | |> Blueprint.Draft.convert(doc) 30 | end 31 | 32 | defp extract_ast_node(%Language.Document{definitions: [node]}) do 33 | node.variable_definitions 34 | |> List.first() 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /test/absinthe/language/variable_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Language.VariableTest do 2 | use Absinthe.Case, async: true 3 | 4 | alias Absinthe.{Blueprint, Language} 5 | 6 | @query """ 7 | query Foo($input: InputObjectSettingFoo = {foo: 2}) { 8 | foo(input: $input) { 9 | baz 10 | } 11 | } 12 | """ 13 | 14 | describe "converting to Blueprint" do 15 | test "builds an Input.Variable.t" do 16 | assert %Blueprint.Input.Variable{name: "input"} = from_input(@query) 17 | end 18 | end 19 | 20 | defp from_input(text) do 21 | {:ok, %{input: doc}} = Absinthe.Phase.Parse.run(text) 22 | 23 | doc 24 | |> extract_ast_node 25 | |> Blueprint.Draft.convert(doc) 26 | end 27 | 28 | defp extract_ast_node(%Language.Document{definitions: [node]}) do 29 | node.selection_set.selections 30 | |> List.first() 31 | |> Map.get(:arguments) 32 | |> List.first() 33 | |> Map.get(:value) 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /test/absinthe/phase/document/validation/provided_an_operation_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Document.Validation.ProvidedAnOperationTest do 2 | @phase Absinthe.Phase.Document.Validation.ProvidedAnOperation 3 | 4 | use Absinthe.ValidationPhaseCase, 5 | phase: @phase, 6 | async: true 7 | 8 | alias Absinthe.Blueprint 9 | 10 | defp no_operation do 11 | bad_value( 12 | Blueprint, 13 | @phase.error_message, 14 | nil 15 | ) 16 | end 17 | 18 | describe "Given an operation" do 19 | test "passes" do 20 | assert_passes_validation( 21 | """ 22 | query Bar { 23 | name 24 | } 25 | """, 26 | [] 27 | ) 28 | end 29 | end 30 | 31 | describe "When empty" do 32 | test "fails" do 33 | assert_fails_validation( 34 | "", 35 | [], 36 | no_operation() 37 | ) 38 | end 39 | end 40 | 41 | describe "When given fragments" do 42 | test "fails" do 43 | assert_fails_validation( 44 | """ 45 | fragment Foo on QueryRootType { 46 | name 47 | } 48 | """, 49 | [], 50 | no_operation() 51 | ) 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /test/absinthe/phase/document/validation/repeatable_directives_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Document.Validation.RepeatableDirectivesTest do 2 | @phase Absinthe.Phase.Document.Validation.RepeatableDirectives 3 | 4 | use Absinthe.ValidationPhaseCase, 5 | phase: @phase, 6 | async: true 7 | 8 | alias Absinthe.Blueprint 9 | 10 | defp duplicate(name, line) do 11 | bad_value( 12 | Blueprint.Directive, 13 | "Directive `#{name}' cannot be applied repeatedly.", 14 | line, 15 | name: name 16 | ) 17 | end 18 | 19 | test "with disallowed repeated directives" do 20 | assert_fails_validation( 21 | """ 22 | query Foo { 23 | skippedField @skip(if: true) @skip(if: true) 24 | } 25 | """, 26 | [], 27 | duplicate("skip", 2) 28 | ) 29 | end 30 | 31 | test "with allowed repeated directives" do 32 | assert_passes_validation( 33 | """ 34 | query Foo { 35 | skippedField @onField @onField 36 | } 37 | 38 | mutation Bar @onMutation { 39 | someField 40 | } 41 | """, 42 | [] 43 | ) 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /test/absinthe/phase/document/validation/unique_variable_names_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Document.Validation.UniqueVariableNamesTest do 2 | @phase Absinthe.Phase.Document.Validation.UniqueVariableNames 3 | 4 | use Absinthe.ValidationPhaseCase, 5 | phase: @phase, 6 | async: true 7 | 8 | alias Absinthe.Blueprint 9 | 10 | defp duplicate_variable(name, line) do 11 | bad_value( 12 | Blueprint.Document.VariableDefinition, 13 | @phase.error_message(name), 14 | line, 15 | name: name 16 | ) 17 | end 18 | 19 | describe "Validate: Unique variable names" do 20 | test "unique variable names" do 21 | assert_passes_validation( 22 | """ 23 | query A($x: Int, $y: String) { __typename } 24 | query B($x: String, $y: Int) { __typename } 25 | """, 26 | [] 27 | ) 28 | end 29 | 30 | test "duplicate variable names" do 31 | assert_fails_validation( 32 | """ 33 | query A($x: Int, $x: Int, $x: String) { __typename } 34 | query B($x: String, $x: Int) { __typename } 35 | query C($x: Int, $x: Int) { __typename } 36 | """, 37 | [], 38 | [ 39 | duplicate_variable("x", 1), 40 | duplicate_variable("x", 2), 41 | duplicate_variable("x", 3) 42 | ] 43 | ) 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /test/absinthe/phase/parse/language_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.Parse.LanguageTest do 2 | use Absinthe.Case, async: true 3 | 4 | @moduletag :parser 5 | 6 | test "parses kitchen-sink.graphql" do 7 | filename = Path.join(__DIR__, "../../../support/fixtures/language/kitchen-sink.graphql") 8 | input = File.read!(filename) 9 | assert {:ok, _} = run(input) 10 | end 11 | 12 | test "parses schema-kitchen-sink.graphql" do 13 | filename = 14 | Path.join(__DIR__, "../../../support/fixtures/language/schema-kitchen-sink.graphql") 15 | 16 | input = File.read!(filename) 17 | assert {:ok, _} = run(input) 18 | end 19 | 20 | test "parses schema-with-emojis.graphql" do 21 | filename = Path.join(__DIR__, "../../../support/fixtures/language/schema-with-emojis.graphql") 22 | 23 | input = File.read!(filename) 24 | assert {:ok, _} = run(input) 25 | end 26 | 27 | def run(input) do 28 | with {:ok, %{input: input}} <- Absinthe.Phase.Parse.run(input) do 29 | {:ok, input} 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/absinthe/phase/schema_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Phase.SchemaTest do 2 | use Absinthe.Case, async: true 3 | 4 | defmodule IntegerInputSchema do 5 | use Absinthe.Schema 6 | 7 | query do 8 | field :test, :string do 9 | arg :integer, :integer 10 | 11 | resolve fn _, _, _ -> 12 | {:ok, "ayup"} 13 | end 14 | end 15 | end 16 | end 17 | 18 | describe "when given [Int] for Int schema node" do 19 | @query """ 20 | { test(integer: [1]) } 21 | """ 22 | 23 | test "doesn't raise an exception" do 24 | assert {:ok, _} = run(@query) 25 | end 26 | end 27 | 28 | def run(query) do 29 | pipeline = 30 | IntegerInputSchema 31 | |> Absinthe.Pipeline.for_document([]) 32 | |> Absinthe.Pipeline.before(Absinthe.Phase.Schema) 33 | 34 | with {:ok, bp, _} <- Absinthe.Pipeline.run(query, pipeline) do 35 | Absinthe.Phase.Schema.run(bp, schema: IntegerInputSchema) 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /test/absinthe/resolution/projector_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Resolution.ProjectorTest do 2 | use Absinthe.Case, async: true 3 | 4 | # describe "merging" do 5 | # test "asdf" 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/absinthe/schema/hydrate_builtins_test.exs: -------------------------------------------------------------------------------- 1 | defmodule HydrateBuiltinsTest do 2 | use ExUnit.Case, async: true 3 | 4 | defmodule Schema do 5 | use Absinthe.Schema 6 | 7 | query do 8 | field :value, :float do 9 | # intentionally a float 10 | resolve fn _, _, _ -> {:ok, 1} end 11 | end 12 | end 13 | 14 | def hydrate(%Absinthe.Blueprint.Schema.ScalarTypeDefinition{identifier: :float}, _) do 15 | {:serialize, &__MODULE__.serialize_float/1} 16 | end 17 | 18 | def hydrate(_, _) do 19 | [] 20 | end 21 | 22 | def serialize_float(number) when is_number(number) do 23 | number * 1.0 24 | end 25 | end 26 | 27 | test "we can override the builtin scalars" do 28 | assert {:ok, %{data: %{"value" => 1.0}}} == Absinthe.run("{ value }", Schema) 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /test/absinthe/schema/hydrate_dynamic_values_test.exs: -------------------------------------------------------------------------------- 1 | defmodule HydrateDynamicValuesTest do 2 | use ExUnit.Case, async: true 3 | 4 | defmodule Schema do 5 | use Absinthe.Schema 6 | 7 | enum :color do 8 | value :red 9 | value :blue 10 | value :green 11 | end 12 | 13 | query do 14 | field :all, list_of(:color) do 15 | resolve fn _, _, _ -> {:ok, [1, 2, 3]} end 16 | end 17 | end 18 | 19 | def hydrate( 20 | %Absinthe.Blueprint.Schema.EnumValueDefinition{identifier: identifier}, 21 | [%Absinthe.Blueprint.Schema.EnumTypeDefinition{identifier: :color}] 22 | ) do 23 | {:as, color_map(identifier)} 24 | end 25 | 26 | def hydrate(_, _) do 27 | [] 28 | end 29 | 30 | defp color_map(:red), do: 1 31 | defp color_map(:blue), do: 2 32 | defp color_map(:green), do: 3 33 | end 34 | 35 | test "can hydrate enum values dynamically" do 36 | assert {:ok, %{data: %{"all" => ["RED", "BLUE", "GREEN"]}}} == Absinthe.run("{ all }", Schema) 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /test/absinthe/schema/rule/directive_must_be_valid_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Schema.Rule.DirectivesMustBeValidTest do 2 | use Absinthe.Case, async: true 3 | 4 | alias Absinthe.Phase.Schema.Validation.DirectivesMustBeValid 5 | 6 | describe "rule" do 7 | test "is enforced" do 8 | assert_schema_error("bad_directives_schema", [ 9 | %{phase: DirectivesMustBeValid, extra: %{}}, 10 | %{phase: DirectivesMustBeValid, extra: %{location: :unknown}} 11 | ]) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/absinthe/schema/rule/names_must_be_valid_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Schema.Rule.NamesMustBeValidTest do 2 | use Absinthe.Case, async: true 3 | 4 | alias Absinthe.Phase.Schema.Validation.NamesMustBeValid 5 | 6 | describe "rule" do 7 | test "is enforced" do 8 | assert_schema_error("bad_names_schema", [ 9 | %{phase: NamesMustBeValid, extra: %{artifact: "field name", value: "bad field name"}}, 10 | %{phase: NamesMustBeValid, extra: %{artifact: "argument name", value: "bad arg name"}}, 11 | %{ 12 | phase: NamesMustBeValid, 13 | extra: %{artifact: "directive name", value: "bad directive name"} 14 | }, 15 | %{phase: NamesMustBeValid, extra: %{artifact: "scalar name", value: "bad?scalar#name"}}, 16 | %{phase: NamesMustBeValid, extra: %{artifact: "object name", value: "bad object name"}}, 17 | %{ 18 | phase: NamesMustBeValid, 19 | extra: %{artifact: "input object name", value: "bad input name"} 20 | }, 21 | %{phase: NamesMustBeValid, extra: %{artifact: "enum value name", value: "1"}} 22 | ]) 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /test/absinthe/schema/rule/no_interface_cycles_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Schema.Rule.NoInterfacecyclesTest do 2 | use Absinthe.Case, async: true 3 | 4 | describe "rule" do 5 | test "is enforced" do 6 | assert_schema_error("interface_cycle_schema", [ 7 | %{ 8 | extra: :named, 9 | locations: [ 10 | %{ 11 | file: "test/support/fixtures/dynamic/interface_cycle_schema.exs", 12 | line: 24 13 | } 14 | ], 15 | message: 16 | "Interface Cycle Error\n\nInterface `named' forms a cycle via: ([:named, :node, :named])", 17 | path: [], 18 | phase: Absinthe.Phase.Schema.Validation.NoInterfaceCyles 19 | }, 20 | %{ 21 | extra: :node, 22 | locations: [ 23 | %{ 24 | file: "test/support/fixtures/dynamic/interface_cycle_schema.exs", 25 | line: 24 26 | } 27 | ], 28 | message: 29 | "Interface Cycle Error\n\nInterface `node' forms a cycle via: ([:node, :named, :node])", 30 | path: [], 31 | phase: Absinthe.Phase.Schema.Validation.NoInterfaceCyles 32 | } 33 | ]) 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /test/absinthe/schema/rule/query_type_must_be_object_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Schema.Rule.QueryTypeMustBeObjectTest do 2 | use Absinthe.Case, async: true 3 | 4 | describe "rule" do 5 | test "is enforced" do 6 | assert_schema_error("empty_schema", [ 7 | %{ 8 | phase: Absinthe.Phase.Schema.Validation.QueryTypeMustBeObject, 9 | extra: %{}, 10 | locations: [ 11 | %{ 12 | file: "test/support/fixtures/dynamic/empty_schema.exs", 13 | line: 0 14 | } 15 | ] 16 | } 17 | ]) 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/absinthe/schema/rule/type_names_are_reserved_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Schema.Rule.TypeNamesAreReservedTest do 2 | use Absinthe.Case, async: true 3 | 4 | alias Absinthe.Phase.Schema.Validation.TypeNamesAreReserved 5 | 6 | describe "rule" do 7 | test "is enforced" do 8 | assert_schema_error("prefix_schema", [ 9 | %{phase: TypeNamesAreReserved, extra: %{artifact: "type name", value: "__MyThing"}}, 10 | %{phase: TypeNamesAreReserved, extra: %{artifact: "field name", value: "__mything"}}, 11 | %{phase: TypeNamesAreReserved, extra: %{artifact: "argument name", value: "__myarg"}}, 12 | %{ 13 | phase: TypeNamesAreReserved, 14 | extra: %{artifact: "directive name", value: "__mydirective"} 15 | }, 16 | %{phase: TypeNamesAreReserved, extra: %{artifact: "argument name", value: "__if"}} 17 | ]) 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/absinthe/schema/rule/type_names_are_valid_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Schema.Rule.TypeNamesAreValidTest do 2 | use Absinthe.Case, async: true 3 | 4 | test "Trying to compile a schema with invalid type references fails" do 5 | assert_raise Absinthe.Schema.Error, fn -> 6 | load_schema("bad_types_schema") 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/support/case.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Case do 2 | defmacro __using__(opts) do 3 | opts = Keyword.drop(opts, [:phase, :schema]) 4 | 5 | quote do 6 | use ExUnit.Case, unquote(opts) 7 | import Absinthe.Case.Helpers.SchemaImplementations 8 | import Absinthe.Case.Helpers.Run 9 | import Absinthe.Case.Assertions.Result 10 | import Absinthe.Case.Assertions.Schema 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/support/case/assertions/result.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Case.Assertions.Result do 2 | import ExUnit.Assertions 3 | 4 | def assert_result({lflag, lhs}, {rflag, rhs}) do 5 | assert clean(lhs) == clean(rhs) 6 | assert lflag == rflag 7 | end 8 | 9 | def assert_data(expected, result) do 10 | assert_result({:ok, %{data: expected}}, result) 11 | end 12 | 13 | def assert_error_message_lines(lines, result) do 14 | assert_error_message(Enum.join(lines, "\n"), result) 15 | end 16 | 17 | # Dialyzer often has issues with test code, and here it says that 18 | # the assertion on line 20 can never match, which is silly. 19 | @dialyzer {:no_match, assert_error_message: 2} 20 | def assert_error_message(error_message, result) do 21 | assert {:ok, %{errors: errors}} = result 22 | 23 | assert Enum.any?(errors, fn %{message: message} -> 24 | message == error_message 25 | end) 26 | end 27 | 28 | defp clean(%{errors: errors} = result) do 29 | cleaned = 30 | errors 31 | |> Enum.map(fn err -> 32 | Map.delete(err, :locations) 33 | end) 34 | 35 | %{result | errors: cleaned} 36 | end 37 | 38 | defp clean(result) do 39 | result 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /test/support/case/helpers/run.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Case.Helpers.Run do 2 | def run(document, schema, options \\ []) do 3 | Absinthe.run(document, schema, options) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/support/case/helpers/schema_implementations.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Case.Helpers.SchemaImplementations do 2 | def schema_implementations(module) do 3 | [ 4 | Module.safe_concat(module, MacroSchema), 5 | Module.safe_concat(module, SDLSchema) 6 | ] 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/support/fixture.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Fixture do 2 | defmacro __using__(_) do 3 | if System.get_env("SCHEMA_PROVIDER") == "persistent_term" do 4 | quote do 5 | @schema_provider Absinthe.Schema.PersistentTerm 6 | end 7 | else 8 | quote do 9 | @schema_provider Absinthe.Schema.Compiled 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/support/fixtures/default_value_schema.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Fixtures.DefaultValueSchema do 2 | use Absinthe.Schema 3 | use Absinthe.Fixture 4 | 5 | # Note: More examples in Absinthe.Fixtures.Query.TestSchemaFieldArgDefaultValueWithImportFields 6 | 7 | query do 8 | field :microsecond, :integer, default_value: DateTime.utc_now().microsecond |> elem(0) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/support/fixtures/dynamic/bad_directives_schema.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.TestSupport.Schema.BadDirectivesSchema do 2 | use Absinthe.Schema 3 | 4 | directive :mydirective do 5 | end 6 | 7 | directive :mydirective2 do 8 | on :unknown 9 | end 10 | 11 | query do 12 | field :foo, :string 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/support/fixtures/dynamic/bad_interface_schema.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.TestSupport.Schema.BadInterfaceSchema do 2 | use Absinthe.Schema 3 | 4 | query do 5 | field :foo, :foo 6 | field :quux, :quux 7 | field :span, :spam 8 | end 9 | 10 | object :foo do 11 | field :not_name, :string 12 | interface :named 13 | interface :aged 14 | 15 | is_type_of fn _ -> 16 | true 17 | end 18 | end 19 | 20 | object :quux do 21 | field :not_name, :string 22 | interface :foo 23 | 24 | is_type_of fn _ -> 25 | true 26 | end 27 | end 28 | 29 | object :spam do 30 | field :name, :string 31 | interface :named 32 | end 33 | 34 | interface :named do 35 | field :name, :string 36 | end 37 | 38 | interface :aged do 39 | field :age, :integer 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /test/support/fixtures/dynamic/bad_names_schema.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.TestSupport.Schema.BadNamesSchema do 2 | use Absinthe.Schema 3 | 4 | object :car, name: "bad object name" do 5 | field :foo, :string 6 | end 7 | 8 | input_object :contact_input, name: "bad input name" do 9 | field :email, non_null(:string) 10 | end 11 | 12 | directive :mydirective, name: "bad directive name" do 13 | on :field 14 | end 15 | 16 | scalar :time, description: "ISOz time", name: "bad?scalar#name" do 17 | parse fn x -> x end 18 | serialize fn x -> x end 19 | end 20 | 21 | query do 22 | field :foo, :string, name: "bad field name" 23 | 24 | field :bar, :car do 25 | arg :foo, :string, name: "bad arg name" 26 | end 27 | end 28 | 29 | enum :rating do 30 | value :"1", as: 1 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/support/fixtures/dynamic/empty_schema.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Test.EmptySchema do 2 | use Absinthe.Schema 3 | end 4 | -------------------------------------------------------------------------------- /test/support/fixtures/dynamic/interface_cycle_schema.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.TestSupport.Schema.InterfaceCycleSchema do 2 | use Absinthe.Schema 3 | 4 | @sdl """ 5 | schema { 6 | query: Query 7 | } 8 | 9 | type Query { 10 | node: Node 11 | } 12 | 13 | interface Node implements Named & Node { 14 | id: ID! 15 | name: String 16 | } 17 | 18 | interface Named implements Node & Named { 19 | id: ID! 20 | name: String 21 | } 22 | """ 23 | 24 | import_sdl @sdl 25 | end 26 | -------------------------------------------------------------------------------- /test/support/fixtures/dynamic/invalid_input_types.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Fixtures.InvalidOutputTypesSchema do 2 | use Absinthe.Schema 3 | 4 | object :user do 5 | field :name, :string 6 | end 7 | 8 | input_object :foo do 9 | field :blah, :user 10 | end 11 | 12 | query do 13 | field :foo, :user do 14 | arg :foo, :foo 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/support/fixtures/dynamic/invalid_input_types_sdl.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Fixtures.InvalidOutputTypesSdlSchema do 2 | use Absinthe.Schema 3 | 4 | import_sdl """ 5 | type User { 6 | name: String 7 | } 8 | 9 | input Foo { 10 | blah: User 11 | } 12 | 13 | type Query { 14 | foo(arg: Foo): User 15 | } 16 | """ 17 | end 18 | -------------------------------------------------------------------------------- /test/support/fixtures/dynamic/invalid_interface_types.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Fixtures.InvalidInterfaceTypes do 2 | use Absinthe.Schema 3 | 4 | object :user do 5 | field :name, non_null(:string) 6 | interface :named 7 | 8 | is_type_of fn _ -> 9 | true 10 | end 11 | end 12 | 13 | object :foo do 14 | field :name, :string 15 | interface :named 16 | 17 | is_type_of fn _ -> 18 | true 19 | end 20 | end 21 | 22 | interface :named do 23 | field :name, non_null(:string) 24 | end 25 | 26 | query do 27 | field :foo, :string 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /test/support/fixtures/dynamic/invalid_output_types.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Fixtures.InvalidInputTypesSchema do 2 | use Absinthe.Schema 3 | 4 | object :user do 5 | field :name, :string 6 | end 7 | 8 | input_object :input do 9 | field :foo, :string 10 | end 11 | 12 | object :bad_object do 13 | field :blah, :input 14 | end 15 | 16 | query do 17 | field :foo, :user do 18 | arg :invalid_arg, :user 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /test/support/fixtures/dynamic/invalid_output_types_sdl.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Fixtures.InvalidInputTypesSdlSchema do 2 | use Absinthe.Schema 3 | 4 | import_sdl """ 5 | type User { 6 | name: String 7 | } 8 | 9 | input Input { 10 | foo: String 11 | } 12 | 13 | type BadObject { 14 | blah: Input 15 | } 16 | 17 | type Query { 18 | foo(invalidArg: User): User 19 | } 20 | """ 21 | end 22 | -------------------------------------------------------------------------------- /test/support/fixtures/dynamic/prefix_schema.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Fixtures.PrefixSchema do 2 | use Absinthe.Schema 3 | 4 | query do 5 | field :foo, :integer do 6 | arg :bar, :string 7 | end 8 | 9 | field :__mything, 10 | name: "__mything", 11 | type: :string, 12 | args: [ 13 | __myarg: [type: :integer] 14 | ], 15 | resolve: fn _, _ -> 16 | {:ok, %{name: "Test"}} 17 | end 18 | end 19 | 20 | object :__mything, name: "__MyThing" do 21 | field :name, :string 22 | end 23 | 24 | directive :__mydirective do 25 | arg :__if, non_null(:boolean), description: "Skipped when true." 26 | 27 | on Language.FragmentSpread 28 | on Language.Field 29 | on Language.InlineFragment 30 | 31 | expand fn 32 | %{if: true}, node -> 33 | Blueprint.put_flag(node, :skip, __MODULE__) 34 | 35 | _, node -> 36 | Blueprint.put_flag(node, :include, __MODULE__) 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /test/support/fixtures/dynamic/schema_with_duplicate_identifiers.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Fixtures.SchemaWithDuplicateIdentifiers do 2 | use Absinthe.Schema 3 | 4 | query do 5 | # Query type must exist 6 | end 7 | 8 | object :person do 9 | description "A person" 10 | field :name, :string 11 | end 12 | 13 | object :person, name: "APersonToo" do 14 | description "A person" 15 | field :name, :string 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/support/fixtures/dynamic/schema_with_duplicate_names.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Fixtures.SchemaWithDuplicateNames do 2 | use Absinthe.Schema 3 | 4 | query do 5 | # Query type must exist 6 | end 7 | 8 | object :person do 9 | description "A person" 10 | field :name, :string 11 | end 12 | 13 | object :another_person, name: "Person" do 14 | description "A person" 15 | field :type, :string 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/support/fixtures/dynamic/unknown_import_schema.exs: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.TestSupport.Schema.UnknownImportSchema do 2 | use Absinthe.Schema 3 | 4 | import_types Test.Unknown 5 | 6 | query do 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/support/fixtures/id_test_schema.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Fixtures.IdTestSchema do 2 | use Absinthe.Schema 3 | use Absinthe.Fixture 4 | 5 | # Example data 6 | @items %{ 7 | "foo" => %{id: "foo", name: "Foo"}, 8 | "bar" => %{id: "bar", name: "Bar"} 9 | } 10 | 11 | query do 12 | field :item, 13 | type: :item, 14 | args: [ 15 | id: [type: non_null(:id)] 16 | ], 17 | resolve: fn %{id: item_id}, _ -> 18 | {:ok, @items[item_id]} 19 | end 20 | end 21 | 22 | object :item do 23 | description "An item" 24 | field :id, :id 25 | field :name, :string 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /test/support/fixtures/import_sdl_binary_fn.graphql: -------------------------------------------------------------------------------- 1 | type User { 2 | name: String! 3 | } -------------------------------------------------------------------------------- /test/support/fixtures/import_sdl_path_option.graphql: -------------------------------------------------------------------------------- 1 | input PostFilter { 2 | name: String 3 | } -------------------------------------------------------------------------------- /test/support/fixtures/import_sdl_path_option_fn.graphql: -------------------------------------------------------------------------------- 1 | "A submitted post" 2 | type Post { 3 | title: String! 4 | upcasedTitle: String! 5 | body: String! 6 | """ 7 | The post author 8 | (is a user) 9 | """ 10 | author: User! 11 | comments: [Comment]! 12 | } -------------------------------------------------------------------------------- /test/support/fixtures/language/kitchen-sink.graphql: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015-present, Facebook, Inc. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree. 5 | 6 | query queryName($foo: ComplexType, $site: Site = MOBILE) { 7 | whoever123is: node(id: [123, 456]) { 8 | id , 9 | ... on User @defer { 10 | field2 { 11 | id , 12 | alias: field1(first:10, after:$foo,) @include(if: $foo) { 13 | id, 14 | ...frag 15 | } 16 | } 17 | } 18 | ... @skip(unless: $foo) { 19 | id 20 | } 21 | ... { 22 | id 23 | } 24 | } 25 | } 26 | 27 | mutation likeStory { 28 | like(story: 123) @defer { 29 | story { 30 | id 31 | } 32 | } 33 | } 34 | 35 | subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) { 36 | storyLikeSubscribe(input: $input) { 37 | story { 38 | likers { 39 | count 40 | } 41 | likeSentence { 42 | text 43 | } 44 | } 45 | } 46 | } 47 | 48 | fragment frag on Friend { 49 | foo(size: $size, bar: $b, obj: {key: "value", block: """ 50 | 51 | block string uses \""" 52 | 53 | """}) 54 | } 55 | 56 | { 57 | unnamed(truthy: true, falsey: false, nullish: null), 58 | query 59 | } 60 | -------------------------------------------------------------------------------- /test/support/fixtures/language/schema-with-emojis.graphql: -------------------------------------------------------------------------------- 1 | # Comment with a 😕 emoji. 2 | schema { 3 | """ 4 | Description with a 🎉 emoji. 5 | """ 6 | query: QueryType 7 | } 8 | 9 | type QueryType { 10 | hello: String 11 | } -------------------------------------------------------------------------------- /test/support/fixtures/object_times_schema.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Fixtures.ObjectTimesSchema do 2 | use Absinthe.Schema 3 | use Absinthe.Fixture 4 | 5 | query do 6 | field :obj_times, :integer do 7 | arg :input, non_null(:times_input) 8 | 9 | resolve fn 10 | _, %{input: %{base: base, multiplier: nil}}, _ -> 11 | {:ok, base} 12 | 13 | _, %{input: %{base: base, multiplier: num}}, _ -> 14 | {:ok, base * num} 15 | end 16 | end 17 | end 18 | 19 | input_object :times_input do 20 | field :multiplier, :integer, default_value: 2 21 | field :base, non_null(:integer) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/support/fixtures/only_query_schema.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Fixtures.OnlyQuerySchema do 2 | use Absinthe.Schema 3 | use Absinthe.Fixture 4 | 5 | query do 6 | field :hello, :string do 7 | resolve fn _, _ -> {:ok, "world"} end 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/support/fixtures/times_schema.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.Fixtures.TimesSchema do 2 | use Absinthe.Schema 3 | use Absinthe.Fixture 4 | 5 | query do 6 | field :times, :integer do 7 | arg :multiplier, :integer, default_value: 2 8 | arg :base, non_null(:integer) 9 | 10 | resolve fn 11 | _, %{base: base, multiplier: nil}, _ -> 12 | {:ok, base} 13 | 14 | _, %{base: base, multiplier: num}, _ -> 15 | {:ok, base * num} 16 | 17 | _, %{base: _}, _ -> 18 | {:error, "Didn't get any multiplier"} 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/support/phase_case.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.PhaseCase do 2 | defmacro __using__(opts) do 3 | phase = Keyword.fetch!(opts, :phase) 4 | schema = Keyword.fetch!(opts, :schema) 5 | 6 | quote do 7 | @doc """ 8 | Execute the pipeline up to and through a phase. 9 | """ 10 | 11 | use Absinthe.Case, unquote(opts) 12 | 13 | @spec run_phase(String.t(), Keyword.t()) :: Absinthe.Phase.result_t() 14 | def run_phase(query, options) do 15 | options = 16 | options 17 | |> Keyword.put(:jump_phases, false) 18 | |> Keyword.put_new(:analyze_complexity, true) 19 | 20 | pipeline = Absinthe.Pipeline.for_document(unquote(schema), options) 21 | Absinthe.Pipeline.run(query, pipeline |> Absinthe.Pipeline.upto(unquote(phase))) 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /test/support/test_telemetry_helper.ex: -------------------------------------------------------------------------------- 1 | defmodule Absinthe.TestTelemetryHelper do 2 | def send_to_pid(event, measurements, metadata, config) do 3 | pid = config[:pid] || self() 4 | send(pid, {:telemetry_event, {event, measurements, metadata, config}}) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.configure(exclude: [pending: true], timeout: 30_000) 2 | ExUnit.start() 3 | 4 | fixtures_schemas = [ 5 | Absinthe.Fixtures.ArgumentsSchema, 6 | Absinthe.Fixtures.ColorSchema, 7 | Absinthe.Fixtures.ContactSchema, 8 | Absinthe.Fixtures.CustomTypesSchema, 9 | Absinthe.Fixtures.IdTestSchema, 10 | Absinthe.Fixtures.NullListsSchema, 11 | Absinthe.Fixtures.ObjectTimesSchema, 12 | Absinthe.Fixtures.OnlyQuerySchema, 13 | Absinthe.Fixtures.PetsSchema, 14 | Absinthe.Fixtures.StrictSchema, 15 | Absinthe.Fixtures.TimesSchema, 16 | Absinthe.Fixtures.Things.SDLSchema, 17 | Absinthe.Fixtures.Things.MacroSchema 18 | ] 19 | 20 | for schema <- fixtures_schemas, 21 | schema.__absinthe_schema_provider__() == Absinthe.Schema.PersistentTerm do 22 | Absinthe.Schema.Manager.start_link(schema) 23 | end 24 | --------------------------------------------------------------------------------