tfwright/live_admin

Allow admin-instance resource config

tfwright opened this issue · 5 comments

See #33

Problems:

  1. right now default config must be defined at the app level, which makes it awkward to use a different set of default configs for different admin UIs (if more than 1 is desired for whatever reason).

  2. since overrides are likely to be desired for most resources, the router config is large/noisy (see this thread)

Proposed solution: deprecate app level default config, and add support for passing a module, which specifies all configuration, including resources:

defmodule MyApp.AdminA do
  use LiveAdmin, 
    ecto_repo: MyApp.RepoA, 
    create_with: {MyApp.AdminContext, :default_create, []},
    update_with: :schema_function_name,
    resources: :get_resources

    def get_resources, do: [MyApp.SchemaA]
end

defmodule MyApp.AdminB do
  use LiveAdmin, 
    ecto_repo: MyApp.RepoB,
    resources: [MyApp.SchemaB]
end

Alternatives:

  • extract resource level config into separate macro that would be called as part of a block, similar to how Phoenix routes work:
live_admin "/admin_url", title: "MyAdmin" do
  admin_resource "/a", MyApp.SchemaA, create_with: :create
  admin_resource "/b", MyApp.SchemaB, create_with: :something
end

It might be that these are two separate problems with config that need to be addressed separately.

@tfwright - thanks a lot for your work here. Just discovered live_admin and it fit our needs perfectly. Specifically, I stumbled upon this issue when looking for multi-repo support. However, I realised, accidentally, that this is already supported out of the box.

I am documenting our use case and how we got it working so someone else looking for the same thing doesn't spend too long on this.
We have 3 apps each with a separate repo. Let's call them AppA.Repo, AppB.Repo and AppC.Repo. Of these, AppA is the parent app to which AppB and AppC are dependencies.

To get live_admin working on all of these with their schemas, all we had to do was the following:

In AppA's config.exs

config :app_a,
  ecto_repos: [AppC.Repo, AppB.Repo, AppA.Repo]

The reason AppA.Repo is listed last is because some of its migrations depend on AppB and AppC and this way migrations run in the correct order when you run mix ecto.migrate.

Then, in AppAWeb's router.ex,

live_admin("/admin",
     resources: [
       AppA.SomeSchema,
       AppB.SomeSchema,
       AppC.SomeSchema
     ]
)

This works because (and I didn't know this previously), AppA.Repo.all(%AppB.SomeSchema{}) just works as long as AppB.Repo is included in ecto_repos for AppA.

Ignore if this is known behaviour but it certainly surprised me and Kaffy's autodetection only picks up AppA schemas.

Definitely not expected behavior! I haven't tried to use multiple Ecto repos myself, so I'm not sure, but LiveAdmin currently executes all queries in the context of whatever repo is set in its config. I am surprised that it would work with any other repos.

Here's a simple example that demos this behaviour: https://github.com/tejpochiraju/multi_repo_live_admin

Hm, just a guess but I think the key implementation detail there is that the same db is being used. I think that means that even though LiveAdmin is still using the "wrong" repo when querying whatever schemas are "supposed" to be on the other repo, since the db is the same the queries end up being directed to the same place. My expectation would be if you alter the AppB.Repo config to point at a different file with the AppB schema tables, and remove those tables from AppA.Repo file, then AppB schema resources will no longer work in LiveAdmin

You are right. With separate DBs (SQLite3 or Postgres), I get the following error:

[error] GenServer #PID<0.851.0> terminating
** (Postgrex.Error) ERROR 42P01 (undefined_table) relation "users" does not exist

Nevertheless, this is still useful behaviour if possibly undocumented on the Ecto side of things (I can't seem to find a reference). Before I stumbled upon this approach, I was considering my options.

  • You said it was "awkward" to configure multiple admin UIs. Does this mean it's possible, how would one go about this?
  • Your suggested approach of passing an MFA during the router-level config would work.
  • Replace calls to repo/0 with a new repo/1 that infers the right Repo from the resource being queried. This seemed the simplest to me. What are the pitfalls here?

EDIT

  • Confirmed that setting ecto_repos: [AppA.Repo] also works. I think this confirms your theory that once you have a Repo connection, the second schema query works if the table is found the current context (DB + prefix).
  • It follows that this will also fail if AppA was configured with a user that does not have access to AppB tables - even if pointing to the same DB.