Browse Source

Merge branch 'add-credo'

master
buttercat1791 2 months ago
parent
commit
187501d720
  1. 1
      config/config.exs
  2. 2
      config/runtime.exs
  3. 5
      config/test.exs
  4. 10
      lib/gc_index_relay/nostr.ex
  5. 2
      lib/gc_index_relay/nostr/event.ex
  6. 5
      lib/gc_index_relay/nostr/filter.ex
  7. 22
      lib/gc_index_relay_web/controllers/filter_controller.ex
  8. 18
      mix.exs
  9. 2
      mix.lock
  10. 4
      test/gc_index_relay/nostr/pub_event_test.exs
  11. 2
      test/gc_index_relay_web/controllers/filter_controller_test.exs

1
config/config.exs

@ -11,7 +11,6 @@ config :gc_index_relay,
ecto_repos: [GcIndexRelay.Repo], ecto_repos: [GcIndexRelay.Repo],
generators: [timestamp_type: :utc_datetime] generators: [timestamp_type: :utc_datetime]
# Configure the endpoint # Configure the endpoint
config :gc_index_relay, GcIndexRelayWeb.Endpoint, config :gc_index_relay, GcIndexRelayWeb.Endpoint,
url: [host: "localhost"], url: [host: "localhost"],

2
config/runtime.exs

@ -24,7 +24,7 @@ config :gc_index_relay, GcIndexRelayWeb.Endpoint,
http: [port: String.to_integer(System.get_env("PORT", "4000"))] http: [port: String.to_integer(System.get_env("PORT", "4000"))]
config :gc_index_relay, GcIndexRelay.Repo, config :gc_index_relay, GcIndexRelay.Repo,
hostname: System.get_env("POSTGRES_HOST"), hostname: System.get_env("POSTGRES_HOST") || "localhost",
port: String.to_integer(System.get_env("POSTGRES_PORT") || "5432"), port: String.to_integer(System.get_env("POSTGRES_PORT") || "5432"),
username: System.get_env("POSTGRES_USER"), username: System.get_env("POSTGRES_USER"),
password: System.get_env("POSTGRES_PASSWORD"), password: System.get_env("POSTGRES_PASSWORD"),

5
config/test.exs

@ -1,6 +1,6 @@
import Config import Config
config :gc_index_relay, :start_repo, true config :gc_index_relay, :start_repo, System.get_env("REQUIRE_DB") == "true"
# Configure your database # Configure your database
# #
@ -11,6 +11,9 @@ config :gc_index_relay, GcIndexRelay.Repo,
username: System.get_env("POSTGRES_USER"), username: System.get_env("POSTGRES_USER"),
password: System.get_env("POSTGRES_PASSWORD"), password: System.get_env("POSTGRES_PASSWORD"),
database: "#{System.get_env("POSTGRES_DB")}#{System.get_env("MIX_TEST_PARTITION")}", database: "#{System.get_env("POSTGRES_DB")}#{System.get_env("MIX_TEST_PARTITION")}",
hostname: "localhost",
port: 5432,
show_sensitive_data_on_connection_error: true,
pool: Ecto.Adapters.SQL.Sandbox, pool: Ecto.Adapters.SQL.Sandbox,
pool_size: System.schedulers_online() * 2 pool_size: System.schedulers_online() * 2

10
lib/gc_index_relay/nostr.ex

@ -73,10 +73,12 @@ defmodule GcIndexRelay.Nostr do
""" """
@spec delete_event(PubEvent.t()) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()} @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
with {:ok, binary_id} <- Base.decode16(event.id, case: :lower) do case Base.decode16(event.id, case: :lower) do
Repo.delete(%Event{id: binary_id}) {:ok, binary_id} ->
else Repo.delete(%Event{id: binary_id})
_ -> {:error, :not_found}
_ ->
{:error, :not_found}
end end
end end
end end

2
lib/gc_index_relay/nostr/event.ex

@ -36,7 +36,7 @@ defmodule GcIndexRelay.Nostr.Event do
|> cast_assoc(:tags, with: &Tag.changeset/2) |> cast_assoc(:tags, with: &Tag.changeset/2)
|> validate_required([:id, :pubkey, :created_at, :kind, :sig]) |> validate_required([:id, :pubkey, :created_at, :kind, :sig])
|> validate_number(:kind, greater_than_or_equal_to: 0) |> validate_number(:kind, greater_than_or_equal_to: 0)
|> validate_number(:kind, less_than: 40000) |> validate_number(:kind, less_than: 40_000)
|> unique_constraint(:id, name: :events_pkey) |> unique_constraint(:id, name: :events_pkey)
end end
end end

5
lib/gc_index_relay/nostr/filter.ex

@ -1,4 +1,9 @@
defmodule GcIndexRelay.Nostr.Filter do defmodule GcIndexRelay.Nostr.Filter do
@moduledoc """
An implementation of Nostr filters, including a struct representation and parsing and validation
functions.
"""
alias GcIndexRelay.Nostr.Event alias GcIndexRelay.Nostr.Event
alias GcIndexRelay.Repo alias GcIndexRelay.Repo
alias GcIndexRelay.Nostr.Tag alias GcIndexRelay.Nostr.Tag

22
lib/gc_index_relay_web/controllers/filter_controller.ex

@ -74,10 +74,10 @@ defmodule GcIndexRelayWeb.FilterController do
@spec validate_required_params(map()) :: {:ok, map()} | {:error, String.t()} @spec validate_required_params(map()) :: {:ok, map()} | {:error, String.t()}
def validate_required_params(params) do def validate_required_params(params) do
if not Map.has_key?(params, "limit") do if Map.has_key?(params, "limit") do
{:error, "The filter must specify a limit."}
else
{:ok, params} {:ok, params}
else
{:error, "The filter must specify a limit."}
end end
end end
@ -141,11 +141,7 @@ defmodule GcIndexRelayWeb.FilterController do
|> Enum.reduce_while({:ok, %{}}, fn {key, value}, {:ok, acc} -> |> Enum.reduce_while({:ok, %{}}, fn {key, value}, {:ok, acc} ->
case parse_param(key, value) do case parse_param(key, value) do
{:ok, parsed_value} -> {:ok, parsed_value} ->
out_key = out_key = parse_tag(key)
if byte_size(key) == 1 and
((key >= "a" and key <= "z") or (key >= "A" and key <= "Z")),
do: "#" <> key,
else: key
{:cont, {:ok, Map.put(acc, out_key, parsed_value)}} {:cont, {:ok, Map.put(acc, out_key, parsed_value)}}
@ -155,6 +151,16 @@ defmodule GcIndexRelayWeb.FilterController do
end) end)
end end
# Parse filter keys that represent tags. Note that only single-letter keys are treated as tags;
# all other keys are passed through unchanged.
@spec parse_tag(String.t()) :: String.t()
defp parse_tag(key) do
if byte_size(key) == 1 and
((key >= "a" and key <= "z") or (key >= "A" and key <= "Z")),
do: "#" <> key,
else: key
end
# Parse individual parameter based on its key # Parse individual parameter based on its key
@spec parse_param(String.t(), String.t()) :: {:ok, any()} | {:error, String.t()} @spec parse_param(String.t(), String.t()) :: {:ok, any()} | {:error, String.t()}
defp parse_param("ids", value), do: {:ok, String.split(value, ",")} defp parse_param("ids", value), do: {:ok, String.split(value, ",")}

18
mix.exs

@ -62,7 +62,8 @@ defmodule GcIndexRelay.MixProject do
{:dns_cluster, "~> 0.2.0"}, {:dns_cluster, "~> 0.2.0"},
{:bandit, "~> 1.5"}, {:bandit, "~> 1.5"},
{:lib_secp256k1, "~> 0.7.1"}, {:lib_secp256k1, "~> 0.7.1"},
{:phoenix_swagger, "~> 0.8"} {:phoenix_swagger, "~> 0.8"},
{:credo, "~> 1.7", only: [:dev, :test], runtime: false}
] ]
end end
@ -77,7 +78,6 @@ defmodule GcIndexRelay.MixProject do
setup: ["deps.get", "ecto.setup"], setup: ["deps.get", "ecto.setup"],
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
"ecto.reset": ["ecto.drop", "ecto.setup"], "ecto.reset": ["ecto.drop", "ecto.setup"],
test: test_alias(),
"test.unit": ["test --only unit"], "test.unit": ["test --only unit"],
"test.integration": [ "test.integration": [
"ecto.create --quiet", "ecto.create --quiet",
@ -88,19 +88,9 @@ defmodule GcIndexRelay.MixProject do
"compile --warnings-as-errors", "compile --warnings-as-errors",
"deps.unlock --unused", "deps.unlock --unused",
"format", "format",
"test.integration" "credo",
"test.unit"
] ]
] ]
end end
# Conditionally set up database for tests
# Set REQUIRE_DB=true to run database migrations before tests
# Or use mix test.integration for integration tests with database
defp test_alias do
if System.get_env("REQUIRE_DB") == "true" do
["test.integration"]
else
["test"]
end
end
end end

2
mix.lock

@ -1,6 +1,8 @@
%{ %{
"bandit": {:hex, :bandit, "1.10.1", "6b1f8609d947ae2a74da5bba8aee938c94348634e54e5625eef622ca0bbbb062", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "4b4c35f273030e44268ace53bf3d5991dfc385c77374244e2f960876547671aa"}, "bandit": {:hex, :bandit, "1.10.1", "6b1f8609d947ae2a74da5bba8aee938c94348634e54e5625eef622ca0bbbb062", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "4b4c35f273030e44268ace53bf3d5991dfc385c77374244e2f960876547671aa"},
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
"cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"}, "cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"},
"credo": {:hex, :credo, "1.7.17", "f92b6aa5b26301eaa5a35e4d48ebf5aa1e7094ac00ae38f87086c562caf8a22f", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1eb5645c835f0b6c9b5410f94b5a185057bcf6d62a9c2b476da971cde8749645"},
"db_connection": {:hex, :db_connection, "2.9.0", "a6a97c5c958a2d7091a58a9be40caf41ab496b0701d21e1d1abff3fa27a7f371", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "17d502eacaf61829db98facf6f20808ed33da6ccf495354a41e64fe42f9c509c"}, "db_connection": {:hex, :db_connection, "2.9.0", "a6a97c5c958a2d7091a58a9be40caf41ab496b0701d21e1d1abff3fa27a7f371", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "17d502eacaf61829db98facf6f20808ed33da6ccf495354a41e64fe42f9c509c"},
"decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"},
"dns_cluster": {:hex, :dns_cluster, "0.2.0", "aa8eb46e3bd0326bd67b84790c561733b25c5ba2fe3c7e36f28e88f384ebcb33", [:mix], [], "hexpm", "ba6f1893411c69c01b9e8e8f772062535a4cf70f3f35bcc964a324078d8c8240"}, "dns_cluster": {:hex, :dns_cluster, "0.2.0", "aa8eb46e3bd0326bd67b84790c561733b25c5ba2fe3c7e36f28e88f384ebcb33", [:mix], [], "hexpm", "ba6f1893411c69c01b9e8e8f772062535a4cf70f3f35bcc964a324078d8c8240"},

4
test/gc_index_relay/nostr/pub_event_test.exs

@ -65,14 +65,14 @@ defmodule GcIndexRelay.Nostr.PubEventTest do
id: Base.decode16!(String.duplicate("ab", 32), case: :lower), id: Base.decode16!(String.duplicate("ab", 32), case: :lower),
pubkey: Base.decode16!(String.duplicate("cd", 32), case: :lower), pubkey: Base.decode16!(String.duplicate("cd", 32), case: :lower),
created_at: ~U[2021-12-20 17:46:40Z], created_at: ~U[2021-12-20 17:46:40Z],
kind: 30023, kind: 30_023,
content: "long-form content", content: "long-form content",
sig: Base.decode16!(String.duplicate("ef", 64), case: :lower), sig: Base.decode16!(String.duplicate("ef", 64), case: :lower),
tags: [] tags: []
} }
assert {:ok, pub_event} = PubEvent.from_db(event) assert {:ok, pub_event} = PubEvent.from_db(event)
assert pub_event.kind == 30023 assert pub_event.kind == 30_023
assert pub_event.content == "long-form content" assert pub_event.content == "long-form content"
end end

2
test/gc_index_relay_web/controllers/filter_controller_test.exs

@ -145,7 +145,7 @@ defmodule GcIndexRelayWeb.FilterControllerTest do
conn = post(conn, ~p"/api/events/filter", %{"limit" => 10}) conn = post(conn, ~p"/api/events/filter", %{"limit" => 10})
assert %{"data" => events} = json_response(conn, 200) assert %{"data" => events} = json_response(conn, 200)
assert length(events) >= 1 assert not Enum.empty?(events)
assert length(events) <= 10 assert length(events) <= 10
end end

Loading…
Cancel
Save