AshOaskit. Spec behaviour
(AshOasKit v0.2.1)
View Source
Runtime support and behaviour for spec modules defined with use AshOaskit.
A spec module turns a set of Ash domains into a long-lived OpenAPI
spec that integrates with the whole Oaskit toolchain:
defmodule MyAppWeb.ApiSpec do
use AshOaskit,
domains: [MyApp.Blog],
title: "My API",
api_version: "1.0.0"
enduse AshOaskit implements the Oaskit behaviour, so the module
gains, with no extra code:
- Caching — the generated spec is stored in
:persistent_termand the Ash domain walk runs once, not per request - Serving —
Oaskit.SpecControllercan serve it as JSON or render a Redoc UI - Request validation —
Oaskit.Plugs.SpecProvider+Oaskit.Plugs.ValidateRequestwork for hand-written Phoenix controllers documented alongside the Ash routes - Export —
mix openapi.dump MyAppWeb.ApiSpec
Options
| Option | Type | Default | Description |
|---|---|---|---|
:domains | [module()] | required | Ash domains to include |
:version | "3.0" or "3.1" | "3.1" | OpenAPI version |
:title | String.t() | "API" | info.title |
:api_version | String.t() | "1.0.0" | info.version |
:description | String.t() | nil | info.description |
:terms_of_service | String.t() | nil | info.termsOfService |
:contact | map() | nil | info.contact |
:license | map() | nil | info.license |
:servers | [String.t() | map()] | [] | servers array |
:security | [map()] | nil | Top-level security requirements |
:external_docs | map() | nil | External documentation object |
:router | module() | nil | Phoenix router for controller introspection |
:modify_open_api | function or MFA | nil | Post-generation hook (see AshOaskit.SpecModifier) |
:spec_builder | module() | nil | AshOaskit.SpecBuilder implementation |
:cache | boolean() | true | Cache the generated spec |
Customizing the spec
Override modify_spec/1 to post-process the generated map (the
result is what gets cached):
defmodule MyAppWeb.ApiSpec do
use AshOaskit, domains: [MyApp.Blog]
@impl AshOaskit.Spec
def modify_spec(spec) do
put_in(spec, ["components", "securitySchemes"], %{
"bearerAuth" => %{"type" => "http", "scheme" => "bearer"}
})
end
endAll Oaskit callbacks remain overridable too — cache/1 to swap the
cache backend, cache_variant/0 to key the cache (e.g. per tenant),
and jsv_opts/0 for validation tuning.
Caching
The spec is generated once and cached in :persistent_term under
{:ash_oaskit_cache, module, cache_variant}. Two switches disable it:
- per module:
use AshOaskit, cache: false, ... - globally at runtime:
config :ash_oaskit, cache_specs: false(recommended indev.exsso code reloads regenerate the spec)
Dual-version output
An Oaskit spec module is one spec by contract. To serve OpenAPI 3.0
and 3.1 side by side, define two spec modules with different
:version options.
Summary
Callbacks
Invoked after the spec is generated, before it is cached.
Functions
Builds (and caches) the OpenAPI spec for a spec module.
Validates use AshOaskit options at compile time.
Types
@type option() :: {:domains, [module()]} | {:version, String.t()} | {:title, String.t()} | {:api_version, String.t()} | {:description, String.t()} | {:terms_of_service, String.t()} | {:contact, map()} | {:license, map()} | {:servers, [String.t() | map()]} | {:security, [map()]} | {:external_docs, map()} | {:router, module()} | {:modify_open_api, function() | {module(), atom(), [term()]}} | {:spec_builder, module()} | {:cache, boolean()}
Compile-time options accepted by use AshOaskit.
Callbacks
Functions
Builds (and caches) the OpenAPI spec for a spec module.
Used by the spec/0 callback that use AshOaskit defines. The cache
is keyed on the module and its cache_variant/0, and can be disabled
per module (cache: false) or globally
(config :ash_oaskit, cache_specs: false).
Parameters
module- The spec module (use AshOaskit)opts- The validateduse AshOaskitoptions
Returns
The OpenAPI specification map.
Validates use AshOaskit options at compile time.
Raises ArgumentError for unknown options, a missing or empty
:domains list, or an unsupported :version.
Examples
iex> AshOaskit.Spec.validate_opts!([domains: [MyApp.Blog]], MyModule)
[domains: [MyApp.Blog]]