From 69c362210eb1dc6336a951d11af48e5d478d35cb Mon Sep 17 00:00:00 2001
From: stuartc Registry process to query and maintain a list of adaptors available for
writing jobs. Currently it queries NPM for all modules in the Usage Caching By default the results are cached to disk, and will be reused every start. In order to disable or configure caching pass see: The process uses Caching By default the results are cached to disk, and will be reused every start. In order to disable or configure caching pass see: The process uses Timeouts There is a 'general' timeout of 30s, this is used for GenServer calls like
Destructures an NPM style package name into module name and version. Example Destructures an NPM style package name into module name and version. Example Queries the AI assistant with the given content. Returns Example Queries the AI assistant with the given content. Returns Example Perform a DELETE request. See Perform a DELETE request. See Perform a DELETE request. See Perform a DELETE request. See Perform a GET request. See Perform a GET request. See Perform a GET request. See Perform a GET request. See Perform a HEAD request. See Perform a HEAD request. See Perform a HEAD request. See Perform a HEAD request. See Perform a OPTIONS request. See Perform a OPTIONS request. See Perform a OPTIONS request. See Perform a OPTIONS request. See Perform a PATCH request. See Perform a PATCH request. See Perform a PATCH request. See Perform a PATCH request. See Perform a POST request. See Perform a POST request. See Perform a POST request. See Perform a POST request. See Perform a PUT request. See Perform a PUT request. See Perform a PUT request. See Perform a PUT request. See Perform a TRACE request. See Perform a TRACE request. See Perform a TRACE request. See Perform a TRACE request. See The OpenFn CLI returns JSON formatted log lines, which are decoded and added
-to a There are two kinds of output: These are usually for general logging, and debugging. The above is the equivalent of the output of a commandapply_user_email(user, password, attrs)
Examples
-
+iex> apply_user_email(user, "valid password", %{email: ...})
-{:ok, %User{}}role: :superuser
-iex> apply_user_email(user, "invalid password", %{email: ...})
-{:error, %Ecto.Changeset{}}
iex> apply_user_email(user, "valid password", %{email: ...})
+{:ok, %User{}}role: :superuser
+iex> apply_user_email(user, "invalid password", %{email: ...})
+{:error, %Ecto.Changeset{}}
change_scheduled_deletion(user, attrs \\ %{
Examples
-
+iex> change_scheduled_deletion(user)
-%Ecto.Changeset{data: %User{}}
iex> change_scheduled_deletion(user)
+%Ecto.Changeset{data: %User{}}
change_superuser_registration(attrs \\ %{})
Examples
-
+iex> change_superuser_registration(user)
-%Ecto.Changeset{data: %User{}}
iex> change_superuser_registration(user)
+%Ecto.Changeset{data: %User{}}
change_user_email(user, attrs \\ %{})
Examples
-
+iex> change_user_email(user)
-%Ecto.Changeset{data: %User{}}
iex> change_user_email(user)
+%Ecto.Changeset{data: %User{}}
change_user_password(user, attrs \\ %{})
Examples
-
+iex> change_user_password(user)
-%Ecto.Changeset{data: %User{}}
iex> change_user_password(user)
+%Ecto.Changeset{data: %User{}}
change_user_registration(attrs \\ %{})
Examples
-
+iex> change_user_registration(user)
-%Ecto.Changeset{data: %User{}}
iex> change_user_registration(user)
+%Ecto.Changeset{data: %User{}}
delete_token(token)
Examples
-iex> delete_token(token)
-{:ok, %UserToken{}}
+
+iex> delete_token(token)
+{:error, %Ecto.Changeset{}}iex> delete_token(token)
+{:ok, %UserToken{}}
-iex> delete_token(token)
-{:error, %Ecto.Changeset{}}
delete_user(user)
Examples
-iex> delete_user(user)
-{:ok, %User{}}
+
+iex> delete_user(user)
+{:error, %Ecto.Changeset{}}iex> delete_user(user)
+{:ok, %User{}}
-iex> delete_user(user)
-{:error, %Ecto.Changeset{}}
deliver_user_confirmation_instructions(user
Examples
-
iex> deliver_user_confirmation_instructions(user)
-{:ok, %{to: ..., body: ...}}
+
+iex> deliver_user_confirmation_instructions(confirmed_user)
+{:error, :already_confirmed}iex> deliver_user_confirmation_instructions(user)
+{:ok, %{to: ..., body: ...}}
-iex> deliver_user_confirmation_instructions(confirmed_user)
-{:error, :already_confirmed}
deliver_user_reset_password_instructions(us
Examples
-
+iex> deliver_user_reset_password_instructions(user, &Routes.user_reset_password_url(conn, :edit, &1))
-{:ok, %{to: ..., body: ...}}
iex> deliver_user_reset_password_instructions(user, &Routes.user_reset_password_url(conn, :edit, &1))
+{:ok, %{to: ..., body: ...}}
get_token!(id)
Examples
-iex> get_token!(123)
-%UserToken{}
+
iex> get_token!(123)
+%UserToken{}
-iex> get_token!(456)
+iex> get_token!(456)
** (Ecto.NoResultsError)
get_user!(id)
Examples
-iex> get_user!(123)
-%User{}
+
@@ -1572,10 +1572,10 @@ iex> get_user!(123)
+%User{}
-iex> get_user!(456)
+iex> get_user!(456)
** (Ecto.NoResultsError)
get_user_by_email(email)
Examples
-iex> get_user_by_email("foo@example.com")
-%User{}
+
@@ -1604,10 +1604,10 @@ iex> get_user_by_email("foo@example.com")
+%User{}
-iex> get_user_by_email("unknown@example.com")
+iex> get_user_by_email("unknown@example.com")
nil
get_user_by_email_and_password(email, passw
Examples
-
iex> get_user_by_email_and_password("foo@example.com", "correct_password")
-%User{}
+
@@ -1636,10 +1636,10 @@ iex> get_user_by_email_and_password("foo@example.com", "correct_password")
+%User{}
-iex> get_user_by_email_and_password("foo@example.com", "invalid_password")
+iex> get_user_by_email_and_password("foo@example.com", "invalid_password")
nil
get_user_by_reset_password_token(token)
Examples
-iex> get_user_by_reset_password_token("validtoken")
-%User{}
+
@@ -1836,8 +1836,8 @@ iex> get_user_by_reset_password_token("validtoken")
+%User{}
-iex> get_user_by_reset_password_token("invalidtoken")
+iex> get_user_by_reset_password_token("invalidtoken")
nil
list_users()
Examples
-
+iex> list_users()
-[%User{}, ...]
iex> list_users()
+[%User{}, ...]
register_superuser(attrs)
Examples
-iex> register_superuser(%{field: value})
-{:ok, %User{}}
+
+iex> register_superuser(%{field: bad_value})
+{:error, %Ecto.Changeset{}}iex> register_superuser(%{field: value})
+{:ok, %User{}}
-iex> register_superuser(%{field: bad_value})
-{:error, %Ecto.Changeset{}}
register_user(attrs)
Examples
-iex> register_user(%{field: value})
-{:ok, %User{}}
+
+iex> register_user(%{field: bad_value})
+{:error, %Ecto.Changeset{}}iex> register_user(%{field: value})
+{:ok, %User{}}
-iex> register_user(%{field: bad_value})
-{:error, %Ecto.Changeset{}}
request_email_update(user, new_email)
Examples
-iex> request_email_update(user, new_email)
+
iex> request_email_update(user, new_email)
:ok
reset_user_password(user, attrs)
Examples
-iex> reset_user_password(user, %{password: "new long password", password_confirmation: "new long password"})
-{:ok, %User{}}
+
+iex> reset_user_password(user, %{password: "valid", password_confirmation: "not the same"})
+{:error, %Ecto.Changeset{}}iex> reset_user_password(user, %{password: "new long password", password_confirmation: "new long password"})
+{:ok, %User{}}
-iex> reset_user_password(user, %{password: "valid", password_confirmation: "not the same"})
-{:error, %Ecto.Changeset{}}
update_user_password(user, password, attrs)
Examples
-
iex> update_user_password(user, "valid password", %{password: ...})
-{:ok, %User{}}
+
+iex> update_user_password(user, "invalid password", %{password: ...})
+{:error, %Ecto.Changeset{}}iex> update_user_password(user, "valid password", %{password: ...})
+{:ok, %User{}}
-iex> update_user_password(user, "invalid password", %{password: ...})
-{:error, %Ecto.Changeset{}}
validate_change_user_email(user, params \\
Examples
-
+iex> validate_change_user_email(user, %{"email" => "new@example.com", "current_password" => "secret"})
-%Ecto.Changeset{...}
iex> validate_change_user_email(user, %{"email" => "new@example.com", "current_password" => "secret"})
+%Ecto.Changeset{...}
request(request)
Examples
-request = %HTTPoison.Request{
+
+request(request)request = %HTTPoison.Request{
method: :post,
url: "https://my.website.com",
body: "{\"foo\": 3}",
- headers: [{"Accept", "application/json"}]
-}
+ headers: [{"Accept", "application/json"}]
+}
-request(request)
request(method, url, body \\ "",
Examples
-
+request(:post, "https://my.website.com", "{\"foo\": 3}", [{"Accept", "application/json"}])
request(:post, "https://my.website.com", "{\"foo\": 3}", [{"Accept", "application/json"}])
@openfn
organization and
filters out modules that are known not to be adaptors.# Starting the process
-AdaptorRegistry.start_link()
+AdaptorRegistry.start_link()
# Getting a list of all adaptors
-Lightning.AdaptorRegistry.AdaptorRegistry.all()
start_link/1
.:continue
to return before the adaptors have been queried.
+start_link/1
.:continue
to return before the adaptors have been queried.
This does mean that the first call to the process will be delayed until
the handle_continue/2
has finished.all/1
and also internally when the modules are being queried. NPM can
@@ -432,10 +432,10 @@ resolve_package_name(package_name)
-
+iex> resolve_package_name("@openfn/language-salesforce@1.2.3")
-{ "@openfn/language-salesforce", "1.2.3" }
-iex> resolve_package_name("@openfn/language-salesforce")
-{ "@openfn/language-salesforce", nil }
iex> resolve_package_name("@openfn/language-salesforce@1.2.3")
+{ "@openfn/language-salesforce", "1.2.3" }
+iex> resolve_package_name("@openfn/language-salesforce")
+{ "@openfn/language-salesforce", nil }
query(session, content)
-{:ok, session}
if the query was successful, otherwise :error
.
+iex> AiAssistant.query(session, "fn()")
-{:ok, session}
{:ok, session}
if the query was successful, otherwise :error
.iex> AiAssistant.query(session, "fn()")
+{:ok, session}
delete(client, url, opts)
-request/1
or request/2
for options definition.
+delete("/users")
-delete("/users", query: [scope: "admin"])
-delete(client, "/users")
-delete(client, "/users", query: [scope: "admin"])
-delete(client, "/users", body: %{name: "Jon"})
request/1
or request/2
for options definition.delete("/users")
+delete("/users", query: [scope: "admin"])
+delete(client, "/users")
+delete(client, "/users", query: [scope: "admin"])
+delete(client, "/users", body: %{name: "Jon"})
delete!(client, url, opts)
-request!/1
or request!/2
for options definition.
+delete!("/users")
-delete!("/users", query: [scope: "admin"])
-delete!(client, "/users")
-delete!(client, "/users", query: [scope: "admin"])
-delete!(client, "/users", body: %{name: "Jon"})
request!/1
or request!/2
for options definition.delete!("/users")
+delete!("/users", query: [scope: "admin"])
+delete!(client, "/users")
+delete!(client, "/users", query: [scope: "admin"])
+delete!(client, "/users", body: %{name: "Jon"})
get(client, url, opts)
-request/1
or request/2
for options definition.
+get("/users")
-get("/users", query: [scope: "admin"])
-get(client, "/users")
-get(client, "/users", query: [scope: "admin"])
-get(client, "/users", body: %{name: "Jon"})
request/1
or request/2
for options definition.get("/users")
+get("/users", query: [scope: "admin"])
+get(client, "/users")
+get(client, "/users", query: [scope: "admin"])
+get(client, "/users", body: %{name: "Jon"})
get!(client, url, opts)
-request!/1
or request!/2
for options definition.
+get!("/users")
-get!("/users", query: [scope: "admin"])
-get!(client, "/users")
-get!(client, "/users", query: [scope: "admin"])
-get!(client, "/users", body: %{name: "Jon"})
request!/1
or request!/2
for options definition.get!("/users")
+get!("/users", query: [scope: "admin"])
+get!(client, "/users")
+get!(client, "/users", query: [scope: "admin"])
+get!(client, "/users", body: %{name: "Jon"})
head(client, url, opts)
-request/1
or request/2
for options definition.
+head("/users")
-head("/users", query: [scope: "admin"])
-head(client, "/users")
-head(client, "/users", query: [scope: "admin"])
-head(client, "/users", body: %{name: "Jon"})
request/1
or request/2
for options definition.head("/users")
+head("/users", query: [scope: "admin"])
+head(client, "/users")
+head(client, "/users", query: [scope: "admin"])
+head(client, "/users", body: %{name: "Jon"})
head!(client, url, opts)
-request!/1
or request!/2
for options definition.
+head!("/users")
-head!("/users", query: [scope: "admin"])
-head!(client, "/users")
-head!(client, "/users", query: [scope: "admin"])
-head!(client, "/users", body: %{name: "Jon"})
request!/1
or request!/2
for options definition.head!("/users")
+head!("/users", query: [scope: "admin"])
+head!(client, "/users")
+head!(client, "/users", query: [scope: "admin"])
+head!(client, "/users", body: %{name: "Jon"})
options(client, url, opts)
-request/1
or request/2
for options definition.
+options("/users")
-options("/users", query: [scope: "admin"])
-options(client, "/users")
-options(client, "/users", query: [scope: "admin"])
-options(client, "/users", body: %{name: "Jon"})
request/1
or request/2
for options definition.options("/users")
+options("/users", query: [scope: "admin"])
+options(client, "/users")
+options(client, "/users", query: [scope: "admin"])
+options(client, "/users", body: %{name: "Jon"})
options!(client, url, opts)
-request!/1
or request!/2
for options definition.
+options!("/users")
-options!("/users", query: [scope: "admin"])
-options!(client, "/users")
-options!(client, "/users", query: [scope: "admin"])
-options!(client, "/users", body: %{name: "Jon"})
request!/1
or request!/2
for options definition.options!("/users")
+options!("/users", query: [scope: "admin"])
+options!(client, "/users")
+options!(client, "/users", query: [scope: "admin"])
+options!(client, "/users", body: %{name: "Jon"})
patch(client, url, body, opts)
-request/1
or request/2
for options definition.
+patch("/users", %{name: "Jon"})
-patch("/users", %{name: "Jon"}, query: [scope: "admin"])
-patch(client, "/users", %{name: "Jon"})
-patch(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
request/1
or request/2
for options definition.patch("/users", %{name: "Jon"})
+patch("/users", %{name: "Jon"}, query: [scope: "admin"])
+patch(client, "/users", %{name: "Jon"})
+patch(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
patch!(client, url, body, opts)
-request!/1
or request!/2
for options definition.
+patch!("/users", %{name: "Jon"})
-patch!("/users", %{name: "Jon"}, query: [scope: "admin"])
-patch!(client, "/users", %{name: "Jon"})
-patch!(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
request!/1
or request!/2
for options definition.patch!("/users", %{name: "Jon"})
+patch!("/users", %{name: "Jon"}, query: [scope: "admin"])
+patch!(client, "/users", %{name: "Jon"})
+patch!(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
post(client, url, body, opts)
-request/1
or request/2
for options definition.
+post("/users", %{name: "Jon"})
-post("/users", %{name: "Jon"}, query: [scope: "admin"])
-post(client, "/users", %{name: "Jon"})
-post(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
request/1
or request/2
for options definition.post("/users", %{name: "Jon"})
+post("/users", %{name: "Jon"}, query: [scope: "admin"])
+post(client, "/users", %{name: "Jon"})
+post(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
post!(client, url, body, opts)
-request!/1
or request!/2
for options definition.
+post!("/users", %{name: "Jon"})
-post!("/users", %{name: "Jon"}, query: [scope: "admin"])
-post!(client, "/users", %{name: "Jon"})
-post!(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
request!/1
or request!/2
for options definition.post!("/users", %{name: "Jon"})
+post!("/users", %{name: "Jon"}, query: [scope: "admin"])
+post!(client, "/users", %{name: "Jon"})
+post!(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
put(client, url, body, opts)
-request/1
or request/2
for options definition.
+put("/users", %{name: "Jon"})
-put("/users", %{name: "Jon"}, query: [scope: "admin"])
-put(client, "/users", %{name: "Jon"})
-put(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
request/1
or request/2
for options definition.put("/users", %{name: "Jon"})
+put("/users", %{name: "Jon"}, query: [scope: "admin"])
+put(client, "/users", %{name: "Jon"})
+put(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
put!(client, url, body, opts)
-request!/1
or request!/2
for options definition.
+put!("/users", %{name: "Jon"})
-put!("/users", %{name: "Jon"}, query: [scope: "admin"])
-put!(client, "/users", %{name: "Jon"})
-put!(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
request!/1
or request!/2
for options definition.put!("/users", %{name: "Jon"})
+put!("/users", %{name: "Jon"}, query: [scope: "admin"])
+put!(client, "/users", %{name: "Jon"})
+put!(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
request(client \\ %Tesla.Client{}, options)
Examples
-
ExampleApi.request(method: :get, url: "/users/path")
+
+ExampleApi.get("/users/1")
+ExampleApi.post(client, "/users", %{name: "Jon"})ExampleApi.request(method: :get, url: "/users/path")
# use shortcut methods
-ExampleApi.get("/users/1")
-ExampleApi.post(client, "/users", %{name: "Jon"})
trace(client, url, opts)
-request/1
or request/2
for options definition.
+trace("/users")
-trace("/users", query: [scope: "admin"])
-trace(client, "/users")
-trace(client, "/users", query: [scope: "admin"])
-trace(client, "/users", body: %{name: "Jon"})
request/1
or request/2
for options definition.trace("/users")
+trace("/users", query: [scope: "admin"])
+trace(client, "/users")
+trace(client, "/users", query: [scope: "admin"])
+trace(client, "/users", body: %{name: "Jon"})
trace!(client, url, opts)
-request!/1
or request!/2
for options definition.
+trace!("/users")
-trace!("/users", query: [scope: "admin"])
-trace!(client, "/users")
-trace!(client, "/users", query: [scope: "admin"])
-trace!(client, "/users", body: %{name: "Jon"})
request!/1
or request!/2
for options definition.trace!("/users")
+trace!("/users", query: [scope: "admin"])
+trace!(client, "/users")
+trace!(client, "/users", query: [scope: "admin"])
+trace!(client, "/users", body: %{name: "Jon"})
request(request)
Examples
-request = %HTTPoison.Request{
+
+request(request)request = %HTTPoison.Request{
method: :post,
url: "https://my.website.com",
body: "{\"foo\": 3}",
- headers: [{"Accept", "application/json"}]
-}
+ headers: [{"Accept", "application/json"}]
+}
-request(request)
request(method, url, body \\ "",
Examples
-
+request(:post, "https://my.website.com", "{\"foo\": 3}", [{"Accept", "application/json"}])
request(:post, "https://my.website.com", "{\"foo\": 3}", [{"Accept", "application/json"}])
do_in(envs, list)
Examples
-do_in(:dev) do
- IO.puts("This will only be printed in the dev environment")
-end
+
+do_in([:dev, :test]) do
+ IO.puts("This will only be printed in the dev and test environments")
+enddo_in(:dev) do
+ IO.puts("This will only be printed in the dev environment")
+end
-do_in([:dev, :test]) do
- IO.puts("This will only be printed in the dev and test environments")
-end
Logs
Result
struct.{"level":"<<level>>","name":"<<module>>","message":"..."],"time":<<timestamp>>}
{"message":["<<message|filepath|output>>"]}
Result
struct.
There are two kinds of output:
{"level":"<<level>>","name":"<<module>>","message":"..."],"time":<<timestamp>>}
These are usually for general logging, and debugging.
{"message":["<<message|filepath|output>>"]}
The above is the equivalent of the output of a command
diff --git a/Lightning.Config.Bootstrap.html b/Lightning.Config.Bootstrap.html index a56dd6be73..9f3e349e11 100644 --- a/Lightning.Config.Bootstrap.html +++ b/Lightning.Config.Bootstrap.html @@ -143,8 +143,8 @@config/runtime.exs
) file.Sourcing envs
Internally this module uses
Dotenvy.source/1
to source environment variables from the.env
,.env.<config_env>
, and.env.<config_env>.override
files. It also sources the system environment variables.Calling
configure/0
without callingsource_envs/0
orDotenvy.source/2
-first will result in no environment variables being loaded.
Usage:
Lightning.Config.Bootstrap.source_envs()
-Lightning.Config.Bootstrap.configure()
+first will result in no environment variables being loaded.Usage:
Lightning.Config.Bootstrap.source_envs()
+Lightning.Config.Bootstrap.configure()
diff --git a/Lightning.Credentials.html b/Lightning.Credentials.html
index 3fccbc4427..bbe1e9ebdb 100644
--- a/Lightning.Credentials.html
+++ b/Lightning.Credentials.html
@@ -406,8 +406,8 @@ iex> change_credential(credential)
-%Ecto.Changeset{data: %Credential{}}
+iex> change_credential(credential)
+%Ecto.Changeset{data: %Credential{}}
iex> create_credential(%{field: value})
-{:ok, %Credential{}}
+iex> create_credential(%{field: value})
+{:ok, %Credential{}}
-iex> create_credential(%{field: bad_value})
-{:error, %Ecto.Changeset{}}
+iex> create_credential(%{field: bad_value})
+{:error, %Ecto.Changeset{}}
iex> delete_credential(credential)
-{:ok, %Credential{}}
+iex> delete_credential(credential)
+{:ok, %Credential{}}
-iex> delete_credential(credential)
-{:error, %Ecto.Changeset{}}
+iex> delete_credential(credential)
+{:error, %Ecto.Changeset{}}
iex> get_credential!(123)
-%Credential{}
+iex> get_credential!(123)
+%Credential{}
-iex> get_credential!(456)
+iex> get_credential!(456)
** (Ecto.NoResultsError)
iex> has_activity_in_projects?(%Credential{id: some_id})
+iex> has_activity_in_projects?(%Credential{id: some_id})
true
-iex> has_activity_in_projects?(%Credential{id: another_id})
+iex> has_activity_in_projects?(%Credential{id: another_id})
false
@@ -659,11 +659,11 @@ invalid_projects_for_user(credential_id, us
Examples
-iex> can_credential_be_shared_to_user(credential_id, user_id)
-[]
+iex> can_credential_be_shared_to_user(credential_id, user_id)
+[]
-iex> can_credential_be_shared_to_user(credential_id, user_id)
-["52ea8758-6ce5-43d7-912f-6a1e1f11dc55"]
+iex> can_credential_be_shared_to_user(credential_id, user_id)
+["52ea8758-6ce5-43d7-912f-6a1e1f11dc55"]
@@ -703,9 +703,9 @@ list_credentials(project)
Examples
- When given a Project:
iex> list_credentials(%Project{id: 1})
-[%Credential{project_id: 1}, %Credential{project_id: 1}]
When given a User:
iex> list_credentials(%User{id: 123})
-[%Credential{user_id: 123}, %Credential{user_id: 123}]
+ When given a Project:
iex> list_credentials(%Project{id: 1})
+[%Credential{project_id: 1}, %Credential{project_id: 1}]
When given a User:
iex> list_credentials(%User{id: 123})
+[%Credential{user_id: 123}, %Credential{user_id: 123}]
@@ -816,11 +816,11 @@ schedule_credential_deletion(credential)
Examples
-iex> schedule_credential_deletion(%Credential{id: some_id})
-{:ok, %Credential{}}
+iex> schedule_credential_deletion(%Credential{id: some_id})
+{:ok, %Credential{}}
-iex> schedule_credential_deletion(%Credential{})
-{:error, %Ecto.Changeset{}}
+iex> schedule_credential_deletion(%Credential{})
+{:error, %Ecto.Changeset{}}
@@ -877,11 +877,11 @@ update_credential(credential, attrs)
Examples
-iex> update_credential(credential, %{field: new_value})
-{:ok, %Credential{}}
+iex> update_credential(credential, %{field: new_value})
+{:ok, %Credential{}}
-iex> update_credential(credential, %{field: bad_value})
-{:error, %Ecto.Changeset{}}
+iex> update_credential(credential, %{field: bad_value})
+{:error, %Ecto.Changeset{}}
diff --git a/Lightning.Invocation.html b/Lightning.Invocation.html
index 98af5cd7fe..512a9c480f 100644
--- a/Lightning.Invocation.html
+++ b/Lightning.Invocation.html
@@ -519,8 +519,8 @@ change_dataclip(dataclip, attrs \\ %{})
Examples
-iex> change_dataclip(dataclip)
-%Ecto.Changeset{data: %Dataclip{}}
+iex> change_dataclip(dataclip)
+%Ecto.Changeset{data: %Dataclip{}}
@@ -550,8 +550,8 @@ change_step(step, attrs \\ %{})
Examples
-iex> change_step(step)
-%Ecto.Changeset{data: %Step{}}
+iex> change_step(step)
+%Ecto.Changeset{data: %Step{}}
@@ -611,11 +611,11 @@ create_dataclip(attrs \\ %{})
Examples
-iex> create_dataclip(%{field: value})
-{:ok, %Dataclip{}}
+iex> create_dataclip(%{field: value})
+{:ok, %Dataclip{}}
-iex> create_dataclip(%{field: bad_value})
-{:error, %Ecto.Changeset{}}
+iex> create_dataclip(%{field: bad_value})
+{:error, %Ecto.Changeset{}}
@@ -643,11 +643,11 @@ delete_dataclip(dataclip)
Examples
-iex> delete_dataclip(dataclip)
-{:ok, %Dataclip{}}
+iex> delete_dataclip(dataclip)
+{:ok, %Dataclip{}}
-iex> delete_dataclip(dataclip)
-{:error, %Ecto.Changeset{}}
+iex> delete_dataclip(dataclip)
+{:error, %Ecto.Changeset{}}
@@ -682,14 +682,14 @@ get_dataclip(step)
Examples
-iex> get_dataclip("27b73932-16c7-4a72-86a3-85d805ccff98")
-%Dataclip{}
+iex> get_dataclip("27b73932-16c7-4a72-86a3-85d805ccff98")
+%Dataclip{}
-iex> get_dataclip("27b73932-16c7-4a72-86a3-85d805ccff98")
+iex> get_dataclip("27b73932-16c7-4a72-86a3-85d805ccff98")
nil
-iex> get_dataclip(%Step{id: "a uuid"})
-%Dataclip{}
+iex> get_dataclip(%Step{id: "a uuid"})
+%Dataclip{}
@@ -723,10 +723,10 @@ get_dataclip!(id)
Examples
-iex> get_dataclip!(123)
-%Dataclip{}
+iex> get_dataclip!(123)
+%Dataclip{}
-iex> get_dataclip!(456)
+iex> get_dataclip!(456)
** (Ecto.NoResultsError)
@@ -924,10 +924,10 @@ get_step!(id)
Examples
-iex> get_step!(123)
-%Step{}
+iex> get_step!(123)
+%Step{}
-iex> get_step!(456)
+iex> get_step!(456)
** (Ecto.NoResultsError)
@@ -1056,8 +1056,8 @@ list_dataclips()
Examples
-iex> list_dataclips()
-[%Dataclip{}, ...]
+iex> list_dataclips()
+[%Dataclip{}, ...]
@@ -1166,8 +1166,8 @@ list_steps()
Examples
-iex> list_steps()
-[%Step{}, ...]
+iex> list_steps()
+[%Step{}, ...]
@@ -1294,7 +1294,7 @@ search_workorders(project)
Example:
-search_workorders(%Project{id: 1}, %SearchParams{status: ["completed"]})
+search_workorders(%Project{id: 1}, %SearchParams{status: ["completed"]})
@@ -1400,11 +1400,11 @@ update_dataclip(dataclip, attrs)
Examples
-iex> update_dataclip(dataclip, %{field: new_value})
-{:ok, %Dataclip{}}
+iex> update_dataclip(dataclip, %{field: new_value})
+{:ok, %Dataclip{}}
-iex> update_dataclip(dataclip, %{field: bad_value})
-{:error, %Ecto.Changeset{}}
+iex> update_dataclip(dataclip, %{field: bad_value})
+{:error, %Ecto.Changeset{}}
diff --git a/Lightning.Jobs.html b/Lightning.Jobs.html
index f81dfdd73b..7c85e26e9b 100644
--- a/Lightning.Jobs.html
+++ b/Lightning.Jobs.html
@@ -306,8 +306,8 @@ change_job(job, attrs \\ %{})
Examples
-iex> change_job(job)
-%Ecto.Changeset{data: %Job{}}
+iex> change_job(job)
+%Ecto.Changeset{data: %Job{}}
@@ -337,11 +337,11 @@ create_job(attrs \\ %{})
Examples
-iex> create_job(%{field: value})
-{:ok, %Job{}}
+iex> create_job(%{field: value})
+{:ok, %Job{}}
-iex> create_job(%{field: bad_value})
-{:error, %Ecto.Changeset{}}
+iex> create_job(%{field: bad_value})
+{:error, %Ecto.Changeset{}}
@@ -405,10 +405,10 @@ get_job!(id)
Examples
-iex> get_job!(123)
-%Job{}
+iex> get_job!(123)
+%Job{}
-iex> get_job!(456)
+iex> get_job!(456)
** (Ecto.NoResultsError)
@@ -620,11 +620,11 @@ update_job(job, attrs)
Examples
-iex> update_job(job, %{field: new_value})
-{:ok, %Job{}}
+iex> update_job(job, %{field: new_value})
+{:ok, %Job{}}
-iex> update_job(job, %{field: bad_value})
-{:error, %Ecto.Changeset{}}
+iex> update_job(job, %{field: bad_value})
+{:error, %Ecto.Changeset{}}
diff --git a/Lightning.OauthClients.html b/Lightning.OauthClients.html
index 34e95ce9fa..b2f6b5976d 100644
--- a/Lightning.OauthClients.html
+++ b/Lightning.OauthClients.html
@@ -267,8 +267,8 @@ change_client(client, attrs \\ %{})
Examples
-iex> change_client(%OauthClient{}, %{name: "New Client"})
-%Ecto.Changeset{...}
+iex> change_client(%OauthClient{}, %{name: "New Client"})
+%Ecto.Changeset{...}
@@ -353,11 +353,11 @@ delete_client(client)
Examples
-iex> delete_client(client)
-{:ok, %OauthClient{}}
+iex> delete_client(client)
+{:ok, %OauthClient{}}
-iex> delete_client(client)
-{:error, %Ecto.Changeset{}}
+iex> delete_client(client)
+{:error, %Ecto.Changeset{}}
@@ -403,10 +403,10 @@ get_client!(id)
Examples
-iex> get_client!(123)
-%OauthClient{}
+iex> get_client!(123)
+%OauthClient{}
-iex> get_client!(456)
+iex> get_client!(456)
** (Ecto.NoResultsError)
@@ -447,9 +447,9 @@ list_clients(project)
Examples
- When given a Project:
iex> list_clients(%Project{id: 1})
-[%OauthClient{project_id: 1}, %OauthClient{project_id: 1}]
When given a User:
iex> list_clients(%User{id: 123})
-[%OauthClient{user_id: 123}, %OauthClient{user_id: 123}]
+ When given a Project:
iex> list_clients(%Project{id: 1})
+[%OauthClient{project_id: 1}, %OauthClient{project_id: 1}]
When given a User:
iex> list_clients(%User{id: 123})
+[%OauthClient{user_id: 123}, %OauthClient{user_id: 123}]
@@ -489,11 +489,11 @@ update_client(client, attrs)
Examples
-iex> update_client(client, %{field: new_value})
-{:ok, %OauthClient{}}
+iex> update_client(client, %{field: new_value})
+{:ok, %OauthClient{}}
-iex> update_client(client, %{field: bad_value})
-{:error, %Ecto.Changeset{}}
+iex> update_client(client, %{field: bad_value})
+{:error, %Ecto.Changeset{}}
diff --git a/Lightning.Policies.Permissions.html b/Lightning.Policies.Permissions.html
index 97317f1032..01e664edc3 100644
--- a/Lightning.Policies.Permissions.html
+++ b/Lightning.Policies.Permissions.html
@@ -138,13 +138,13 @@
This module defines a unique interface managing authorizations in Lightning.
Users in Lightning have instance-wide and project-wide roles which determine their level of access to resources in the application. Fo rmore details see the documentation.
These authorizations policies are all implemented under the lib/lightning/policies
folder. In that folder you can find 3 files:
- The
users.ex
file has all the policies for the instances wide access levels - The
project_users.ex
file has all the policies for the project wide access levels - The
permissions.ex
file defines the Lightning.Policies.Permissions.can/4
interface. Which is a wrapper around the Bodyguard.permit/4
function.
-We use that interface to be able to harmonize the use of policies accross the entire app.
All the policies are tested in the test/lightning/policies
folder. And the test are written in a way that allows the reader to quickly who can do what in the app.
We have two variants of the Lightning.Policies.Permissions.can/4
interface:
Lightning.Policies.Permissions.can(policy, action, actor, resource)
returns :ok
if the actor can perform the action on the resource and {:error, :unauthorized}
otherwise.Lightning.Policies.Permissions.can?(policy, action, actor, resource)
returns true
if the actor can perform the action on the resource and false
otherwise.
Here is an example of how we the Lightning.Policies.Permissions.can/4
interface to check if the a user can edit a job or not
can_edit_workflow = Lightning.Policies.ProjectUsers |> Lightning.Policies.Permissions.can?(:edit_workflow, socket.assigns.current_user, socket.assigns.project)
+We use that interface to be able to harmonize the use of policies accross the entire app.All the policies are tested in the test/lightning/policies
folder. And the test are written in a way that allows the reader to quickly who can do what in the app.
We have two variants of the Lightning.Policies.Permissions.can/4
interface:
Lightning.Policies.Permissions.can(policy, action, actor, resource)
returns :ok
if the actor can perform the action on the resource and {:error, :unauthorized}
otherwise.Lightning.Policies.Permissions.can?(policy, action, actor, resource)
returns true
if the actor can perform the action on the resource and false
otherwise.
Here is an example of how we the Lightning.Policies.Permissions.can/4
interface to check if the a user can edit a job or not
can_edit_workflow = Lightning.Policies.ProjectUsers |> Lightning.Policies.Permissions.can?(:edit_workflow, socket.assigns.current_user, socket.assigns.project)
-if can_edit_workflow do
+if can_edit_workflow do
# allow user to edit the workflow
-else
+else
# quick user out
-end
+end
@@ -220,11 +220,11 @@ can(policy, action, user, params \\ [])
Examples
-iex> can(Lightning.Policies.Users, :create_workflow, user, project)
+iex> can(Lightning.Policies.Users, :create_workflow, user, project)
:ok
-iex> can(Lightning.Policies.Users, :create_project, user, %{})
-{:error, :unauthorized}
+iex> can(Lightning.Policies.Users, :create_project, user, %{})
+{:error, :unauthorized}
@@ -254,10 +254,10 @@ can?(policy, action, user, params \\ [])
Examples
-iex> can(Lightning.Policies.Users, :create_workflow, user, project)
+iex> can(Lightning.Policies.Users, :create_workflow, user, project)
true
-iex> can(Lightning.Policies.Users, :create_project, user, %{})
+iex> can(Lightning.Policies.Users, :create_project, user, %{})
false
diff --git a/Lightning.Projects.html b/Lightning.Projects.html
index 1d6435c963..3fae874a2d 100644
--- a/Lightning.Projects.html
+++ b/Lightning.Projects.html
@@ -662,8 +662,8 @@ change_project(project, attrs \\ %{})
Examples
-iex> change_project(project)
-%Ecto.Changeset{data: %Project{}}
+iex> change_project(project)
+%Ecto.Changeset{data: %Project{}}
@@ -695,11 +695,11 @@ create_project(attrs \\ %{}, schedule_email
Examples
-iex> create_project(%{field: value})
-{:ok, %Project{}}
+iex> create_project(%{field: value})
+{:ok, %Project{}}
-iex> create_project(%{field: bad_value})
-{:error, %Ecto.Changeset{}}
+iex> create_project(%{field: bad_value})
+{:error, %Ecto.Changeset{}}
@@ -728,11 +728,11 @@ delete_project(project)
Examples
-iex> delete_project(project)
-{:ok, %Project{}}
+iex> delete_project(project)
+{:ok, %Project{}}
-iex> delete_project(project)
-{:error, %Ecto.Changeset{}}
+iex> delete_project(project)
+{:error, %Ecto.Changeset{}}
@@ -797,8 +797,8 @@ export_project(atom, project_id, snapshot_i
Examples
-iex> export_project(:yaml, project_id)
-{:ok, string}
+iex> export_project(:yaml, project_id)
+{:ok, string}
@@ -870,10 +870,10 @@ get_project!(id)
Examples
-iex> get_project!(123)
-%Project{}
+iex> get_project!(123)
+%Project{}
-iex> get_project!(456)
+iex> get_project!(456)
** (Ecto.NoResultsError)
@@ -968,10 +968,10 @@ get_project_user!(id)
Examples
-iex> get_project_user!(123)
-%ProjectUser{}
+iex> get_project_user!(123)
+%ProjectUser{}
-iex> get_project_user!(456)
+iex> get_project_user!(456)
** (Ecto.NoResultsError)
@@ -1001,16 +1001,16 @@ get_project_user_role(user, project)
Examples
-iex> get_project_user_role(user, project)
+iex> get_project_user_role(user, project)
:admin
-iex> get_project_user_role(user, project)
+iex> get_project_user_role(user, project)
:viewer
-iex> get_project_user_role(user, project)
+iex> get_project_user_role(user, project)
:editor
-iex> get_project_user_role(user, project)
+iex> get_project_user_role(user, project)
:owner
@@ -1061,10 +1061,10 @@ get_project_with_users!(id)
Examples
-iex> get_project!(123)
-%Project{}
+iex> get_project!(123)
+%Project{}
-iex> get_project!(456)
+iex> get_project!(456)
** (Ecto.NoResultsError)
@@ -1242,8 +1242,8 @@ list_projects()
Examples
-iex> list_projects()
-[%Project{}, ...]
+iex> list_projects()
+[%Project{}, ...]
@@ -1723,11 +1723,11 @@ update_project(project, attrs)
Examples
-iex> update_project(project, %{field: new_value})
-{:ok, %Project{}}
+iex> update_project(project, %{field: new_value})
+{:ok, %Project{}}
-iex> update_project(project, %{field: bad_value})
-{:error, %Ecto.Changeset{}}
+iex> update_project(project, %{field: bad_value})
+{:error, %Ecto.Changeset{}}
@@ -1755,11 +1755,11 @@ update_project_user(project_user, attrs)
Examples
-iex> update_project_user(project_user, %{field: new_value})
-{:ok, %ProjectUser{}}
+iex> update_project_user(project_user, %{field: new_value})
+{:ok, %ProjectUser{}}
-iex> update_project_user(projectUser, %{field: bad_value})
-{:error, %Ecto.Changeset{}}
+iex> update_project_user(projectUser, %{field: bad_value})
+{:error, %Ecto.Changeset{}}
@@ -1840,8 +1840,8 @@ validate_for_deletion(project, attrs)
Examples
-iex> validate_for_deletion(project)
-%Ecto.Changeset{data: %Project{}}
+iex> validate_for_deletion(project)
+%Ecto.Changeset{data: %Project{}}
diff --git a/Lightning.PromEx.html b/Lightning.PromEx.html
index 8a4caf73eb..e47a3afd1a 100644
--- a/Lightning.PromEx.html
+++ b/Lightning.PromEx.html
@@ -142,24 +142,24 @@
more details regarding configuring PromEx:config :lightning, Lightning.PromEx,
disabled: false,
manual_metrics_start_delay: :no_delay,
- drop_metrics_groups: [],
+ drop_metrics_groups: [],
grafana: :disabled,
metrics_server: :disabled
Add this module to your application supervision tree. It should be one of the first
things that is started so that no Telemetry events are missed. For example, if PromEx
is started after your Repo module, you will miss Ecto's init events and the dashboards
-will be missing some data points:
def start(_type, _args) do
- children = [
+will be missing some data points:def start(_type, _args) do
+ children = [
Lightning.PromEx,
...
- ]
+ ]
...
-end
Update your endpoint.ex
file to expose your metrics (or configure a standalone
+
end
Update your endpoint.ex
file to expose your metrics (or configure a standalone
server using the :metrics_server
config options). Be sure to put this plug before
your Plug.Telemetry
entry so that you can avoid having calls to your /metrics
endpoint create their own metrics and logs which can pollute your logs/metrics given
-that Prometheus will scrape at a regular interval and that can get noisy:
defmodule LightningWeb.Endpoint do
+that Prometheus will scrape at a regular interval and that can get noisy:defmodule LightningWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :lightning
...
@@ -167,7 +167,7 @@
plug PromEx.Plug, prom_ex_module: Lightning.PromEx
...
-end
Update the list of plugins in the plugins/0
function return list to reflect your
+
end
Update the list of plugins in the plugins/0
function return list to reflect your
application's dependencies. Also update the list of dashboards that are to be uploaded
to Grafana in the dashboards/0
function.
diff --git a/Lightning.Repo.html b/Lightning.Repo.html
index 08fe001035..daf2c51684 100644
--- a/Lightning.Repo.html
+++ b/Lightning.Repo.html
@@ -1738,13 +1738,13 @@ transact(fun, opts \\ [])
A small wrapper around Repo.transaction/2
.
Commits the transaction if the lambda returns :ok
or {:ok, result}
,
rolling it back if the lambda returns :error
or {:error, reason}
. In both
-cases, the function returns the result of the lambda.
Example:
Repo.transact(fn ->
- with {:ok, user} <- Accounts.create_user(params),
- {:ok, _log} <- Logs.log_action(:user_registered, user),
- {:ok, _job} <- Mailer.enqueue_email_confirmation(user) do
- {:ok, user}
- end
-end)
From blog post found here
+cases, the function returns the result of the lambda.Example:
Repo.transact(fn ->
+ with {:ok, user} <- Accounts.create_user(params),
+ {:ok, _log} <- Logs.log_action(:user_registered, user),
+ {:ok, _job} <- Mailer.enqueue_email_confirmation(user) do
+ {:ok, user}
+ end
+end)
From blog post found here
diff --git a/Lightning.Runs.Query.html b/Lightning.Runs.Query.html
index cb3b5a08be..d6407b117b 100644
--- a/Lightning.Runs.Query.html
+++ b/Lightning.Runs.Query.html
@@ -241,7 +241,7 @@ eligible_for_claim()
This query does not currently take into account the priority of the run.
To allow for prioritization, the query should be updated to order by
-priority.
eligible_for_claim() |> prepend_order_by([:priority])
+priority.eligible_for_claim() |> prepend_order_by([:priority])
diff --git a/Lightning.Runs.html b/Lightning.Runs.html
index 762787264e..20a6037fea 100644
--- a/Lightning.Runs.html
+++ b/Lightning.Runs.html
@@ -477,7 +477,7 @@ get(id, opts \\ [])
-Get a run by id.
Optionally preload associations by passing a list of atoms to :include
.
Lightning.Runs.get(id, include: [:workflow])
+Get a run by id.
Optionally preload associations by passing a list of atoms to :include
.
Lightning.Runs.get(id, include: [:workflow])
diff --git a/Lightning.Runtime.LogAgent.html b/Lightning.Runtime.LogAgent.html
index 89d4f0758a..960a6766cd 100644
--- a/Lightning.Runtime.LogAgent.html
+++ b/Lightning.Runtime.LogAgent.html
@@ -138,9 +138,9 @@
Agent facility to consume STDOUT/STDERR byte by byte.
Since it works on a byte by byte basis, you will need to perform line-splitting
-yourself.
Usage:
{:ok, log} = LogAgent.start_link()
-"foo" = LogAgent.process_chunk(log, {:stdout, "foo"})
-"foobar" = LogAgent.process_chunk(log, {:stdout, "bar"})
+yourself.Usage:
{:ok, log} = LogAgent.start_link()
+"foo" = LogAgent.process_chunk(log, {:stdout, "foo"})
+"foobar" = LogAgent.process_chunk(log, {:stdout, "bar"})
diff --git a/Lightning.Runtime.RuntimeManager.html b/Lightning.Runtime.RuntimeManager.html
index d3d3b85896..a2a7e88b62 100644
--- a/Lightning.Runtime.RuntimeManager.html
+++ b/Lightning.Runtime.RuntimeManager.html
@@ -146,8 +146,8 @@
Sample:
config :lightining, Elixir.Lightning.Runtime.RuntimeManager,
version: "0.1.0",
start: true,
args: ~w(js/app.js --bundle --target=es2016 --outdir=../priv/static/assets),
-cd: Path.expand("../assets", __DIR__),
-env: %{}
Options:
:version
- the expected runtime version
:start
- flag to start the runtime manager. If false
the GenServer
+
cd: Path.expand("../assets", __DIR__),
+env: %{}
Options:
:version
- the expected runtime version
:start
- flag to start the runtime manager. If false
the GenServer
won't be started
:path
- the path to find the runtime executable at. By
default, it is automatically downloaded and placed inside
the _build
directory of your current app
Overriding the :path
is not recommended, as we will automatically
diff --git a/Lightning.Scrubber.html b/Lightning.Scrubber.html
index 382b75ecd4..7c1cb84600 100644
--- a/Lightning.Scrubber.html
+++ b/Lightning.Scrubber.html
@@ -137,11 +137,11 @@
-Process used to scrub strings of sensitive information.
Can be started via start_link/1
.
{:ok, scrubber} =
- Lightning.Scrubber.start_link(
+Process used to scrub strings of sensitive information.
Can be started via start_link/1
.
{:ok, scrubber} =
+ Lightning.Scrubber.start_link(
samples:
- Lightning.Credentials.sensitive_values_for(credential)
- )
Takes an optional :name
key, in case you need to name the process.
+ Lightning.Credentials.sensitive_values_for(credential)
+ )
Takes an optional :name
key, in case you need to name the process.
diff --git a/Lightning.Storage.GCS.html b/Lightning.Storage.GCS.html
index 233ac99fa3..9626a0ea43 100644
--- a/Lightning.Storage.GCS.html
+++ b/Lightning.Storage.GCS.html
@@ -150,10 +150,10 @@
Example Usage
# Store a file in GCS
-Lightning.Storage.GCS.store("/path/to/source", "destination/path")
+Lightning.Storage.GCS.store("/path/to/source", "destination/path")
# Get a signed URL for the stored file
-{:ok, url} = Lightning.Storage.GCS.get_url("destination/path")
+
{:ok, url} = Lightning.Storage.GCS.get_url("destination/path")
diff --git a/Lightning.Storage.Local.html b/Lightning.Storage.Local.html
index 15622e4ee4..5440a198df 100644
--- a/Lightning.Storage.Local.html
+++ b/Lightning.Storage.Local.html
@@ -156,11 +156,11 @@
Example Usage
# Store a file
-{:ok, filename} =
- Lightning.Storage.Local.store("/path/to/source", "destination/path")
+{:ok, filename} =
+ Lightning.Storage.Local.store("/path/to/source", "destination/path")
# Get the URL for the stored file
-{:ok, url} = Lightning.Storage.Local.get_url("destination/path")
+
{:ok, url} = Lightning.Storage.Local.get_url("destination/path")
diff --git a/Lightning.Storage.ProjectFileDefinition.html b/Lightning.Storage.ProjectFileDefinition.html
index cd0d7bae7c..6964cc0e3b 100644
--- a/Lightning.Storage.ProjectFileDefinition.html
+++ b/Lightning.Storage.ProjectFileDefinition.html
@@ -138,13 +138,13 @@ This module provides functionality for managing the storage and retrieval of project files.
It handles operations related to storing project files, generating URLs for accessing these files, and constructing storage paths for exported files. It serves as an abstraction layer over the underlying storage mechanism provided by the Lightning.Storage
module.
## Functions
store/2
: Stores a file from a given source path into the storage system based on the file's path.get_url/1
: Retrieves the URL for accessing a stored file.storage_path_for_exports/2
: Constructs a storage path for exported files, defaulting to a .zip
extension.## Example Usage
# Store a file
- Lightning.Storage.ProjectFileDefinition.store("/path/to/source", project_file)
+ Lightning.Storage.ProjectFileDefinition.store("/path/to/source", project_file)
# Get a URL for the stored file
- url = Lightning.Storage.ProjectFileDefinition.get_url(project_file)
+ url = Lightning.Storage.ProjectFileDefinition.get_url(project_file)
# Get the storage path for an exported file
- path = Lightning.Storage.ProjectFileDefinition.storage_path_for_exports(project_file)
+ path = Lightning.Storage.ProjectFileDefinition.storage_path_for_exports(project_file)
A TaskWorker with concurrency limits.
A simple concurrency limiter that wraps Task.Supervisor
, which already does
have the ability to specify max_children
; it throws an error when
that limit is exceeded.
To use it, start it like any other process; ideally in your supervision tree.
...,
- {Lightning.TaskWorker, name: :cli_task_worker, max_tasks: 4}
Options
:max_tasks
Defaults to the number of system schedulers available to the vm.Options
:max_tasks
Defaults to the number of system schedulers available to the vm.Validate that only one of the fields is set at a time.
Example:
changeset
-|> validate_exclusive(
- [:source_job_id, :source_trigger_id],
+|> validate_exclusive(
+ [:source_job_id, :source_trigger_id],
"source_job_id and source_trigger_id are mutually exclusive"
-)
+)
Perform a DELETE request.
See request/1
or request/2
for options definition.
delete("/users")
-delete("/users", query: [scope: "admin"])
-delete(client, "/users")
-delete(client, "/users", query: [scope: "admin"])
-delete(client, "/users", body: %{name: "Jon"})
+Perform a DELETE request.
See request/1
or request/2
for options definition.
delete("/users")
+delete("/users", query: [scope: "admin"])
+delete(client, "/users")
+delete(client, "/users", query: [scope: "admin"])
+delete(client, "/users", body: %{name: "Jon"})
Perform a DELETE request.
See request!/1
or request!/2
for options definition.
delete!("/users")
-delete!("/users", query: [scope: "admin"])
-delete!(client, "/users")
-delete!(client, "/users", query: [scope: "admin"])
-delete!(client, "/users", body: %{name: "Jon"})
+Perform a DELETE request.
See request!/1
or request!/2
for options definition.
delete!("/users")
+delete!("/users", query: [scope: "admin"])
+delete!(client, "/users")
+delete!(client, "/users", query: [scope: "admin"])
+delete!(client, "/users", body: %{name: "Jon"})
Perform a GET request.
See request/1
or request/2
for options definition.
get("/users")
-get("/users", query: [scope: "admin"])
-get(client, "/users")
-get(client, "/users", query: [scope: "admin"])
-get(client, "/users", body: %{name: "Jon"})
+Perform a GET request.
See request/1
or request/2
for options definition.
get("/users")
+get("/users", query: [scope: "admin"])
+get(client, "/users")
+get(client, "/users", query: [scope: "admin"])
+get(client, "/users", body: %{name: "Jon"})
Perform a GET request.
See request!/1
or request!/2
for options definition.
get!("/users")
-get!("/users", query: [scope: "admin"])
-get!(client, "/users")
-get!(client, "/users", query: [scope: "admin"])
-get!(client, "/users", body: %{name: "Jon"})
+Perform a GET request.
See request!/1
or request!/2
for options definition.
get!("/users")
+get!("/users", query: [scope: "admin"])
+get!(client, "/users")
+get!(client, "/users", query: [scope: "admin"])
+get!(client, "/users", body: %{name: "Jon"})
Perform a HEAD request.
See request/1
or request/2
for options definition.
head("/users")
-head("/users", query: [scope: "admin"])
-head(client, "/users")
-head(client, "/users", query: [scope: "admin"])
-head(client, "/users", body: %{name: "Jon"})
+Perform a HEAD request.
See request/1
or request/2
for options definition.
head("/users")
+head("/users", query: [scope: "admin"])
+head(client, "/users")
+head(client, "/users", query: [scope: "admin"])
+head(client, "/users", body: %{name: "Jon"})
Perform a HEAD request.
See request!/1
or request!/2
for options definition.
head!("/users")
-head!("/users", query: [scope: "admin"])
-head!(client, "/users")
-head!(client, "/users", query: [scope: "admin"])
-head!(client, "/users", body: %{name: "Jon"})
+Perform a HEAD request.
See request!/1
or request!/2
for options definition.
head!("/users")
+head!("/users", query: [scope: "admin"])
+head!(client, "/users")
+head!(client, "/users", query: [scope: "admin"])
+head!(client, "/users", body: %{name: "Jon"})
Perform a OPTIONS request.
See request/1
or request/2
for options definition.
options("/users")
-options("/users", query: [scope: "admin"])
-options(client, "/users")
-options(client, "/users", query: [scope: "admin"])
-options(client, "/users", body: %{name: "Jon"})
+Perform a OPTIONS request.
See request/1
or request/2
for options definition.
options("/users")
+options("/users", query: [scope: "admin"])
+options(client, "/users")
+options(client, "/users", query: [scope: "admin"])
+options(client, "/users", body: %{name: "Jon"})
Perform a OPTIONS request.
See request!/1
or request!/2
for options definition.
options!("/users")
-options!("/users", query: [scope: "admin"])
-options!(client, "/users")
-options!(client, "/users", query: [scope: "admin"])
-options!(client, "/users", body: %{name: "Jon"})
+Perform a OPTIONS request.
See request!/1
or request!/2
for options definition.
options!("/users")
+options!("/users", query: [scope: "admin"])
+options!(client, "/users")
+options!(client, "/users", query: [scope: "admin"])
+options!(client, "/users", body: %{name: "Jon"})
Perform a PATCH request.
See request/1
or request/2
for options definition.
patch("/users", %{name: "Jon"})
-patch("/users", %{name: "Jon"}, query: [scope: "admin"])
-patch(client, "/users", %{name: "Jon"})
-patch(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
+Perform a PATCH request.
See request/1
or request/2
for options definition.
patch("/users", %{name: "Jon"})
+patch("/users", %{name: "Jon"}, query: [scope: "admin"])
+patch(client, "/users", %{name: "Jon"})
+patch(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
Perform a PATCH request.
See request!/1
or request!/2
for options definition.
patch!("/users", %{name: "Jon"})
-patch!("/users", %{name: "Jon"}, query: [scope: "admin"])
-patch!(client, "/users", %{name: "Jon"})
-patch!(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
+Perform a PATCH request.
See request!/1
or request!/2
for options definition.
patch!("/users", %{name: "Jon"})
+patch!("/users", %{name: "Jon"}, query: [scope: "admin"])
+patch!(client, "/users", %{name: "Jon"})
+patch!(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
Perform a POST request.
See request/1
or request/2
for options definition.
post("/users", %{name: "Jon"})
-post("/users", %{name: "Jon"}, query: [scope: "admin"])
-post(client, "/users", %{name: "Jon"})
-post(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
+Perform a POST request.
See request/1
or request/2
for options definition.
post("/users", %{name: "Jon"})
+post("/users", %{name: "Jon"}, query: [scope: "admin"])
+post(client, "/users", %{name: "Jon"})
+post(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
Perform a POST request.
See request!/1
or request!/2
for options definition.
post!("/users", %{name: "Jon"})
-post!("/users", %{name: "Jon"}, query: [scope: "admin"])
-post!(client, "/users", %{name: "Jon"})
-post!(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
+Perform a POST request.
See request!/1
or request!/2
for options definition.
post!("/users", %{name: "Jon"})
+post!("/users", %{name: "Jon"}, query: [scope: "admin"])
+post!(client, "/users", %{name: "Jon"})
+post!(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
Perform a PUT request.
See request/1
or request/2
for options definition.
put("/users", %{name: "Jon"})
-put("/users", %{name: "Jon"}, query: [scope: "admin"])
-put(client, "/users", %{name: "Jon"})
-put(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
+Perform a PUT request.
See request/1
or request/2
for options definition.
put("/users", %{name: "Jon"})
+put("/users", %{name: "Jon"}, query: [scope: "admin"])
+put(client, "/users", %{name: "Jon"})
+put(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
Perform a PUT request.
See request!/1
or request!/2
for options definition.
put!("/users", %{name: "Jon"})
-put!("/users", %{name: "Jon"}, query: [scope: "admin"])
-put!(client, "/users", %{name: "Jon"})
-put!(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
+Perform a PUT request.
See request!/1
or request!/2
for options definition.
put!("/users", %{name: "Jon"})
+put!("/users", %{name: "Jon"}, query: [scope: "admin"])
+put!(client, "/users", %{name: "Jon"})
+put!(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
ExampleApi.request(method: :get, url: "/users/path")
+ExampleApi.request(method: :get, url: "/users/path")
# use shortcut methods
-ExampleApi.get("/users/1")
-ExampleApi.post(client, "/users", %{name: "Jon"})
+ExampleApi.get("/users/1")
+ExampleApi.post(client, "/users", %{name: "Jon"})
Perform a TRACE request.
See request/1
or request/2
for options definition.
trace("/users")
-trace("/users", query: [scope: "admin"])
-trace(client, "/users")
-trace(client, "/users", query: [scope: "admin"])
-trace(client, "/users", body: %{name: "Jon"})
+Perform a TRACE request.
See request/1
or request/2
for options definition.
trace("/users")
+trace("/users", query: [scope: "admin"])
+trace(client, "/users")
+trace(client, "/users", query: [scope: "admin"])
+trace(client, "/users", body: %{name: "Jon"})
Perform a TRACE request.
See request!/1
or request!/2
for options definition.
trace!("/users")
-trace!("/users", query: [scope: "admin"])
-trace!(client, "/users")
-trace!(client, "/users", query: [scope: "admin"])
-trace!(client, "/users", body: %{name: "Jon"})
+Perform a TRACE request.
See request!/1
or request!/2
for options definition.
trace!("/users")
+trace!("/users", query: [scope: "admin"])
+trace!(client, "/users")
+trace!(client, "/users", query: [scope: "admin"])
+trace!(client, "/users", body: %{name: "Jon"})
Creating a WebhookAuthMethod
without an associated trigger:
iex> create_auth_method(%{valid_attributes}, actor: %User{})
-{:ok, %WebhookAuthMethod{}}
+Creating a WebhookAuthMethod
without an associated trigger:
iex> create_auth_method(%{valid_attributes}, actor: %User{})
+{:ok, %WebhookAuthMethod{}}
-iex> create_auth_method(%{invalid_attributes}, actor: %User{})
-{:error, %Ecto.Changeset{}}
Creating a WebhookAuthMethod
with an associated trigger:
iex> create_auth_method(%Trigger{}, %{valid_attributes}, actor: %User{})
-{:ok, %WebhookAuthMethod{}}
+iex> create_auth_method(%{invalid_attributes}, actor: %User{})
+{:error, %Ecto.Changeset{}}
Creating a WebhookAuthMethod
with an associated trigger:
iex> create_auth_method(%Trigger{}, %{valid_attributes}, actor: %User{})
+{:ok, %WebhookAuthMethod{}}
-iex> create_auth_method(%Trigger{}, %{invalid_attributes}, actor: %User{})
-{:error, %Ecto.Changeset{}}
+
iex> create_auth_method(%Trigger{}, %{invalid_attributes}, actor: %User{})
+{:error, %Ecto.Changeset{}}
Creating a changeset for an API type auth method:
iex> Lightning.Workflows.create_changeset(%WebhookAuthMethod{auth_type: :api}, %{})
-%WebhookAuthMethod{api_key: some_new_api_key}
Creating a changeset for a non-API type auth method:
iex> Lightning.Workflows.create_changeset(%WebhookAuthMethod{auth_type: :other}, %{})
-%WebhookAuthMethod{}
Creating a changeset for an API type auth method:
iex> Lightning.Workflows.create_changeset(%WebhookAuthMethod{auth_type: :api}, %{})
+%WebhookAuthMethod{api_key: some_new_api_key}
Creating a changeset for a non-API type auth method:
iex> Lightning.Workflows.create_changeset(%WebhookAuthMethod{auth_type: :other}, %{})
+%WebhookAuthMethod{}
Successful deletion:
iex> delete_auth_method(%WebhookAuthMethod{id: "some_id"})
-{:ok, %WebhookAuthMethod{}}
Deletion fails due to the item not existing or other conflict:
iex> delete_auth_method(%WebhookAuthMethod{id: "non_existing_id"})
-{:error, reason}
Successful deletion:
iex> delete_auth_method(%WebhookAuthMethod{id: "some_id"})
+{:ok, %WebhookAuthMethod{}}
Deletion fails due to the item not existing or other conflict:
iex> delete_auth_method(%WebhookAuthMethod{id: "non_existing_id"})
+{:error, reason}
When a matching WebhookAuthMethod
is found:
iex> Lightning.Workflows.find_by_api_key("existing_api_key", %Project{id: "existing_project_id"})
-%WebhookAuthMethod{}
When there is no matching WebhookAuthMethod
:
iex> Lightning.Workflows.find_by_api_key("non_existing_api_key", %Project{id: "existing_project_id"})
+When a matching WebhookAuthMethod
is found:
iex> Lightning.Workflows.find_by_api_key("existing_api_key", %Project{id: "existing_project_id"})
+%WebhookAuthMethod{}
When there is no matching WebhookAuthMethod
:
iex> Lightning.Workflows.find_by_api_key("non_existing_api_key", %Project{id: "existing_project_id"})
nil
When a WebhookAuthMethod
with the given ID exists:
iex> Lightning.Workflows.find_by_id!("existing_id")
-%WebhookAuthMethod{}
When there is no WebhookAuthMethod
with the given ID:
iex> Lightning.Workflows.find_by_id!("non_existing_id")
+When a WebhookAuthMethod
with the given ID exists:
iex> Lightning.Workflows.find_by_id!("existing_id")
+%WebhookAuthMethod{}
When there is no WebhookAuthMethod
with the given ID:
iex> Lightning.Workflows.find_by_id!("non_existing_id")
** (Ecto.NoResultsError)
@@ -652,8 +652,8 @@ find_by_username_and_password(username, pas
Examples
-When a matching WebhookAuthMethod
is found and the password is valid:
iex> Lightning.Workflows.find_by_username_and_password("existing_username", "valid_password", %Project{id: "existing_project_id"})
-%WebhookAuthMethod{}
When the username is found but the password is invalid or no matching record is found:
iex> Lightning.Workflows.find_by_username_and_password("existing_username", "invalid_password", %Project{id: "existing_project_id"})
+When a matching WebhookAuthMethod
is found and the password is valid:
iex> Lightning.Workflows.find_by_username_and_password("existing_username", "valid_password", %Project{id: "existing_project_id"})
+%WebhookAuthMethod{}
When the username is found but the password is invalid or no matching record is found:
iex> Lightning.Workflows.find_by_username_and_password("existing_username", "invalid_password", %Project{id: "existing_project_id"})
nil
@@ -702,9 +702,9 @@ list_for_project(project)
Examples
-When the project exists and has associated auth methods:
iex> list_for_project(%Project{id: "existing_project_id"})
-[%WebhookAuthMethod{}, ...]
When the project does not exist or has no associated auth methods:
iex> list_for_project(%Project{id: "non_existing_project_id"})
-[]
+When the project exists and has associated auth methods:
iex> list_for_project(%Project{id: "existing_project_id"})
+[%WebhookAuthMethod{}, ...]
When the project does not exist or has no associated auth methods:
iex> list_for_project(%Project{id: "non_existing_project_id"})
+[]
@@ -752,9 +752,9 @@ list_for_trigger(trigger)
Examples
-When the Trigger
has associated WebhookAuthMethod
s not scheduled for deletion:
iex> Lightning.Workflows.list_for_trigger(%Trigger{id: "existing_trigger_id"})
-[%WebhookAuthMethod{}, ...]
When the Trigger
has no associated WebhookAuthMethod
s or they are all scheduled for deletion:
iex> Lightning.Workflows.list_for_trigger(%Trigger{id: "trigger_without_methods"})
-[]
+When the Trigger
has associated WebhookAuthMethod
s not scheduled for deletion:
iex> Lightning.Workflows.list_for_trigger(%Trigger{id: "existing_trigger_id"})
+[%WebhookAuthMethod{}, ...]
When the Trigger
has no associated WebhookAuthMethod
s or they are all scheduled for deletion:
iex> Lightning.Workflows.list_for_trigger(%Trigger{id: "trigger_without_methods"})
+[]
@@ -807,10 +807,10 @@ perform(job)
Example
-%Oban.Job{
-args: %{"type" => "purge_deleted"}
-}
-|> MyModule.perform()
+%Oban.Job{
+args: %{"type" => "purge_deleted"}
+}
+|> MyModule.perform()
# => {:ok, %{disassociated_count: 2, deleted_count: 2}}
@@ -864,9 +864,9 @@ schedule_for_deletion(webhook_auth_method,
Examples
-When a webhook auth method is successfully scheduled for deletion:
iex> Lightning.Workflows.schedule_for_deletion(%WebhookAuthMethod{id: some_id})
-{:ok, %WebhookAuthMethod{scheduled_deletion: deletion_date}}
When scheduling for deletion fails due to validation errors:
iex> Lightning.Workflows.schedule_for_deletion(%WebhookAuthMethod{})
-{:error, %Ecto.Changeset{}}
+When a webhook auth method is successfully scheduled for deletion:
iex> Lightning.Workflows.schedule_for_deletion(%WebhookAuthMethod{id: some_id})
+{:ok, %WebhookAuthMethod{scheduled_deletion: deletion_date}}
When scheduling for deletion fails due to validation errors:
iex> Lightning.Workflows.schedule_for_deletion(%WebhookAuthMethod{})
+{:error, %Ecto.Changeset{}}
@@ -916,9 +916,9 @@ update_auth_method(webhook_auth_method, att
Examples
-Successful update:
iex> update_auth_method(webhook_auth_method, %{field: new_value}, actor: %User{})
-{:ok, %WebhookAuthMethod{}}
Update fails due to invalid data:
iex> update_auth_method(webhook_auth_method, %{field: bad_value}, actor: %User{})
-{:error, %Ecto.Changeset{}}
+Successful update:
iex> update_auth_method(webhook_auth_method, %{field: new_value}, actor: %User{})
+{:ok, %WebhookAuthMethod{}}
Update fails due to invalid data:
iex> update_auth_method(webhook_auth_method, %{field: bad_value}, actor: %User{})
+{:error, %Ecto.Changeset{}}
@@ -974,9 +974,9 @@ update_trigger_auth_methods(trigger, auth_m
Examples
-Successful association update:
iex> update_trigger_auth_methods(trigger, [webhook_auth_method], actor: %User{})
-{:ok, %Trigger{}}
Update fails due to an invalid changeset:
iex> update_trigger_auth_methods(trigger, [invalid_webhook_auth_method], actor: %User{})
-{:error, %Ecto.Changeset{}}
+Successful association update:
iex> update_trigger_auth_methods(trigger, [webhook_auth_method], actor: %User{})
+{:ok, %Trigger{}}
Update fails due to an invalid changeset:
iex> update_trigger_auth_methods(trigger, [invalid_webhook_auth_method], actor: %User{})
+{:error, %Ecto.Changeset{}}
diff --git a/Lightning.WorkOrders.ExportWorker.html b/Lightning.WorkOrders.ExportWorker.html
index 931f4e7f8a..f2ed8099c4 100644
--- a/Lightning.WorkOrders.ExportWorker.html
+++ b/Lightning.WorkOrders.ExportWorker.html
@@ -138,7 +138,7 @@
This module handles the export of work orders for a given project. The export process is performed asynchronously using the Oban background job system.
## Responsibilities
- Enqueueing Export Jobs: The
enqueue_export/2
function creates and enqueues an Oban job for exporting work orders based on the given project and search parameters. - Processing Exports: The
perform/1
function is the main entry point for executing the export job. It retrieves the project, processes work orders, and handles the export process. - Export Logic: The export logic involves querying work orders, extracting relevant entities, processing logs and dataclips asynchronously, and writing the final export data to files.
- Error Handling: The module includes comprehensive error handling and logging to ensure that issues during the export process are recorded and can be diagnosed.
- Zip File Creation: After processing, the exported files are compressed into a zip file for easy download or further use.
## Usage
- To enqueue an export job, call
enqueue_export/2
with the project and search parameters. - The export process is triggered by Oban and runs in the
history_exports
queue, limited to a single attempt per job.
## Example
# Enqueue an export job
- Lightning.WorkOrders.ExportWorker.enqueue_export(project, search_params)
+ Lightning.WorkOrders.ExportWorker.enqueue_export(project, search_params)
# The job will run in the background and log the status of the export process.
This module is designed to handle potentially large datasets efficiently by using streaming, async processing, and error recovery mechanisms.
diff --git a/Lightning.WorkOrders.html b/Lightning.WorkOrders.html
index 66a63570ff..1c1c7b7e9c 100644
--- a/Lightning.WorkOrders.html
+++ b/Lightning.WorkOrders.html
@@ -451,7 +451,7 @@ create_for(target, multi \\ Multi.new(), op
-
Create a new Work Order.
For a webhook
create_for(trigger, workflow: workflow, dataclip: dataclip)
For a user
create_for(job, workflow: workflow, dataclip: dataclip, user: user)
+Create a new Work Order.
For a webhook
create_for(trigger, workflow: workflow, dataclip: dataclip)
For a user
create_for(job, workflow: workflow, dataclip: dataclip, user: user)
@@ -503,7 +503,7 @@ get(id, opts \\ [])
-Get a Work Order by id.
Optionally preload associations by passing a list of atoms to :include
.
Lightning.WorkOrders.get(id, include: [:runs])
+Get a Work Order by id.
Optionally preload associations by passing a list of atoms to :include
.
Lightning.WorkOrders.get(id, include: [:runs])
diff --git a/Lightning.Workflows.Job.html b/Lightning.Workflows.Job.html
index b87611b1c2..79e8c502b9 100644
--- a/Lightning.Workflows.Job.html
+++ b/Lightning.Workflows.Job.html
@@ -356,17 +356,17 @@ put_workflow(changeset, workflow)
Attaches a workflow to a job, this is useful when you have an unpersisted
Workflow changeset - and want it to be created at the same time as a Job.
Example:
workflow =
- Ecto.Changeset.cast(
- %Lightning.Workflows.Workflow{},
- %{ "project_id" => attrs[:project_id], "id" => Ecto.UUID.generate() },
- [:project_id, :id]
- )
+ Ecto.Changeset.cast(
+ %Lightning.Workflows.Workflow{},
+ %{ "project_id" => attrs[:project_id], "id" => Ecto.UUID.generate() },
+ [:project_id, :id]
+ )
job =
- %Job{}
- |> Ecto.Changeset.change()
- |> Job.put_workflow(workflow)
- |> Job.changeset(attrs)
+
%Job{}
+ |> Ecto.Changeset.change()
+ |> Job.put_workflow(workflow)
+ |> Job.changeset(attrs)
diff --git a/Lightning.Workflows.Presence.html b/Lightning.Workflows.Presence.html
index 5cd868e055..c50ac32251 100644
--- a/Lightning.Workflows.Presence.html
+++ b/Lightning.Workflows.Presence.html
@@ -352,27 +352,27 @@ build_presences_summary(presences, params)<
Examples
-iex> presences = [
-...> %{user: %{id: 1}, joined_at: ~N[2024-07-03 12:00:00], active_sessions: 1},
-...> %{user: %{id: 2}, joined_at: ~N[2024-07-03 12:05:00], active_sessions: 1},
-...> %{user: %{id: 3}, joined_at: ~N[2024-07-03 12:10:00], active_sessions: 1}
-...> ]
-iex> params = %{
-...> current_user_presence: %{user: %{id: 1}, joined_at: ~N[2024-07-03 12:00:00], active_sessions: 1},
-...> current_user: %{id: 1},
-...> view_only_users_ids: [2]
-...> }
-iex> build_presences_summary(presences, params)
-%{
- presences: [
- %{user: %{id: 1}, joined_at: ~N[2024-07-03 12:00:00], active_sessions: 1},
- %{user: %{id: 2}, joined_at: ~N[2024-07-03 12:05:00], active_sessions: 1},
- %{user: %{id: 3}, joined_at: ~N[2024-07-03 12:10:00], active_sessions: 1}
- ],
- prior_user_presence: %{user: %{id: 3}, joined_at: ~N[2024-07-03 12:10:00], active_sessions: 1},
- current_user_presence: %{user: %{id: 1}, joined_at: ~N[2024-07-03 12:00:00], active_sessions: 1},
+iex> presences = [
+...> %{user: %{id: 1}, joined_at: ~N[2024-07-03 12:00:00], active_sessions: 1},
+...> %{user: %{id: 2}, joined_at: ~N[2024-07-03 12:05:00], active_sessions: 1},
+...> %{user: %{id: 3}, joined_at: ~N[2024-07-03 12:10:00], active_sessions: 1}
+...> ]
+iex> params = %{
+...> current_user_presence: %{user: %{id: 1}, joined_at: ~N[2024-07-03 12:00:00], active_sessions: 1},
+...> current_user: %{id: 1},
+...> view_only_users_ids: [2]
+...> }
+iex> build_presences_summary(presences, params)
+%{
+ presences: [
+ %{user: %{id: 1}, joined_at: ~N[2024-07-03 12:00:00], active_sessions: 1},
+ %{user: %{id: 2}, joined_at: ~N[2024-07-03 12:05:00], active_sessions: 1},
+ %{user: %{id: 3}, joined_at: ~N[2024-07-03 12:10:00], active_sessions: 1}
+ ],
+ prior_user_presence: %{user: %{id: 3}, joined_at: ~N[2024-07-03 12:10:00], active_sessions: 1},
+ current_user_presence: %{user: %{id: 1}, joined_at: ~N[2024-07-03 12:00:00], active_sessions: 1},
has_presence_edit_priority: true
-}
+}
@@ -516,8 +516,8 @@ list_presences(topic)
Examples
-iex> Lightning.Workflows.Presence.list_presences("workflow:canvas")
-[%Lightning.Workflows.Presence{user: %User{id: 1}, ...}, ...]
+iex> Lightning.Workflows.Presence.list_presences("workflow:canvas")
+[%Lightning.Workflows.Presence{user: %User{id: 1}, ...}, ...]
@@ -553,12 +553,12 @@ new_user_presence(user, joined_at, active_s
Examples
-iex> Lightning.Workflows.Presence.new_user_presence(%User{id: 1}, 1625597762000000)
-%Lightning.Workflows.Presence{
- user: %User{id: 1},
+iex> Lightning.Workflows.Presence.new_user_presence(%User{id: 1}, 1625597762000000)
+%Lightning.Workflows.Presence{
+ user: %User{id: 1},
joined_at: 1625597762000000,
active_sessions: 0
-}
+}
@@ -636,7 +636,7 @@ track_user_presence(user, topic, pid)
Examples
-iex> Lightning.Workflows.Presence.track_user_presence(%User{id: 1}, "room:lobby", self())
+iex> Lightning.Workflows.Presence.track_user_presence(%User{id: 1}, "room:lobby", self())
:ok
diff --git a/Lightning.Workflows.html b/Lightning.Workflows.html
index c57cdc9a93..18a5b83d94 100644
--- a/Lightning.Workflows.html
+++ b/Lightning.Workflows.html
@@ -444,8 +444,8 @@ change_workflow(workflow, attrs \\ %{})
Examples
-iex> change_workflow(workflow)
-%Ecto.Changeset{data: %Workflow{}}
+iex> change_workflow(workflow)
+%Ecto.Changeset{data: %Workflow{}}
@@ -507,12 +507,12 @@ get_edge_by_trigger(trigger)
Examples
-trigger = %Trigger{id: 1, ...}
-Lightning.Workflows.get_edge_by_trigger(trigger)
+trigger = %Trigger{id: 1, ...}
+Lightning.Workflows.get_edge_by_trigger(trigger)
# => %Edge{source_trigger: %Trigger{}, target_job: %Job{}, ...}
-non_existent_trigger = %Trigger{id: 999, ...}
-Lightning.Workflows.get_edge_by_trigger(non_existent_trigger)
+non_existent_trigger = %Trigger{id: 999, ...}
+Lightning.Workflows.get_edge_by_trigger(non_existent_trigger)
# => nil
@@ -582,10 +582,10 @@ get_trigger_by_webhook(path)
Examples
-Lightning.Workflows.get_trigger_by_webhook("some_path_or_id")
+Lightning.Workflows.get_trigger_by_webhook("some_path_or_id")
# => %Trigger{id: 1, custom_path: "some_path_or_id", ...}
-Lightning.Workflows.get_trigger_by_webhook("non_existent_path_or_id")
+Lightning.Workflows.get_trigger_by_webhook("non_existent_path_or_id")
# => nil
@@ -660,10 +660,10 @@ get_workflow!(id)
Examples
-iex> get_workflow!(123)
-%Workflow{}
+iex> get_workflow!(123)
+%Workflow{}
-iex> get_workflow!(456)
+iex> get_workflow!(456)
** (Ecto.NoResultsError)
@@ -767,8 +767,8 @@ list_workflows()
Examples
-iex> list_workflows()
-[%Workflow{}, ...]
+iex> list_workflows()
+[%Workflow{}, ...]
@@ -798,8 +798,8 @@ mark_for_deletion(workflow, attrs \\ %{})
Examples
-iex> change_request_deletion(workflow)
-%Ecto.Changeset{data: %Workflow{}}
+iex> change_request_deletion(workflow)
+%Ecto.Changeset{data: %Workflow{}}
diff --git a/Lightning.epub b/Lightning.epub
index 48d158d372e00f69c383617e7e507c4e93368594..cd63748b6b872a061f7e670a25051b4e557ce154 100644
GIT binary patch
delta 216103
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