From 5ab901725bb6bed9eb1271fccbc70f5283ba1c9e Mon Sep 17 00:00:00 2001 From: Josh Lee Date: Sun, 7 Jul 2024 00:25:57 -0400 Subject: [PATCH] persist chats --- src/chatservice/config/test.exs | 6 - src/chatservice/lib/chatservice.ex | 5 +- .../lib/chatservice/application.ex | 1 - .../lib/chatservice/chat_context.ex | 22 ++++ .../lib/chatservice/chat_context/message.ex | 20 ++++ .../lib/chatservice/chat_server.ex | 26 ++-- src/chatservice/lib/chatservice_web.ex | 1 - .../lib/chatservice_web/gettext.ex | 24 ---- .../priv/gettext/en/LC_MESSAGES/errors.po | 112 ------------------ src/chatservice/priv/gettext/errors.pot | 109 ----------------- .../20240706182811_create_messages.exs | 14 +++ src/chatservice/priv/static/favicon.ico | Bin 152 -> 0 bytes src/chatservice/priv/static/images/logo.svg | 6 - src/chatservice/priv/static/robots.txt | 5 - .../test/chatservice/chats_test.exs | 36 ++++++ .../test/support/fixtures/chats_fixtures.ex | 23 ++++ 16 files changed, 131 insertions(+), 279 deletions(-) create mode 100644 src/chatservice/lib/chatservice/chat_context.ex create mode 100644 src/chatservice/lib/chatservice/chat_context/message.ex delete mode 100644 src/chatservice/lib/chatservice_web/gettext.ex delete mode 100644 src/chatservice/priv/gettext/en/LC_MESSAGES/errors.po delete mode 100644 src/chatservice/priv/gettext/errors.pot create mode 100644 src/chatservice/priv/repo/migrations/20240706182811_create_messages.exs delete mode 100644 src/chatservice/priv/static/favicon.ico delete mode 100644 src/chatservice/priv/static/images/logo.svg delete mode 100644 src/chatservice/priv/static/robots.txt create mode 100644 src/chatservice/test/chatservice/chats_test.exs create mode 100644 src/chatservice/test/support/fixtures/chats_fixtures.ex diff --git a/src/chatservice/config/test.exs b/src/chatservice/config/test.exs index 769bec3708..586e85b69b 100644 --- a/src/chatservice/config/test.exs +++ b/src/chatservice/config/test.exs @@ -20,12 +20,6 @@ config :chatservice, ChatServiceWeb.Endpoint, secret_key_base: "cfHSrMhdqQLdzdAiLRZazXjUBlnd12ZuG3ilwKigBsbA58cOWzW0Rm2cUa5oF8ts", server: false -# In test we don't send emails. -config :chatservice, ChatService.Mailer, adapter: Swoosh.Adapters.Test - -# Disable swoosh api client as it is only required for production adapters. -config :swoosh, :api_client, false - # Print only warnings and errors during test config :logger, level: :warning diff --git a/src/chatservice/lib/chatservice.ex b/src/chatservice/lib/chatservice.ex index ff47b8c3c0..4369ca7fd7 100644 --- a/src/chatservice/lib/chatservice.ex +++ b/src/chatservice/lib/chatservice.ex @@ -1,9 +1,6 @@ defmodule ChatService do @moduledoc """ - ChatService keeps the contexts that define your domain - and business logic. + The ChatService supervision tree is in chatservice/application.ex - Contexts are also responsible for managing your data, regardless - if it comes from the database, an external API or others. """ end diff --git a/src/chatservice/lib/chatservice/application.ex b/src/chatservice/lib/chatservice/application.ex index 058835cf05..90e115e8c4 100644 --- a/src/chatservice/lib/chatservice/application.ex +++ b/src/chatservice/lib/chatservice/application.ex @@ -14,7 +14,6 @@ defmodule ChatService.Application do children = [ ChatServiceWeb.Telemetry, ChatService.Repo, - # {DNSCluster, query: Application.get_env(:chatservice, :dns_cluster_query) || :ignore}, {Phoenix.PubSub, name: ChatService.PubSub}, {Registry, keys: :unique, name: ChatService.Registry}, ChatServiceWeb.Endpoint diff --git a/src/chatservice/lib/chatservice/chat_context.ex b/src/chatservice/lib/chatservice/chat_context.ex new file mode 100644 index 0000000000..69d61f0ead --- /dev/null +++ b/src/chatservice/lib/chatservice/chat_context.ex @@ -0,0 +1,22 @@ +defmodule ChatService.ChatContext do + @moduledoc """ + The database/persistence context + """ + + import Ecto.Query, warn: false + alias ChatService.Repo + + alias ChatService.ChatContext.Message + + def list_messages(topic) do + Message + |> where(topic: ^topic) + |> Repo.all() + end + + def create_message(attrs \\ %{}) do + %Message{} + |> Message.changeset(attrs) + |> Repo.insert() + end +end diff --git a/src/chatservice/lib/chatservice/chat_context/message.ex b/src/chatservice/lib/chatservice/chat_context/message.ex new file mode 100644 index 0000000000..fa04c71cb4 --- /dev/null +++ b/src/chatservice/lib/chatservice/chat_context/message.ex @@ -0,0 +1,20 @@ +defmodule ChatService.ChatContext.Message do + @derive {Jason.Encoder, only: [:name, :message, :inserted_at, :topic]} + use Ecto.Schema + import Ecto.Changeset + + schema "messages" do + field :topic, :string + field :name, :string + field :message, :string + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(message, attrs) do + message + |> cast(attrs, [:topic, :name, :message]) + |> validate_required([:topic, :name, :message]) + end +end diff --git a/src/chatservice/lib/chatservice/chat_server.ex b/src/chatservice/lib/chatservice/chat_server.ex index 9431afe394..519c47e0e4 100644 --- a/src/chatservice/lib/chatservice/chat_server.ex +++ b/src/chatservice/lib/chatservice/chat_server.ex @@ -1,5 +1,6 @@ defmodule ChatService.ChatServer do - require OpenTelemetry.Tracer + use GenServer + alias ChatService.ChatContext def start_chat(topic) do case Registry.lookup(ChatService.Registry, topic) do @@ -8,10 +9,6 @@ defmodule ChatService.ChatServer do end end - def start_link(topic) do - GenServer.start_link(__MODULE__, %{}, name: via_tuple(topic)) - end - def list_topics() do ChatService.Registry |> Registry.select([{{:"$1", :_, :_}, [], [{{:"$1"}}]}]) @@ -19,23 +16,30 @@ defmodule ChatService.ChatServer do end def send_message(topic, message) do - OpenTelemetry.Tracer.with_span :send_message do - GenServer.call(via_tuple(topic), {:send_message, message}) - end + GenServer.call(via_tuple(topic), {:send_message, Map.put(message, "topic", topic)}) end def get_messages(topic) do GenServer.call(via_tuple(topic), :get_messages) end - def init(_) do - {:ok, []} + def start_link(topic) do + GenServer.start_link(__MODULE__, topic, name: via_tuple(topic)) + end + + @impl true + def init(topic) do + messages = ChatContext.list_messages(topic) + {:ok, messages} end + @impl true def handle_call({:send_message, message}, _from, state) do - {:reply, :ok, [message | state]} + saved = ChatContext.create_message(message) + {:reply, saved, [saved | state]} end + @impl true def handle_call(:get_messages, _from, state) do {:reply, Enum.reverse(state), state} end diff --git a/src/chatservice/lib/chatservice_web.ex b/src/chatservice/lib/chatservice_web.ex index 484e91a759..4c5ea2ef10 100644 --- a/src/chatservice/lib/chatservice_web.ex +++ b/src/chatservice/lib/chatservice_web.ex @@ -43,7 +43,6 @@ defmodule ChatServiceWeb do layouts: [html: ChatServiceWeb.Layouts] import Plug.Conn - import ChatServiceWeb.Gettext unquote(verified_routes()) end diff --git a/src/chatservice/lib/chatservice_web/gettext.ex b/src/chatservice/lib/chatservice_web/gettext.ex deleted file mode 100644 index 360767e440..0000000000 --- a/src/chatservice/lib/chatservice_web/gettext.ex +++ /dev/null @@ -1,24 +0,0 @@ -defmodule ChatServiceWeb.Gettext do - @moduledoc """ - A module providing Internationalization with a gettext-based API. - - By using [Gettext](https://hexdocs.pm/gettext), - your module gains a set of macros for translations, for example: - - import ChatServiceWeb.Gettext - - # Simple translation - gettext("Here is the string to translate") - - # Plural translation - ngettext("Here is the string to translate", - "Here are the strings to translate", - 3) - - # Domain-based translation - dgettext("errors", "Here is the error message to translate") - - See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage. - """ - use Gettext, otp_app: :chatservice -end diff --git a/src/chatservice/priv/gettext/en/LC_MESSAGES/errors.po b/src/chatservice/priv/gettext/en/LC_MESSAGES/errors.po deleted file mode 100644 index 844c4f5cea..0000000000 --- a/src/chatservice/priv/gettext/en/LC_MESSAGES/errors.po +++ /dev/null @@ -1,112 +0,0 @@ -## `msgid`s in this file come from POT (.pot) files. -## -## Do not add, change, or remove `msgid`s manually here as -## they're tied to the ones in the corresponding POT file -## (with the same domain). -## -## Use `mix gettext.extract --merge` or `mix gettext.merge` -## to merge POT files into PO files. -msgid "" -msgstr "" -"Language: en\n" - -## From Ecto.Changeset.cast/4 -msgid "can't be blank" -msgstr "" - -## From Ecto.Changeset.unique_constraint/3 -msgid "has already been taken" -msgstr "" - -## From Ecto.Changeset.put_change/3 -msgid "is invalid" -msgstr "" - -## From Ecto.Changeset.validate_acceptance/3 -msgid "must be accepted" -msgstr "" - -## From Ecto.Changeset.validate_format/3 -msgid "has invalid format" -msgstr "" - -## From Ecto.Changeset.validate_subset/3 -msgid "has an invalid entry" -msgstr "" - -## From Ecto.Changeset.validate_exclusion/3 -msgid "is reserved" -msgstr "" - -## From Ecto.Changeset.validate_confirmation/3 -msgid "does not match confirmation" -msgstr "" - -## From Ecto.Changeset.no_assoc_constraint/3 -msgid "is still associated with this entry" -msgstr "" - -msgid "are still associated with this entry" -msgstr "" - -## From Ecto.Changeset.validate_length/3 -msgid "should have %{count} item(s)" -msgid_plural "should have %{count} item(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should be %{count} character(s)" -msgid_plural "should be %{count} character(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should be %{count} byte(s)" -msgid_plural "should be %{count} byte(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should have at least %{count} item(s)" -msgid_plural "should have at least %{count} item(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should be at least %{count} character(s)" -msgid_plural "should be at least %{count} character(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should be at least %{count} byte(s)" -msgid_plural "should be at least %{count} byte(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should have at most %{count} item(s)" -msgid_plural "should have at most %{count} item(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should be at most %{count} character(s)" -msgid_plural "should be at most %{count} character(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should be at most %{count} byte(s)" -msgid_plural "should be at most %{count} byte(s)" -msgstr[0] "" -msgstr[1] "" - -## From Ecto.Changeset.validate_number/3 -msgid "must be less than %{number}" -msgstr "" - -msgid "must be greater than %{number}" -msgstr "" - -msgid "must be less than or equal to %{number}" -msgstr "" - -msgid "must be greater than or equal to %{number}" -msgstr "" - -msgid "must be equal to %{number}" -msgstr "" diff --git a/src/chatservice/priv/gettext/errors.pot b/src/chatservice/priv/gettext/errors.pot deleted file mode 100644 index eef2de2ba4..0000000000 --- a/src/chatservice/priv/gettext/errors.pot +++ /dev/null @@ -1,109 +0,0 @@ -## This is a PO Template file. -## -## `msgid`s here are often extracted from source code. -## Add new translations manually only if they're dynamic -## translations that can't be statically extracted. -## -## Run `mix gettext.extract` to bring this file up to -## date. Leave `msgstr`s empty as changing them here has no -## effect: edit them in PO (`.po`) files instead. -## From Ecto.Changeset.cast/4 -msgid "can't be blank" -msgstr "" - -## From Ecto.Changeset.unique_constraint/3 -msgid "has already been taken" -msgstr "" - -## From Ecto.Changeset.put_change/3 -msgid "is invalid" -msgstr "" - -## From Ecto.Changeset.validate_acceptance/3 -msgid "must be accepted" -msgstr "" - -## From Ecto.Changeset.validate_format/3 -msgid "has invalid format" -msgstr "" - -## From Ecto.Changeset.validate_subset/3 -msgid "has an invalid entry" -msgstr "" - -## From Ecto.Changeset.validate_exclusion/3 -msgid "is reserved" -msgstr "" - -## From Ecto.Changeset.validate_confirmation/3 -msgid "does not match confirmation" -msgstr "" - -## From Ecto.Changeset.no_assoc_constraint/3 -msgid "is still associated with this entry" -msgstr "" - -msgid "are still associated with this entry" -msgstr "" - -## From Ecto.Changeset.validate_length/3 -msgid "should have %{count} item(s)" -msgid_plural "should have %{count} item(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should be %{count} character(s)" -msgid_plural "should be %{count} character(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should be %{count} byte(s)" -msgid_plural "should be %{count} byte(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should have at least %{count} item(s)" -msgid_plural "should have at least %{count} item(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should be at least %{count} character(s)" -msgid_plural "should be at least %{count} character(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should be at least %{count} byte(s)" -msgid_plural "should be at least %{count} byte(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should have at most %{count} item(s)" -msgid_plural "should have at most %{count} item(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should be at most %{count} character(s)" -msgid_plural "should be at most %{count} character(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should be at most %{count} byte(s)" -msgid_plural "should be at most %{count} byte(s)" -msgstr[0] "" -msgstr[1] "" - -## From Ecto.Changeset.validate_number/3 -msgid "must be less than %{number}" -msgstr "" - -msgid "must be greater than %{number}" -msgstr "" - -msgid "must be less than or equal to %{number}" -msgstr "" - -msgid "must be greater than or equal to %{number}" -msgstr "" - -msgid "must be equal to %{number}" -msgstr "" diff --git a/src/chatservice/priv/repo/migrations/20240706182811_create_messages.exs b/src/chatservice/priv/repo/migrations/20240706182811_create_messages.exs new file mode 100644 index 0000000000..bbda5e0c9e --- /dev/null +++ b/src/chatservice/priv/repo/migrations/20240706182811_create_messages.exs @@ -0,0 +1,14 @@ +defmodule Chatservice.Repo.Migrations.CreateMessages do + use Ecto.Migration + + def change do + create table(:messages) do + add :topic, :string + add :name, :string + add :message, :string + add :sent_at, :utc_datetime + + timestamps(type: :utc_datetime) + end + end +end diff --git a/src/chatservice/priv/static/favicon.ico b/src/chatservice/priv/static/favicon.ico deleted file mode 100644 index 7f372bfc21cdd8cb47585339d5fa4d9dd424402f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 152 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=@t!V@Ar*{oFEH`~d50E!_s``s q?{G*w(7?#d#v@^nKnY_HKaYb01EZMZjMqTJ89ZJ6T-G@yGywoKK_h|y diff --git a/src/chatservice/priv/static/images/logo.svg b/src/chatservice/priv/static/images/logo.svg deleted file mode 100644 index 9f26babac2..0000000000 --- a/src/chatservice/priv/static/images/logo.svg +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/src/chatservice/priv/static/robots.txt b/src/chatservice/priv/static/robots.txt deleted file mode 100644 index 26e06b5f19..0000000000 --- a/src/chatservice/priv/static/robots.txt +++ /dev/null @@ -1,5 +0,0 @@ -# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file -# -# To ban all spiders from the entire site uncomment the next two lines: -# User-agent: * -# Disallow: / diff --git a/src/chatservice/test/chatservice/chats_test.exs b/src/chatservice/test/chatservice/chats_test.exs new file mode 100644 index 0000000000..df74418ba5 --- /dev/null +++ b/src/chatservice/test/chatservice/chats_test.exs @@ -0,0 +1,36 @@ +defmodule ChatService.ChatsTest do + use ChatService.DataCase + + alias ChatService.ChatContext + + describe "messages" do + alias ChatService.ChatContext.Message + + import ChatService.ChatsFixtures + + @invalid_attrs %{topic: nil, message: nil, name: nil, sent_at: nil} + + test "list_messages/0 returns all messages for topic" do + message = message_fixture() + assert ChatContext.list_messages(message.topic) == [message] + end + + test "create_message/1 with valid data creates a message" do + valid_attrs = %{ + topic: "josh", + message: "some message", + name: "some name", + sent_at: ~U[2024-07-05 18:28:00Z] + } + + assert {:ok, %Message{} = message} = ChatContext.create_message(valid_attrs) + assert message.message == "some message" + assert message.name == "some name" + assert message.sent_at == ~U[2024-07-05 18:28:00Z] + end + + test "create_message/1 with invalid data returns error changeset" do + assert {:error, %Ecto.Changeset{}} = ChatContext.create_message(@invalid_attrs) + end + end +end diff --git a/src/chatservice/test/support/fixtures/chats_fixtures.ex b/src/chatservice/test/support/fixtures/chats_fixtures.ex new file mode 100644 index 0000000000..96ee9c91fb --- /dev/null +++ b/src/chatservice/test/support/fixtures/chats_fixtures.ex @@ -0,0 +1,23 @@ +defmodule ChatService.ChatsFixtures do + @moduledoc """ + This module defines test helpers for creating + entities via the `Chatservice.Chats` context. + """ + + @doc """ + Generate a message. + """ + def message_fixture(attrs \\ %{}) do + {:ok, message} = + attrs + |> Enum.into(%{ + topic: "test", + message: "some message", + name: "some name", + sent_at: ~U[2024-07-05 18:28:00Z] + }) + |> ChatService.ChatContext.create_message() + + message + end +end