diff --git a/CHANGELOG.md b/CHANGELOG.md index 726c627613..e4b49da8cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,11 @@ and this project adheres to ### Added +- Added an `iex` command to setup a user, apiToken, project, and credential so + that it's possible to get a fully running lightning instance via external + shell script. (This is a tricky requirement for a distributed set of local + deployments) [#2369](https://github.com/OpenFn/lightning/issues/2369) + ### Changed ### Fixed diff --git a/lib/lightning/setup_utils.ex b/lib/lightning/setup_utils.ex index ca12576c4e..0947c49127 100644 --- a/lib/lightning/setup_utils.ex +++ b/lib/lightning/setup_utils.ex @@ -868,4 +868,61 @@ defmodule Lightning.SetupUtils do |> limit(1) |> Repo.one!() end + + @doc """ + In some (mostly remote-controlled) deployments, it's necessary to create a + user, empty projects that they can access, and credentials (shared with those + projects) that they own so that later `openfn deploy` calls can make use of + these artifacts. + + When run _before_ `openfn deploy`, this function makes it possible to set up + an entire lightning instance with a working project (including secrets) + without using the web UI. + """ + def setup_user(user, token, project_names, credentials) do + # create user + {:ok, user} = Accounts.create_user(user) + + # create token + Repo.insert!(%Lightning.Accounts.UserToken{ + user_id: user.id, + context: "api", + token: token + }) + + # create projects + projects = + project_names + |> Enum.map(fn name -> + {:ok, project} = + Projects.create_project( + %{ + name: name, + history_retention_period: + Application.get_env(:lightning, :default_retention_period), + project_users: [%{user_id: user.id, role: :owner}] + }, + false + ) + + project + end) + + # create credentials + Enum.each(credentials, fn credential -> + {:ok, _credential} = + Credentials.create_credential( + credential + |> Map.put(:user_id, user.id) + |> Map.put( + :project_credentials, + Enum.map(projects, fn project -> + %{project_id: project.id} + end) + ) + ) + end) + + :ok + end end diff --git a/test/lightning/setup_utils_test.exs b/test/lightning/setup_utils_test.exs index 4fa06be127..08492d7dcb 100644 --- a/test/lightning/setup_utils_test.exs +++ b/test/lightning/setup_utils_test.exs @@ -4,7 +4,9 @@ defmodule Lightning.SetupUtilsTest do import Swoosh.TestAssertions alias Lightning.{Accounts, Projects, Workflows, Jobs, SetupUtils} - alias Lightning.Accounts.User + alias Lightning.Projects.{Project, ProjectUser, ProjectCredential} + alias Lightning.Accounts.{User, UserToken} + alias Lightning.Credentials.{Credential} describe "Setup demo site seed data" do setup do @@ -34,7 +36,7 @@ defmodule Lightning.SetupUtilsTest do User.valid_password?(super_user, "welcome123") user_token = - Lightning.Repo.all(Lightning.Accounts.UserToken) + Lightning.Repo.all(UserToken) |> List.first() assert user_token.user_id == super_user.id @@ -648,6 +650,68 @@ defmodule Lightning.SetupUtilsTest do end end + describe "setup_user/4" do + test "creates a user, an api token, projects, and credentials" do + assert :ok == + Lightning.SetupUtils.setup_user( + %{ + first_name: "Taylor", + last_name: "Downs", + email: "contact@openfn.org", + password: "shh12345678!" + }, + "abc123", + ["project-a", "project-b"], + [ + %{ + name: "openmrs", + schema: "raw", + body: %{"a" => "secret"} + }, + %{ + name: "dhis2", + schema: "raw", + body: %{"b" => "safe"} + } + ] + ) + + # check that the user and the API token has been created + assert %User{id: user_id} = Repo.get_by(User, email: "contact@openfn.org") + assert %UserToken{} = Repo.get_by(UserToken, token: "abc123") + + # check that both projects are there + assert [%Project{id: p1_id}, %Project{id: p2_id}] = Repo.all(Project) + + # check that the user has owner access to both + assert [ + %ProjectUser{project_id: ^p1_id, user_id: ^user_id, role: :owner}, + %ProjectUser{project_id: ^p2_id, user_id: ^user_id, role: :owner} + ] = + Repo.all(ProjectUser) + + credentials = + Repo.all(Credential) |> Repo.preload(:project_credentials) + + assert [ + %Credential{ + name: "openmrs", + project_credentials: [ + %ProjectCredential{project_id: ^p1_id}, + %ProjectCredential{project_id: ^p2_id} + ] + }, + %Credential{ + name: "dhis2", + project_credentials: [ + %ProjectCredential{project_id: ^p1_id}, + %ProjectCredential{project_id: ^p2_id} + ] + } + ] = credentials + end + end + defp get_dataclip_body(dataclip_id) do from(d in Lightning.Invocation.Dataclip, select: type(d.body, :string),