Current section

Files

Jump to
ash_authentication_phoenix lib mix tasks ash_authentication_phoenix.upgrade.ex
Raw

lib/mix/tasks/ash_authentication_phoenix.upgrade.ex

# SPDX-FileCopyrightText: 2022 Alembic Pty Ltd
#
# SPDX-License-Identifier: MIT
if Code.ensure_loaded?(Igniter) do
defmodule Mix.Tasks.AshAuthenticationPhoenix.Upgrade do
@moduledoc false
# credo:disable-for-this-file Credo.Check.Design.AliasUsage
# credo:disable-for-this-file Credo.Check.Refactor.Nesting
use Igniter.Mix.Task
@impl Igniter.Mix.Task
def info(_argv, _composing_task) do
%Igniter.Mix.Task.Info{
# Groups allow for overlapping arguments for tasks by the same author
# See the generators guide for more.
group: :ash_authentication_phoenix,
# *other* dependencies to add
# i.e `{:foo, "~> 2.0"}`
adds_deps: [],
# *other* dependencies to add and call their associated installers, if they exist
# i.e `{:foo, "~> 2.0"}`
installs: [],
# a list of positional arguments, i.e `[:file]`
positional: [:from, :to],
# Other tasks your task composes using `Igniter.compose_task`, passing in the CLI argv
# This ensures your option schema includes options from nested tasks
composes: [],
# `OptionParser` schema
schema: [],
# Default values for the options in the `schema`
defaults: [],
# CLI aliases
aliases: [],
# A list of options in the schema that are required
required: []
}
end
@impl Igniter.Mix.Task
def igniter(igniter) do
positional = igniter.args.positional
options = igniter.args.options
upgrades =
%{
"2.10.6" => [&change_auth_routes_for_to_auth_routes_for_all_routers/2]
}
# For each version that requires a change, add it to this map
# Each key is a version that points at a list of functions that take an
# igniter and options (i.e. flags or other custom options).
# See the upgrades guide for more.
Igniter.Upgrades.run(igniter, positional.from, positional.to, upgrades,
custom_opts: options
)
end
defp change_auth_routes_for_to_auth_routes_for_all_routers(igniter, _opts) do
igniter
|> Igniter.Libs.Phoenix.list_routers()
|> then(fn {igniter, routers} ->
Enum.reduce(routers, igniter, &change_auth_routes_for_to_auth_routes_for_router(&2, &1))
end)
end
defp change_auth_routes_for_to_auth_routes_for_router(igniter, router) do
igniter
|> Igniter.Project.Module.find_and_update_module!(router, fn zipper ->
updated_zipper = transform_all_auth_routes_for_calls(zipper)
{:ok, updated_zipper}
end)
end
defp transform_all_auth_routes_for_calls(zipper) do
# Keep transforming until no more auth_routes_for calls are found
case find_next_auth_routes_for_call(zipper) do
{:ok, call_zipper} ->
# Transform this call
updated_zipper = transform_auth_routes_for_call(call_zipper)
# Continue searching from the top again
transform_all_auth_routes_for_calls(Sourceror.Zipper.top(updated_zipper))
:error ->
# No more calls found, we're done
zipper
end
end
defp find_next_auth_routes_for_call(zipper) do
Igniter.Code.Common.move_to(
zipper,
&Igniter.Code.Function.function_call?(&1, :auth_routes_for, 2)
)
end
defp transform_auth_routes_for_call(call_zipper) do
case Sourceror.Zipper.node(call_zipper) do
{:auth_routes_for, meta, [resource_ast, opts_ast]} ->
case extract_controller_from_opts(opts_ast) do
{:ok, controller_ast} ->
# Remove the :to option from the opts since controller is now the first parameter
cleaned_opts_ast = remove_to_option_from_opts(opts_ast)
# If options list is empty, omit it entirely
args =
if opts_empty?(cleaned_opts_ast) do
[controller_ast, resource_ast]
else
[controller_ast, resource_ast, cleaned_opts_ast]
end
new_call_ast = {:auth_routes, meta, args}
Sourceror.Zipper.replace(call_zipper, new_call_ast)
:error ->
call_zipper
end
_ ->
call_zipper
end
end
defp extract_controller_from_opts(opts_ast) do
# Create a temporary zipper for the opts AST to use Igniter.Code.Keyword
temp_zipper = Sourceror.Zipper.zip(opts_ast)
case Igniter.Code.Keyword.get_key(temp_zipper, :to) do
{:ok, controller_zipper} ->
{:ok, Sourceror.Zipper.node(controller_zipper)}
:error ->
:error
end
end
defp remove_to_option_from_opts(opts_ast) do
# Create a temporary zipper for the opts AST to use Igniter.Code.Keyword
temp_zipper = Sourceror.Zipper.zip(opts_ast)
case Igniter.Code.Keyword.remove_keyword_key(temp_zipper, :to) do
{:ok, updated_zipper} ->
Sourceror.Zipper.node(updated_zipper)
:error ->
opts_ast
end
end
defp opts_empty?(opts_ast) do
case opts_ast do
[] -> true
list when is_list(list) -> Enum.empty?(list)
_ -> false
end
end
end
else
defmodule Mix.Tasks.AshAuthenticationPhoenix.Upgrade do
@moduledoc false
use Mix.Task
@impl Mix.Task
def run(_argv) do
Mix.shell().error("""
The task 'ash_authentication_phoenix.upgrade' requires igniter. Please install igniter and try again.
For more information, see: https://hexdocs.pm/igniter/readme.html#installation
""")
exit({:shutdown, 1})
end
end
end