Skip to content

Commit

Permalink
Feat: add --check option in Mix tasks to compare generated spec w/ fi…
Browse files Browse the repository at this point in the history
…le (#618)

Add a --check option to run Mix tasks and compare the generated spec
with a previously generated file.

This is useful for scenarios where a CI check is desirable to catch
unwanted drifts from a validated OpenAPI spec: e.g. the OpenAPI spec is
committed and reviewed, and should not change without additional review.

Fixes #617

Signed-off-by: Davide Briani <davide@briani.dev>
  • Loading branch information
davidebriani authored Jul 7, 2024
1 parent 5a79def commit 56dcf23
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 5 deletions.
3 changes: 3 additions & 0 deletions lib/mix/tasks/openapi.spec.json.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ defmodule Mix.Tasks.Openapi.Spec.Json do
$ mix openapi.spec.json --spec PhoenixAppWeb.ApiSpec apispec.json
$ mix openapi.spec.json --spec PhoenixAppWeb.ApiSpec --pretty=true
$ mix openapi.spec.json --spec PhoenixAppWeb.ApiSpec --check=true
$ mix openapi.spec.json --spec PhoenixAppWeb.ApiSpec --start-app=false
$ mix openapi.spec.json --spec PhoenixAppWeb.ApiSpec --vendor-extensions=false
Expand All @@ -16,6 +17,8 @@ defmodule Mix.Tasks.Openapi.Spec.Json do
* `--pretty` - Whether to prettify the generated JSON (defaults to false)
* `--check` - Whether to only compare the generated JSON with the spec file (defaults to false)
* `--start-app` - Whether need to start application before generate schema (defaults to true)
* `--vendor-extensions` - Whether to include open_api_spex OpenAPI vendor extensions
Expand Down
3 changes: 3 additions & 0 deletions lib/mix/tasks/openapi.spec.yaml.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ defmodule Mix.Tasks.Openapi.Spec.Yaml do
## Examples
$ mix openapi.spec.yaml --spec PhoenixAppWeb.ApiSpec apispec.yaml
$ mix openapi.spec.yaml --spec PhoenixAppWeb.ApiSpec --check=true
$ mix openapi.spec.yaml --spec PhoenixAppWeb.ApiSpec --start-app=false
$ mix openapi.spec.yaml --spec PhoenixAppWeb.ApiSpec --vendor-extensions=false
## Command line options
* `--spec` - The ApiSpec module from which to generate the OpenAPI YAML file
* `--check` - Whether to only compare the generated YAML with the spec file (defaults to false)
* `--start-app` - Whether to start the application before generating the schema (defaults to true)
* `--vendor-extensions` - Whether to include open_api_spex OpenAPI vendor extensions
Expand Down
29 changes: 24 additions & 5 deletions lib/open_api_spex/export_spec.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,27 @@ defmodule OpenApiSpex.ExportSpec do
defmodule Options do
@moduledoc false

defstruct filename: nil, spec: nil, pretty: false, vendor_extensions: true, quiet: false
defstruct filename: nil,
spec: nil,
pretty: false,
check: false,
vendor_extensions: true,
quiet: false
end

def call(argv, encode_spec, default_filename) do
opts = parse_options(argv, default_filename)

opts
|> generate_spec()
|> encode_spec.(opts)
|> write_spec(opts)
encoded_spec =
opts
|> generate_spec()
|> encode_spec.(opts)

if opts.check do
check_spec(encoded_spec, opts)
else
write_spec(encoded_spec, opts)
end
end

defp generate_spec(%{spec: spec, vendor_extensions: vendor_extensions}) do
Expand Down Expand Up @@ -45,6 +56,7 @@ defmodule OpenApiSpex.ExportSpec do
spec: :string,
endpoint: :string,
pretty: :boolean,
check: :boolean,
vendor_extensions: :boolean,
quiet: :boolean
]
Expand All @@ -56,11 +68,18 @@ defmodule OpenApiSpex.ExportSpec do
filename: args |> List.first() || default_filename,
spec: find_spec(opts),
pretty: Keyword.get(opts, :pretty, false),
check: Keyword.get(opts, :check, false),
vendor_extensions: Keyword.get(opts, :vendor_extensions, true),
quiet: Keyword.get(opts, :quiet, false)
}
end

defp check_spec(content, opts) do
unless content == File.read!(opts.filename) do
Mix.raise("The OpenAPI spec file does not match the generated spec:\n\n#{content}")
end
end

defp write_spec(content, opts) do
case Path.dirname(opts.filename) do
"." -> true
Expand Down

0 comments on commit 56dcf23

Please sign in to comment.