Skip to content

Commit

Permalink
backup
Browse files Browse the repository at this point in the history
  • Loading branch information
IvanIvanoff committed Aug 7, 2023
1 parent 0b6635d commit 2794a46
Show file tree
Hide file tree
Showing 8 changed files with 627 additions and 66 deletions.
92 changes: 92 additions & 0 deletions lib/sanbase/queries/autocomplete.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
defmodule Sanbase.Clickhouse.Autocomplete do
alias Sanbase.ClickhouseRepo

def get_data() do
Sanbase.ClickhouseRepo.put_dynamic_repo(Sanbase.ClickhouseRepo.ReadOnly)

result = %{
columns: get_columns(),
functions: get_functions(),
tables: get_tables()
}

{:ok, result}
end

defp get_tables() do
sql = """
SELECT name, engine, partition_key, sorting_key, primary_key
FROM system.tables
WHERE database = 'default'
"""

query_struct = Sanbase.Clickhouse.Query.new(sql, %{})

{:ok, result} =
ClickhouseRepo.query_transform(
query_struct,
fn [
name,
engine,
partition_key,
sorting_key,
primary_key
] ->
%{
name: name,
engine: engine,
partition_key: partition_key,
sorting_key: sorting_key,
primary_key: primary_key
}
end
)

result
end

defp get_columns() do
sql = """
SELECT table, name, type, is_in_partition_key, is_in_sorting_key, is_in_primary_key
FROM system.columns
WHERE database = 'default'
"""

query_struct = Sanbase.Clickhouse.Query.new(sql, %{})

{:ok, result} =
ClickhouseRepo.query_transform(
query_struct,
fn [
table,
name,
type,
is_in_partition_key,
is_in_sorting_key,
is_in_primary_key
] ->
%{
table: table,
name: name,
type: type,
is_in_partition_key: is_in_partition_key == 1,
is_in_sorting_key: is_in_sorting_key == 1,
is_in_primary_key: is_in_primary_key == 1
}
end
)

result
end

defp get_functions() do
sql = """
SELECT name
FROM system.functions
"""

query_struct = Sanbase.Clickhouse.Query.new(sql, %{})
{:ok, result} = ClickhouseRepo.query_transform(query_struct, fn [name] -> %{name: name} end)
result
end
end
91 changes: 77 additions & 14 deletions lib/sanbase/queries/dashboard/dashboard.ex
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ defmodule Sanbase.Queries.Dashboard do
optional(:is_hidden) => boolean()
}

@type option ::
{:preload?, boolean}
| {:preload, [atom()]}
| {:page, non_neg_integer()}
| {:page_size, non_neg_integer()}

@type opts :: [option]

@typedoc ~s"""
The map of arguments that can be passed to the create or update functions.
"""
Expand Down Expand Up @@ -115,40 +123,76 @@ defmodule Sanbase.Queries.Dashboard do
Get a query in order to read or run it.
This can be done by owner or by anyone if the query is public.
"""
@spec get_for_read(dashboard_id, user_id | nil) :: Ecto.Query.t()
def get_for_read(dashboard_id, nil = _querying_user_id) do
@spec get_for_read(dashboard_id, user_id | nil, opts) :: Ecto.Query.t()
def get_for_read(dashboard_id, nil = _querying_user_id, opts) do
from(
q in base_query(),
where: q.id == ^dashboard_id and q.is_public == true,
preload: ^@preload
d in base_query(),
where: d.id == ^dashboard_id and d.is_public == true
)
|> maybe_preload(opts)
end

def get_for_read(dashboard_id, querying_user_id) do
def get_for_read(dashboard_id, querying_user_id, opts) do
from(
q in base_query(),
where: q.id == ^dashboard_id and (q.is_public == true or q.user_id == ^querying_user_id),
preload: ^@preload
d in base_query(),
where: d.id == ^dashboard_id and (d.is_public == true or d.user_id == ^querying_user_id)
)
|> maybe_preload(opts)
end

@doc ~s"""
Get a query in order to mutate it (update or delete).
Only the owner of the query can do that.
"""
@spec get_for_mutation(dashboard_id, user_id) :: Ecto.Query.t()
def get_for_mutation(dashboard_id, querying_user_id) when not is_nil(querying_user_id) do
@spec get_for_mutation(dashboard_id, user_id, opts) :: Ecto.Query.t()
def get_for_mutation(dashboard_id, querying_user_id, opts) when not is_nil(querying_user_id) do
from(
d in base_query(),
where: d.id == ^dashboard_id and d.user_id == ^querying_user_id
)
|> maybe_preload(opts)
end

@spec get_user_dashboards(dashboard_id, user_id | nil, opts) :: Ecto.Query.t()
def get_user_dashboards(user_id, querying_user_id, opts) do
where =
case querying_user_id do
nil ->
# only the public dashboards if no user
dynamic([d], d.user_id == ^user_id and d.is_public == true)

_ ->
# all dashboards if querying_user_id == user_id, the public ones otherwise
dynamic(
[d],
d.user_id == ^user_id and (d.is_public == true or d.user_id == ^querying_user_id)
)
end

from(
q in base_query(),
where: q.id == ^dashboard_id and q.user_id == ^querying_user_id,
d in base_query(),
where: ^where,
preload: ^@preload
)
|> paginate(opts)
|> maybe_preload(opts)
end

def get_visibility_data(dashboard_id) do
from(d in base_query(),
where: d.id == ^dashboard_id,
select: %{
user_id: d.user_id,
is_public: d.is_public,
is_hidden: d.is_hidden
}
)
end

# Entity Behaviour functions

@impl Sanbase.Entity.Behaviour
@spec by_id(non_neg_integer(), Keyword.t()) :: {:ok, t()} | {:error, String.t()}
@spec by_id(non_neg_integer(), opts) :: {:ok, t()} | {:error, String.t()}
def by_id(dashboard_id, opts \\ []) do
query = from(d in __MODULE__, where: d.id == ^dashboard_id)

Expand Down Expand Up @@ -231,4 +275,23 @@ defmodule Sanbase.Queries.Dashboard do
)
|> select([ul], ul.id)
end

defp paginate(query, opts) do
{limit, offset} = Sanbase.Utils.Transform.opts_to_limit_offset(opts)

query
|> limit(^limit)
|> offset(^offset)
end

defp maybe_preload(query, opts) do
case Keyword.get(opts, :preload?, true) do
false ->
query

true ->
preload = Keyword.get(opts, :preload, @preload)
query |> preload(^preload)
end
end
end
110 changes: 61 additions & 49 deletions lib/sanbase/queries/dashboards.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
defmodule Sanbase.Dashboards do
@moduledoc ~s"""
Dashboard is a collection of SQL queries and static widgets.
"""

alias Sanbase.Repo
alias Sanbase.Queries.Query
alias Sanbase.Queries.Dashboard
alias Sanbase.Queries.DashboardQueryMapping

Expand All @@ -12,6 +17,12 @@ defmodule Sanbase.Dashboards do
@type create_dashboard_args :: Dashboard.create_dashboard_args()
@type update_dashboard_args :: Dashboard.update_dashboard_args()

@type visibility_data :: %{
user_id: user_id(),
is_public: boolean(),
is_hidden: boolean()
}

@spec get_dashboard(dashboard_id(), user_id()) :: {:ok, Dashboard.t()} | {:error, String.t()}
def get_dashboard(dashboard_id, querying_user_id) do
query = Dashboard.get_for_read(dashboard_id, querying_user_id)
Expand Down Expand Up @@ -50,10 +61,10 @@ defmodule Sanbase.Dashboards do
{:ok, Dashboard.t()} | {:error, Changeset.t()}
def update_dashboard(dashboard_id, args, querying_user_id) do
Ecto.Multi.new()
|> Ecto.Multi.run(:get_for_mutation, fn _repo, _changes ->
get_for_mutation(dashboard_id, querying_user_id)
|> Ecto.Multi.run(:dashboard_get_for_mutation, fn _repo, _changes ->
dashboard_get_for_mutation(dashboard_id, querying_user_id)
end)
|> Ecto.Multi.run(:update, fn _repo, %{get_for_mutation: struct} ->
|> Ecto.Multi.run(:update, fn _repo, %{dashboard_get_for_mutation: struct} ->
Dashboard.update_changeset(struct, args) |> Repo.update()
end)
|> Repo.transaction()
Expand All @@ -67,75 +78,67 @@ defmodule Sanbase.Dashboards do
{:ok, Dashboard.t()} | {:error, Changeset.t()}
def delete_dashboard(dashboard_id, querying_user_id) do
Ecto.Multi.new()
|> Ecto.Multi.run(:get_for_mutation, fn _repo, _changes ->
get_for_mutation(dashboard_id, querying_user_id)
|> Ecto.Multi.run(:dashboard_get_for_mutation, fn _repo, _changes ->
dashboard_get_for_mutation(dashboard_id, querying_user_id)
end)
|> Ecto.Multi.run(:delete, fn _repo, %{get_for_mutation: struct} ->
|> Ecto.Multi.run(:delete, fn _repo, %{dashboard_get_for_mutation: struct} ->
Repo.delete(struct)
end)
|> Repo.transaction()
|> process_transaction_result(:delete)
end

@doc ~s"""
TODO
Return a boolean showing if the dashboard is public or not.
"""
@spec public?(Dashboard.t()) :: boolean()
def public?(%Dashboard{is_public: is_public}), do: is_public

@doc ~s"""
TODO
Return a list of user dashboards.
If the querying_user_id and user_id are the same, return all dashboards of that user.
If the querying_user_id and user_id are different, or querying_user_id is nil (denoting
anonymous user), return only the public dashboards of the user with id user_id
"""
@spec user_dashboards(user_id()) :: {:ok, [Dashboard.t()]}
def user_dashboards(user_id) do
# query =
# from(
# ds in Dashboard,
# where: ds.user_id == ^user_id
# )

# {:ok, Repo.all(query)}
@spec user_dashboards(user_id(), user_id() | nil, Keyword.t()) :: {:ok, [Dashboard.t()]}
def user_dashboards(user_id, querying_user_id, opts \\ []) do
query = Dashboard.get_user_dashboards(user_id, querying_user_id, opts)

{:ok, Repo.all(query)}
end

@doc ~s"""
TODO
Return the subset of fields of a dashboard that are used to determine the visibility
of the dashboard - who owns it, is it public, is it hidden.
"""
@spec user_public_dashboards(user_id()) :: {:ok, [Dashboard.t()]}
def user_public_dashboards(user_id) do
# query =
# from(
# ds in Dashboard,
# where: ds.user_id == ^user_id and ds.is_public == true
# )

# {:ok, Repo.all(query)}
end
@spec get_visibility_data(dashboard_id()) :: {:ok, visibility_data()} | {:error, String.t()}
def get_visibility_data(dashboard_id) do
query = get_visibility_data(dashboard_id)

@spec get_is_public_and_owner(dashboard_id()) ::
{:ok, %{user_id: user_id(), is_public: boolean()}} | {:error, String.t()}
def get_is_public_and_owner(dashboard_id) do
# result =
# from(d in Dashboard,
# where: d.id == ^dashboard_id,
# select: %{user_id: d.user_id, is_public: d.is_public}
# )
# |> Repo.one()

# case result do
# nil -> {:error, "Dashboard does not exist"}
# data -> {:ok, data}
# end
case Repo.one(query) do
nil -> {:error, "Dashboard does not exist."}
%{} = data -> {:ok, data}
end
end

@doc ~s"""
Add a query to a dashboard.
One query can be added multiple times to a dashboard, with different settings.
"""
@spec add_query_to_dashboard(dashboard_id(), query_id(), Map.t(), user_id()) ::
{:ok, Dashboard.t()} | {:error, String.t()}
{:ok, DashboardQueryMapping.t()} | {:error, String.t()}
def add_query_to_dashboard(dashboard_id, query_id, settings, querying_user_id) do
Ecto.Multi.new()
|> Ecto.Multi.run(:get_for_mutation, fn _repo, _changes ->
# Only to make sure the querying user can mutate the dashboard
get_for_mutation(dashboard_id, querying_user_id)
|> Ecto.Multi.run(:dashboard_get_for_mutation, fn _repo, _changes ->
# Only to make sure the user can mutate the dashboard. Do not preload any assoc
dashboard_get_for_mutation(dashboard_id, querying_user_id, preload?: false)
end)
|> Ecto.Multi.run(:add_query_to_dashboard, fn _repo, %{get_for_mutation: _struct} ->
|> Ecto.Multi.run(:query_get_for_read, fn _repo, _changes ->
# Only to make sure the user can read the query. Do not preload any assoc.
query_get_for_read(query_id, querying_user_id, preload?: false)
end)
|> Ecto.Multi.run(:add_query_to_dashboard, fn _repo, _changes ->
query =
DashboardQueryMapping.create_changeset(%DashboardQueryMapping{}, %{
dashboard_id: dashboard_id,
Expand All @@ -152,15 +155,24 @@ defmodule Sanbase.Dashboards do

# Private functions

defp get_for_mutation(dashboard_id, querying_user_id) do
query = Dashboard.get_for_mutation(dashboard_id, querying_user_id)
defp dashboard_get_for_mutation(dashboard_id, querying_user_id, opts \\ []) do
query = Dashboard.get_for_mutation(dashboard_id, querying_user_id, opts)

case Repo.one(query) do
%Dashboard{} = struct -> {:ok, struct}
nil -> {:error, "Dashboard does not exist or it belongs to another user."}
end
end

defp query_get_for_read(query_id, querying_user_id, opts \\ []) do
query = Query.get_for_read(query_id, querying_user_id)

case Repo.one(query) do
%Query{} = struct -> {:ok, struct}
nil -> {:error, "Query does not exist, or it belongs to another user and is private."}
end
end

defp process_transaction_result({:ok, map}, ok_field),
do: {:ok, map[ok_field]}

Expand Down
Loading

0 comments on commit 2794a46

Please sign in to comment.