Browse Source

Code polish, minor refactors, and type hints

master
buttercat1791 3 months ago
parent
commit
b931109ac8
  1. 8
      lib/gc_index_relay/nostr.ex
  2. 2
      lib/gc_index_relay/nostr/event.ex
  3. 6
      lib/gc_index_relay/nostr/filter.ex
  4. 64
      lib/gc_index_relay/nostr/pub_event.ex
  5. 8
      lib/gc_index_relay_web/controllers/event_controller.ex

8
lib/gc_index_relay/nostr.ex

@ -5,7 +5,7 @@ defmodule GcIndexRelay.Nostr do
## Supported Operations ## Supported Operations
The Nostr context module supports create, read, and delete operations on events, as well as a The Nostr context module supports create, read, and delete operations on events, as well as a
number of query types to find collections of events. Update operations are not supported, sincafter number of query types to find collections of events. Update operations are not supported, since a
Nostr event, once signed, is immutable. Nostr event, once signed, is immutable.
""" """
@ -20,7 +20,8 @@ defmodule GcIndexRelay.Nostr do
Returns: `GcIndexRelay.Nostr.PubEvent` Returns: `GcIndexRelay.Nostr.PubEvent`
""" """
def get_event!(id) when is_binary(id) do @spec get_event(binary()) :: {:ok, PubEvent.t()} | {:error, :not_found}
def get_event(id) when is_binary(id) do
Event Event
|> Repo.get(id) |> Repo.get(id)
|> Repo.preload(:tags) |> Repo.preload(:tags)
@ -38,14 +39,13 @@ defmodule GcIndexRelay.Nostr do
%Event{} %Event{}
|> Event.changeset(Map.from_struct(db_event)) |> Event.changeset(Map.from_struct(db_event))
|> Repo.insert() |> Repo.insert()
else
{:error, reason} -> {:error, reason}
end end
end end
@doc """ @doc """
Deletes a `GcIndexRelay.Nostr.PubEvent` from the database. Deletes a `GcIndexRelay.Nostr.PubEvent` from the database.
""" """
@spec delete_event(PubEvent.t()) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()}
def delete_event(event) when is_struct(event, PubEvent) do def delete_event(event) when is_struct(event, PubEvent) do
db_event = PubEvent.to_db(event) db_event = PubEvent.to_db(event)

2
lib/gc_index_relay/nostr/event.ex

@ -21,12 +21,10 @@ defmodule GcIndexRelay.Nostr.Event do
@primary_key {:id, :binary_id, autogenerate: false} @primary_key {:id, :binary_id, autogenerate: false}
schema "events" do schema "events" do
# 32-bytes lowercase hex-encoded
field :pubkey, :binary field :pubkey, :binary
field :created_at, :utc_datetime field :created_at, :utc_datetime
field :kind, :integer field :kind, :integer
field :content, :string field :content, :string
# 64 bytes lowercase hex-encoded
field :sig, :binary field :sig, :binary
has_many :tags, GcIndexRelay.Nostr.Tag has_many :tags, GcIndexRelay.Nostr.Tag
end end

6
lib/gc_index_relay/nostr/filter.ex

@ -46,8 +46,6 @@ defmodule GcIndexRelay.Nostr.Filter do
until: until, until: until,
limit: limit limit: limit
}} }}
else
{:error, reason} -> {:error, reason}
end end
end end
@ -139,8 +137,6 @@ defmodule GcIndexRelay.Nostr.Filter do
with :ok <- validate_tag_keys(tags), with :ok <- validate_tag_keys(tags),
:ok <- validate_tag_values(tags) do :ok <- validate_tag_values(tags) do
{:ok, tags} {:ok, tags}
else
{:error, reason} -> {:error, reason}
end end
end end
@ -257,7 +253,7 @@ defmodule GcIndexRelay.Nostr.Filter do
@spec apply_authors(Ecto.Query.t(), [String.t()] | nil) :: Ecto.Query.t() @spec apply_authors(Ecto.Query.t(), [String.t()] | nil) :: Ecto.Query.t()
defp apply_authors(query, nil), do: query defp apply_authors(query, nil), do: query
defp apply_authors(query, []), do: query defp apply_authors(query, []), do: query
defp apply_authors(query, authors), do: where(query, [e], e.author in ^authors) defp apply_authors(query, authors), do: where(query, [e], e.pubkey in ^authors)
@spec apply_kinds(Ecto.Query.t(), [integer()] | nil) :: Ecto.Query.t() @spec apply_kinds(Ecto.Query.t(), [integer()] | nil) :: Ecto.Query.t()
defp apply_kinds(query, nil), do: query defp apply_kinds(query, nil), do: query

64
lib/gc_index_relay/nostr/pub_event.ex

@ -12,42 +12,53 @@ defmodule GcIndexRelay.Nostr.PubEvent do
@derive Jason.Encoder @derive Jason.Encoder
defstruct [:id, :pubkey, :created_at, :kind, :tags, :content, :sig] defstruct [:id, :pubkey, :created_at, :kind, :tags, :content, :sig]
@type t :: %__MODULE__{
id: binary(),
pubkey: binary(),
created_at: integer(),
kind: integer(),
tags: [[String.t()]],
content: String.t(),
sig: binary()
}
@doc """ @doc """
Converts a `GcIndexRelay.Nostr.PubEvent` to its corresponding `GcIndexRelay.Nostr.Event` and Converts a `GcIndexRelay.Nostr.PubEvent` to its corresponding `GcIndexRelay.Nostr.Event` and
`GcIndexRelay.Nostr.Tag` representations. `GcIndexRelay.Nostr.Tag` representations.
Returns an Ecto changeset for `GcIndexRelay.Nostr.Event`. Returns an Ecto for `GcIndexRelay.Nostr.Event`.
""" """
@spec to_db(t()) :: Ecto.Schema.t()
def to_db(%__MODULE__{tags: tags} = pub_event) do def to_db(%__MODULE__{tags: tags} = pub_event) do
%Event{to_event(pub_event) | tags: to_tags(tags)} %Event{to_event(pub_event) | tags: to_tags(tags)}
end end
defp to_event(%__MODULE__{ @spec to_event(t()) :: Ecto.Schema.t()
defp to_event(%__MODULE__{} = pub_event)
when is_binary(pub_event.id) and is_binary(pub_event.pubkey) and
is_integer(pub_event.created_at) and is_integer(pub_event.kind) and
is_binary(pub_event.sig) do
with {:ok, id} <- Base.decode16(pub_event.id, case: :lower),
{:ok, pubkey} <- Base.decode16(pub_event.pubkey, case: :lower),
{:ok, signature} <- Base.decode16(pub_event.sig, case: :lower) do
%Event{
id: id, id: id,
pubkey: pubkey, pubkey: pubkey,
created_at: epoch, created_at: DateTime.from_unix!(pub_event.created_at),
kind: kind, kind: pub_event.kind,
content: content, content: pub_event.content,
sig: signature sig: signature
})
when is_binary(id) and is_binary(pubkey) and is_integer(epoch) and is_integer(kind) and
is_binary(signature) do
%Event{
id: Base.decode16(id, case: :lower),
pubkey: Base.decode16(pubkey, case: :lower),
created_at: DateTime.from_unix!(epoch),
kind: kind,
content: content,
sig: Base.decode16(signature, case: :lower)
} }
else
error -> {:error, error}
end end
defp to_tags(tags) when is_list(tags) do
for t <- tags, do: to_tag(t)
end end
defp to_tag(tag) when is_list(tag) do @spec to_tags([[String.t()]]) :: [Ecto.Schema.t()]
[name | values] = tag defp to_tags(tags) when is_list(tags) do
for t <- tags do
[name | values] = t
# Single-element tags will cause a crash
[value | rest] = values [value | rest] = values
%Tag{ %Tag{
@ -56,13 +67,17 @@ defmodule GcIndexRelay.Nostr.PubEvent do
additional_values: rest additional_values: rest
} }
end end
end
@doc """ @doc """
Converts the DB representations of `GcIndexRelay.Nostr.Event` and `GcIndexRelay.Nostr.Tag` to the Converts the DB representations of `GcIndexRelay.Nostr.Event` and `GcIndexRelay.Nostr.Tag` to the
domain representation `GcIndexRelay.Nostr.PubEvent`. domain representation `GcIndexRelay.Nostr.PubEvent`.
""" """
@spec from_db(struct()) :: {:ok, t()} | {:error, :not_found}
def from_db(event) when is_nil(event), do: {:error, :not_found}
def from_db(%Event{tags: tags} = event) when is_struct(event, Event) and is_list(tags) do def from_db(%Event{tags: tags} = event) when is_struct(event, Event) and is_list(tags) do
%{from_event(event) | tags: from_tags(tags)} {:ok, %{from_event(event) | tags: from_tags(tags)}}
end end
defp from_event(%Event{} = event) when is_struct(event, Event) do defp from_event(%Event{} = event) when is_struct(event, Event) do
@ -77,11 +92,6 @@ defmodule GcIndexRelay.Nostr.PubEvent do
end end
defp from_tags(tags) when is_list(tags) do defp from_tags(tags) when is_list(tags) do
for t <- tags, do: from_tag(t) for t <- tags, do: [t.name, t.value | t.additional_values]
end
defp from_tag(%Tag{name: name, value: value, additional_values: rest})
when is_binary(name) and is_binary(value) and is_list(rest) do
[name | [value | rest]]
end end
end end

8
lib/gc_index_relay_web/controllers/event_controller.ex

@ -3,6 +3,7 @@ defmodule GcIndexRelayWeb.EventController do
alias GcIndexRelay.Nostr alias GcIndexRelay.Nostr
alias GcIndexRelay.Nostr.Event alias GcIndexRelay.Nostr.Event
alias GcIndexRelay.Nostr.PubEvent
action_fallback GcIndexRelayWeb.FallbackController action_fallback GcIndexRelayWeb.FallbackController
@ -18,14 +19,13 @@ defmodule GcIndexRelayWeb.EventController do
def show(conn, %{"id" => id}) do def show(conn, %{"id" => id}) do
# TODO: Add 404 for event not found in FallbackController # TODO: Add 404 for event not found in FallbackController
event = Nostr.get_event!(id) event = Nostr.get_event(id)
render(conn, :show, event: event) render(conn, :show, event: event)
end end
def delete(conn, %{"id" => id}) do def delete(conn, %{"id" => id}) do
event = Nostr.get_event!(id) with {:ok, %PubEvent{} = pub_event} <- Nostr.get_event(id),
{:ok, %Event{} = _} <- Nostr.delete_event(pub_event) do
with {:ok, %Event{}} <- Nostr.delete_event(event) do
send_resp(conn, :no_content, "") send_resp(conn, :no_content, "")
end end
end end

Loading…
Cancel
Save