GraphqlQuery.Schema.Remote (graphql_query v0.6.2)

View Source

Utilities for managing remote GraphQL schemas.

Provides functions for deriving schema file paths from module names, resolving schema directory configuration, discovering remote schema modules, fetching remote schema content, and managing schema files on disk.

Configuration

The schemas directory can be configured at multiple levels (in order of precedence):

  1. Per-module :schemas_dir option in use GraphqlQuery.Schema
  2. Application config: config :graphql_query, schemas_dir: "priv/graphql/schemas"
  3. Default: "priv/graphql/schemas"

File Path Derivation

Module names are converted to underscored/nested file paths:

  • MyApp.ExternalSchema<schemas_dir>/my_app/external_schema.graphql
  • MyApp.GitHub.Schema<schemas_dir>/my_app/git_hub/schema.graphql

Summary

Functions

Builds the schema/0 function AST for a remote schema module.

Generates the quoted AST block for a remote schema module.

Returns the default schemas directory path.

Derives a schema file path from a module name and schemas directory.

Discovers all compiled modules that have remote schema configuration.

Fetches the SDL content for a remote schema.

Returns remote schema information for a specific module.

Resolves the schema file path and schemas directory for a remote module.

Resolves the schemas directory from the given option, application config, or default.

Resolves a remote URL at runtime.

Saves schema content to the given file path, creating directories as needed.

Checks whether the stored schema file matches the given content.

Checks whether a URL value is valid for the :remote :url option.

Validates the :remote configuration keyword list.

Emits a compile-time warning when a remote schema file does not exist locally.

Functions

build_schema_fn_ast(schema_path)

@spec build_schema_fn_ast(String.t()) :: Macro.t()

Builds the schema/0 function AST for a remote schema module.

When the local schema file exists at compile time, generates a function that loads it via gql_from_file/2. When it does not exist, generates a function that raises GraphqlQuery.Schema.RemoteNotFetchedError.

compile_ast(remote, schemas_dir_opt, module, explicit_path)

@spec compile_ast(keyword(), String.t() | nil, module(), String.t() | nil) ::
  Macro.t()

Generates the quoted AST block for a remote schema module.

Called by GraphqlQuery.Schema.__using__/1 when the :remote option is present. Validates the remote configuration, resolves file paths, warns if the local schema file is missing, and returns a quoted block that defines __remote_config__/0, __schemas_dir__/0, build_request/1, schema/0, and schema_path/0 on the caller module.

Parameters

  • remote — the keyword list passed as the :remote option
  • schemas_dir_opt — the optional :schemas_dir override (or nil)
  • module — the caller module atom (__CALLER__.module)
  • explicit_path — an explicit :schema_path value (or nil)

default_schemas_dir()

@spec default_schemas_dir() :: String.t()

Returns the default schemas directory path.

derive_schema_path(module, schemas_dir)

@spec derive_schema_path(module(), String.t()) :: String.t()

Derives a schema file path from a module name and schemas directory.

Converts the module name to an underscored/nested path structure with a .graphql extension.

Examples

iex> GraphqlQuery.Schema.Remote.derive_schema_path(MyApp.ExternalSchema, "priv/graphql/schemas")
"priv/graphql/schemas/my_app/external_schema.graphql"

iex> GraphqlQuery.Schema.Remote.derive_schema_path(MyApp.GitHub.Schema, "priv/schemas")
"priv/schemas/my_app/git_hub/schema.graphql"

discover_remote_schemas()

@spec discover_remote_schemas() :: [map()]

Discovers all compiled modules that have remote schema configuration.

Scans all loaded applications and their modules for those that export a __remote_config__/0 function, indicating they have remote schema setup.

Returns a list of maps with module info including the module, remote config, schemas directory, and derived file path.

Examples

iex> GraphqlQuery.Schema.Remote.discover_remote_schemas()
[
  %{
    module: MyApp.ExternalSchema,
    remote: [url: "https://api.example.com/schema.graphql"],
    schemas_dir: "priv/graphql/schemas",
    schema_path: "priv/graphql/schemas/my_app/external_schema.graphql"
  }
]

fetch_schema(map)

@spec fetch_schema(map()) :: {:ok, String.t()} | {:error, String.t()}

Fetches the SDL content for a remote schema.

Accepts the info map produced by module_remote_info/1 (or discover_remote_schemas/0). Resolves the URL at runtime (supporting both plain strings and {Module, :function} tuples), selects the correct HTTP strategy based on :mode, and delegates to the module's build_request/1 callback for request customisation.

Returns {:ok, sdl} on success or {:error, reason} on failure.

Examples

iex> info = GraphqlQuery.Schema.Remote.module_remote_info(MyApp.ExternalSchema)
iex> GraphqlQuery.Schema.Remote.fetch_schema(info)
{:ok, "type Query { ... }"}

module_remote_info(module)

@spec module_remote_info(module()) :: map()

Returns remote schema information for a specific module.

Examples

iex> GraphqlQuery.Schema.Remote.module_remote_info(MyApp.ExternalSchema)
%{
  module: MyApp.ExternalSchema,
  remote: [url: "https://api.example.com/schema.graphql"],
  schemas_dir: "priv/graphql/schemas",
  schema_path: "priv/graphql/schemas/my_app/external_schema.graphql"
}

resolve_schema_paths(explicit_path, schemas_dir_opt, module)

@spec resolve_schema_paths(String.t() | nil, String.t() | nil, module()) ::
  {String.t(), String.t() | nil}

Resolves the schema file path and schemas directory for a remote module.

When explicit_path is provided, it is used directly and schemas_dir is nil. Otherwise, the path is derived from the module name using derive_schema_path/2 and the resolved schemas directory.

Returns a {schema_path, schemas_dir} tuple.

resolve_schemas_dir(module_opt)

@spec resolve_schemas_dir(String.t() | nil) :: String.t()

Resolves the schemas directory from the given option, application config, or default.

Resolution Order

  1. The module_opt value if not nil
  2. Application config: config :graphql_query, schemas_dir: "..."
  3. Default: "priv/graphql/schemas"

Examples

iex> GraphqlQuery.Schema.Remote.resolve_schemas_dir("custom/path")
"custom/path"

iex> GraphqlQuery.Schema.Remote.resolve_schemas_dir(nil)
"priv/graphql/schemas"

resolve_url(url)

@spec resolve_url(String.t() | {module(), atom()}) :: String.t()

Resolves a remote URL at runtime.

Accepts either a plain string URL or a {Module, :function} tuple. When a tuple is given the function is called with no arguments and its return value (which must be a non-empty string) is used as the URL.

Examples

iex> GraphqlQuery.Schema.Remote.resolve_url("https://example.com/schema.graphql")
"https://example.com/schema.graphql"

iex> GraphqlQuery.Schema.Remote.resolve_url({Module, :url})
# calls Module.url() at runtime

save_schema(path, content)

@spec save_schema(String.t(), String.t()) :: :ok | {:error, term()}

Saves schema content to the given file path, creating directories as needed.

Examples

iex> GraphqlQuery.Schema.Remote.save_schema("/tmp/test_schema.graphql", "type Query { hello: String }")
:ok

schemas_match?(path, content)

@spec schemas_match?(String.t(), String.t()) :: boolean()

Checks whether the stored schema file matches the given content.

Returns true if the file exists and its content matches exactly, false otherwise (including when the file doesn't exist).

Examples

iex> File.write!("/tmp/test_match.graphql", "type Query { hello: String }")
iex> GraphqlQuery.Schema.Remote.schemas_match?("/tmp/test_match.graphql", "type Query { hello: String }")
true

iex> GraphqlQuery.Schema.Remote.schemas_match?("/tmp/nonexistent.graphql", "content")
false

valid_url?(url)

@spec valid_url?(term()) :: boolean()

Checks whether a URL value is valid for the :remote :url option.

Accepts non-empty binary strings and {Module, :function} tuples.

validate_config!(remote)

@spec validate_config!(keyword()) :: :ok

Validates the :remote configuration keyword list.

Raises CompileError when:

  • the value is not a keyword list
  • the :url key is missing
  • unknown keys are present (valid keys: [:url, :mode])
  • the :url value is not a non-empty string or {Module, :function} tuple
  • the :mode value is not one of [:fetch, :introspect]

warn_if_schema_missing(schema_path, module)

@spec warn_if_schema_missing(String.t(), module()) :: :ok

Emits a compile-time warning when a remote schema file does not exist locally.

Called during compilation to alert developers that the schema needs to be fetched with mix graphql_query.schema.fetch.