2023-08-15 10:30:35 +12:00
|
|
|
defmodule Ash.Mix.Tasks.Helpers do
|
|
|
|
@moduledoc """
|
|
|
|
Helpers for Ash Mix tasks.
|
|
|
|
"""
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Gets all extensions in use by the current project's apis and resources
|
|
|
|
"""
|
2023-08-15 11:18:45 +12:00
|
|
|
def extensions!(argv, opts \\ []) do
|
|
|
|
if opts[:in_use?] do
|
|
|
|
apis = Ash.Mix.Tasks.Helpers.apis!(argv)
|
2023-08-15 10:30:35 +12:00
|
|
|
|
2023-08-15 11:18:45 +12:00
|
|
|
resource_extensions =
|
|
|
|
apis
|
|
|
|
|> Enum.flat_map(&Ash.Api.Info.resources/1)
|
|
|
|
|> all_extensions()
|
2023-08-15 10:30:35 +12:00
|
|
|
|
2023-08-15 11:18:45 +12:00
|
|
|
Enum.uniq(all_extensions(apis) ++ resource_extensions)
|
|
|
|
else
|
|
|
|
Application.loaded_applications()
|
|
|
|
|> Enum.map(&elem(&1, 0))
|
|
|
|
|> Enum.flat_map(&elem(:application.get_key(&1, :modules), 1))
|
|
|
|
|> Stream.chunk_every(100)
|
|
|
|
# This takes a while, but it doesn't when we split up the work
|
|
|
|
|> Task.async_stream(fn modules ->
|
|
|
|
modules
|
|
|
|
|> Enum.filter(&Spark.implements_behaviour?(&1, Spark.Dsl.Extension))
|
|
|
|
|> Enum.uniq()
|
|
|
|
end)
|
|
|
|
# we're assuming no failures
|
|
|
|
|> Stream.flat_map(&elem(&1, 1))
|
|
|
|
|> Enum.uniq()
|
|
|
|
end
|
2023-08-15 10:30:35 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Get all apis for the current project and ensure they are compiled.
|
|
|
|
"""
|
2023-08-15 11:18:45 +12:00
|
|
|
def apis!(argv) do
|
2023-08-15 10:30:35 +12:00
|
|
|
{opts, _} = OptionParser.parse!(argv, strict: [apis: :string])
|
|
|
|
|
|
|
|
apis =
|
|
|
|
if opts[:apis] && opts[:apis] != "" do
|
|
|
|
opts[:apis]
|
|
|
|
|> Kernel.||("")
|
|
|
|
|> String.split(",")
|
|
|
|
|> Enum.flat_map(fn
|
|
|
|
"" ->
|
|
|
|
[]
|
|
|
|
|
|
|
|
api ->
|
|
|
|
[Module.concat([api])]
|
|
|
|
end)
|
|
|
|
else
|
|
|
|
apps =
|
|
|
|
if apps_paths = Mix.Project.apps_paths() do
|
|
|
|
apps_paths |> Map.keys() |> Enum.sort()
|
|
|
|
else
|
|
|
|
[Mix.Project.config()[:app]]
|
|
|
|
end
|
|
|
|
|
|
|
|
Enum.flat_map(apps, &Application.get_env(&1, :ash_apis, []))
|
|
|
|
end
|
|
|
|
|
|
|
|
apis
|
|
|
|
|> Enum.map(&ensure_compiled(&1, argv))
|
|
|
|
|> case do
|
|
|
|
[] ->
|
|
|
|
raise "must supply the --apis argument, or set `config :my_app, ash_apis: [...]` in config"
|
|
|
|
|
|
|
|
apis ->
|
|
|
|
apis
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defp all_extensions(modules) do
|
|
|
|
modules
|
|
|
|
|> Enum.flat_map(&Spark.extensions/1)
|
|
|
|
|> Enum.uniq()
|
|
|
|
end
|
|
|
|
|
|
|
|
defp ensure_compiled(api, args) do
|
|
|
|
if Code.ensure_loaded?(Mix.Tasks.App.Config) do
|
|
|
|
Mix.Task.run("app.config", args)
|
|
|
|
else
|
|
|
|
Mix.Task.run("loadpaths", args)
|
|
|
|
"--no-compile" not in args && Mix.Task.run("compile", args)
|
|
|
|
end
|
|
|
|
|
|
|
|
case Code.ensure_compiled(api) do
|
|
|
|
{:module, _} ->
|
|
|
|
# TODO: We shouldn't need to make sure that the resources are compiled
|
|
|
|
api
|
|
|
|
|> Ash.Api.Info.resources()
|
|
|
|
|> Enum.each(&Code.ensure_compiled/1)
|
|
|
|
|
|
|
|
api
|
|
|
|
|
|
|
|
{:error, error} ->
|
|
|
|
Mix.raise("Could not load #{inspect(api)}, error: #{inspect(error)}. ")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|