Skip to content

Commit a2c188e

Browse files
committed
Allow the user to Add and Delete OpenGraphObjects
1 parent e7b2547 commit a2c188e

File tree

9 files changed

+478
-0
lines changed

9 files changed

+478
-0
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
defmodule OgCatalog.OpenGraphObjects do
2+
@moduledoc """
3+
The OpenGraphObjects context.
4+
"""
5+
6+
import Ecto.Query, warn: false
7+
alias OgCatalog.Repo
8+
9+
alias OgCatalog.OpenGraphObjects.OpenGraphObject
10+
11+
@doc """
12+
Returns the list of open_graph_objects.
13+
14+
## Examples
15+
16+
iex> list_open_graph_objects()
17+
[%OpenGraphObject{}, ...]
18+
19+
"""
20+
def list_open_graph_objects do
21+
from(o in OpenGraphObject, order_by: [desc: o.inserted_at])
22+
|> Repo.all()
23+
end
24+
25+
@doc """
26+
Gets a single open_graph_object.
27+
28+
Raises `Ecto.NoResultsError` if the Open graph object does not exist.
29+
30+
## Examples
31+
32+
iex> get_open_graph_object!(123)
33+
%OpenGraphObject{}
34+
35+
iex> get_open_graph_object!(456)
36+
** (Ecto.NoResultsError)
37+
38+
"""
39+
def get_open_graph_object!(id), do: Repo.get!(OpenGraphObject, id)
40+
41+
@doc """
42+
Creates an open_graph_object.
43+
44+
## Examples
45+
46+
iex> create_open_graph_object(%{field: value})
47+
{:ok, %OpenGraphObject{}}
48+
49+
iex> create_open_graph_object(%{field: bad_value})
50+
{:error, %Ecto.Changeset{}}
51+
52+
"""
53+
def create_open_graph_object(attrs) do
54+
%OpenGraphObject{}
55+
|> OpenGraphObject.changeset(attrs)
56+
|> Repo.insert()
57+
end
58+
59+
@doc """
60+
Updates an open_graph_object.
61+
62+
## Examples
63+
64+
iex> update_open_graph_object(open_graph_object, %{field: new_value})
65+
{:ok, %OpenGraphObject{}}
66+
67+
iex> update_open_graph_object(open_graph_object, %{field: bad_value})
68+
{:error, %Ecto.Changeset{}}
69+
70+
"""
71+
def update_open_graph_object(%OpenGraphObject{} = open_graph_object, attrs) do
72+
open_graph_object
73+
|> OpenGraphObject.update_changeset(attrs)
74+
|> Repo.update()
75+
end
76+
77+
@doc """
78+
Deletes an open_graph_object.
79+
80+
## Examples
81+
82+
iex> delete_open_graph_object(open_graph_object)
83+
{:ok, %OpenGraphObject{}}
84+
85+
iex> delete_open_graph_object(open_graph_object)
86+
{:error, %Ecto.Changeset{}}
87+
88+
"""
89+
def delete_open_graph_object(%OpenGraphObject{} = open_graph_object) do
90+
Repo.delete(open_graph_object)
91+
end
92+
93+
@doc """
94+
Returns an `%Ecto.Changeset{}` for tracking open_graph_object changes.
95+
96+
## Examples
97+
98+
iex> change_open_graph_object(open_graph_object)
99+
%Ecto.Changeset{data: %OpenGraphObject{}}
100+
101+
"""
102+
def change_open_graph_object(%OpenGraphObject{} = open_graph_object, attrs \\ %{}) do
103+
OpenGraphObject.changeset(open_graph_object, attrs)
104+
end
105+
end
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
defmodule OgCatalog.OpenGraphObjects.OpenGraphObject do
2+
use Ecto.Schema
3+
import Ecto.Changeset
4+
5+
@primary_key {:id, :binary_id, autogenerate: true}
6+
@foreign_key_type :binary_id
7+
schema "open_graph_objects" do
8+
field :page_url, :string
9+
field :og_image_url, :string
10+
field :status, Ecto.Enum, values: [:pending, :processing, :processed, :failed], default: :pending
11+
field :error_msg, :string
12+
13+
timestamps(type: :utc_datetime)
14+
end
15+
16+
@doc false
17+
def changeset(open_graph_object, attrs) do
18+
open_graph_object
19+
|> cast(attrs, [:page_url])
20+
|> validate_required([:page_url])
21+
|> validate_format(:page_url, ~r/^https?:\/\/\S+$/)
22+
|> unique_constraint(:page_url)
23+
end
24+
25+
@doc false
26+
def update_changeset(open_graph_object, attrs) do
27+
open_graph_object
28+
|> cast(attrs, [:og_image_url, :status, :error_msg])
29+
|> validate_required([:status])
30+
end
31+
end
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
defmodule OgCatalogWeb.OpenGraphObjectLive.Index do
2+
use OgCatalogWeb, :live_view
3+
4+
alias OgCatalog.OpenGraphObjects
5+
alias OgCatalog.OpenGraphObjects.OpenGraphObject
6+
7+
@impl true
8+
def mount(_params, _session, socket) do
9+
open_graph_object = %OpenGraphObject{}
10+
11+
{:ok,
12+
socket
13+
|> assign(:page_title, "Open graph objects")
14+
|> stream(:open_graph_objects, list_open_graph_objects())
15+
|> assign(:open_graph_object, open_graph_object)
16+
|> assign(:form, to_form(OpenGraphObjects.change_open_graph_object(open_graph_object)))}
17+
end
18+
19+
@impl true
20+
def render(assigns) do
21+
~H"""
22+
<Layouts.app flash={@flash}>
23+
<.header>
24+
{@page_title}
25+
<:subtitle>Manage open graph object records.</:subtitle>
26+
</.header>
27+
28+
<div class="grid gap-8">
29+
<section>
30+
<h2 class="text-xl font-bold">Add New Open Graph Object</h2>
31+
<.form for={@form} id="open_graph_object-form" phx-change="validate" phx-submit="save">
32+
<.input field={@form[:page_url]} type="text" label="Page url" />
33+
<footer>
34+
<.button phx-disable-with="Saving...">Save Open graph object</.button>
35+
</footer>
36+
</.form>
37+
</section>
38+
39+
<section>
40+
<h2 class="text-xl font-bold">Open Graph Objects</h2>
41+
<.table
42+
id="open_graph_objects"
43+
rows={@streams.open_graph_objects}
44+
row_click={fn {_id, open_graph_object} -> JS.navigate(~p"/open_graph_objects/#{open_graph_object}") end}
45+
>
46+
<:col :let={{_id, open_graph_object}} label="Page url">{open_graph_object.page_url}</:col>
47+
<:col :let={{_id, open_graph_object}} label="Status">{open_graph_object.status}</:col>
48+
<:col :let={{_id, open_graph_object}} label="Og image url">
49+
<img :if={open_graph_object.og_image_url}
50+
src={open_graph_object.og_image_url}
51+
alt={open_graph_object.page_url}
52+
class="max-w-[100px] max-h-[100px] object-cover"/>
53+
</:col>
54+
<:action :let={{id, open_graph_object}}>
55+
<.button
56+
phx-click={JS.push("delete", value: %{id: open_graph_object.id}) |> hide("##{id}")}
57+
data-confirm="Are you sure?"
58+
>
59+
Delete
60+
</.button>
61+
</:action>
62+
</.table>
63+
</section>
64+
</div>
65+
</Layouts.app>
66+
"""
67+
end
68+
69+
@impl true
70+
def handle_event("validate", %{"open_graph_object" => params}, socket) do
71+
changeset = OpenGraphObjects.change_open_graph_object(socket.assigns.open_graph_object, params)
72+
{:noreply, assign(socket, form: to_form(changeset, action: :validate))}
73+
end
74+
75+
@impl true
76+
def handle_event("save", %{"open_graph_object" => params}, socket) do
77+
case OpenGraphObjects.create_open_graph_object(params) do
78+
{:ok, _open_graph_object} ->
79+
{:noreply,
80+
socket
81+
|> put_flash(:info, "Open graph object created successfully")
82+
|> stream(:open_graph_objects, list_open_graph_objects(), reset: true)
83+
|> assign(:open_graph_object, %OpenGraphObject{})
84+
|> assign(:form, to_form(OpenGraphObjects.change_open_graph_object(%OpenGraphObject{})))}
85+
86+
{:error, %Ecto.Changeset{} = changeset} ->
87+
{:noreply, assign(socket, form: to_form(changeset, action: :insert))}
88+
end
89+
end
90+
91+
@impl true
92+
def handle_event("delete", %{"id" => id}, socket) do
93+
open_graph_object = OpenGraphObjects.get_open_graph_object!(id)
94+
95+
case OpenGraphObjects.delete_open_graph_object(open_graph_object) do
96+
{:ok, _deleted} ->
97+
{:noreply, stream_delete(socket, :open_graph_objects, open_graph_object)}
98+
{:error, _reason} ->
99+
{:noreply,
100+
socket
101+
|> put_flash(:error, "Could not delete open graph object")}
102+
end
103+
end
104+
105+
defp list_open_graph_objects() do
106+
OpenGraphObjects.list_open_graph_objects()
107+
end
108+
end
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
defmodule OgCatalogWeb.OpenGraphObjectLive.Show do
2+
use OgCatalogWeb, :live_view
3+
4+
alias OgCatalog.OpenGraphObjects
5+
6+
@impl true
7+
def render(assigns) do
8+
~H"""
9+
<Layouts.app flash={@flash}>
10+
<.header>
11+
Open graph object: {@open_graph_object.page_url}
12+
<:subtitle>This is an open_graph_object record from your database.</:subtitle>
13+
<:actions>
14+
<.button navigate={~p"/open_graph_objects"}>
15+
<.icon name="hero-arrow-left" />
16+
</.button>
17+
</:actions>
18+
</.header>
19+
20+
<.list>
21+
<:item title="Page url">{@open_graph_object.page_url}</:item>
22+
<:item title="Og image url">{@open_graph_object.og_image_url}</:item>
23+
<:item title="Status">{@open_graph_object.status}</:item>
24+
<:item title="Error msg">{@open_graph_object.error_msg}</:item>
25+
</.list>
26+
</Layouts.app>
27+
"""
28+
end
29+
30+
@impl true
31+
def mount(%{"id" => id}, _session, socket) do
32+
{:ok,
33+
socket
34+
|> assign(:page_title, "Show Open graph object")
35+
|> assign(:open_graph_object, OpenGraphObjects.get_open_graph_object!(id))}
36+
end
37+
end

lib/og_catalog_web/router.ex

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ defmodule OgCatalogWeb.Router do
1818
pipe_through :browser
1919

2020
get "/", PageController, :home
21+
22+
live "/open_graph_objects", OpenGraphObjectLive.Index, :index
23+
live "/open_graph_objects/:id", OpenGraphObjectLive.Show, :show
2124
end
2225

2326
# Other scopes may use custom stacks.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
defmodule OgCatalog.Repo.Migrations.CreateOpenGraphObjects do
2+
use Ecto.Migration
3+
4+
def change do
5+
create table(:open_graph_objects, primary_key: false) do
6+
add :id, :binary_id, primary_key: true
7+
add :page_url, :string, null: false
8+
add :og_image_url, :string
9+
add :status, :string, null: false, default: "pending"
10+
add :error_msg, :string
11+
12+
timestamps(type: :utc_datetime)
13+
end
14+
15+
create unique_index(:open_graph_objects, [:page_url])
16+
execute("create index open_graph_objects_inserted_at_index on public.open_graph_objects (inserted_at DESC)")
17+
end
18+
end
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
defmodule OgCatalog.OpenGraphObjectsTest do
2+
use OgCatalog.DataCase
3+
4+
alias OgCatalog.OpenGraphObjects
5+
6+
describe "open_graph_objects" do
7+
alias OgCatalog.OpenGraphObjects.OpenGraphObject
8+
9+
import OgCatalog.OpenGraphObjectsFixtures
10+
11+
@valid_page_url "https://www.validurl.com/page.html"
12+
@nil_attrs %{status: nil, error_msg: nil, og_image_url: nil}
13+
14+
test "list_open_graph_objects/0 returns all open_graph_objects" do
15+
open_graph_object = open_graph_object_fixture()
16+
assert OpenGraphObjects.list_open_graph_objects() == [open_graph_object]
17+
end
18+
19+
test "get_open_graph_object!/1 returns the open_graph_object with given id" do
20+
open_graph_object = open_graph_object_fixture()
21+
assert OpenGraphObjects.get_open_graph_object!(open_graph_object.id) == open_graph_object
22+
end
23+
24+
test "create_open_graph_object/1 with valid data creates an open_graph_object" do
25+
valid_attrs = %{page_url: @valid_page_url}
26+
27+
assert {:ok, %OpenGraphObject{} = open_graph_object} = OpenGraphObjects.create_open_graph_object(valid_attrs)
28+
assert open_graph_object.status == :pending
29+
assert open_graph_object.error_msg == nil
30+
assert open_graph_object.page_url == @valid_page_url
31+
assert open_graph_object.og_image_url == nil
32+
end
33+
34+
test "create_open_graph_object/1 with nil data returns error changeset" do
35+
all_nil = %{page_url: nil}
36+
assert {:error, %Ecto.Changeset{}} = OpenGraphObjects.create_open_graph_object(all_nil)
37+
end
38+
39+
test "create_open_graph_object/1 with invalid page_url returns error changeset" do
40+
invalid_page_url = %{page_url: "this_is_an_invalid_url"}
41+
assert {:error, %Ecto.Changeset{}} = OpenGraphObjects.create_open_graph_object(invalid_page_url)
42+
end
43+
44+
test "create_open_graph_object/1 with a duplicated page_url returns error changeset" do
45+
open_graph_object = open_graph_object_fixture()
46+
duplicated_page_url = %{page_url: open_graph_object.page_url}
47+
assert {:error, %Ecto.Changeset{}} = OpenGraphObjects.create_open_graph_object(duplicated_page_url)
48+
end
49+
50+
test "update_open_graph_object/2 with valid data updates the open_graph_object" do
51+
open_graph_object = open_graph_object_fixture()
52+
original_page_url = open_graph_object.page_url
53+
update_attrs = %{status: :processing, error_msg: "some updated error_msg", page_url: @valid_page_url, og_image_url: "some updated og_image_url"}
54+
55+
assert {:ok, %OpenGraphObject{} = open_graph_object} = OpenGraphObjects.update_open_graph_object(open_graph_object, update_attrs)
56+
assert open_graph_object.status == :processing
57+
assert open_graph_object.error_msg == "some updated error_msg"
58+
assert open_graph_object.page_url == original_page_url
59+
assert open_graph_object.og_image_url == "some updated og_image_url"
60+
end
61+
62+
test "update_open_graph_object/2 with invalid data returns error changeset" do
63+
open_graph_object = open_graph_object_fixture()
64+
assert {:error, %Ecto.Changeset{}} = OpenGraphObjects.update_open_graph_object(open_graph_object, @nil_attrs)
65+
assert open_graph_object == OpenGraphObjects.get_open_graph_object!(open_graph_object.id)
66+
end
67+
68+
test "delete_open_graph_object/1 deletes the open_graph_object" do
69+
open_graph_object = open_graph_object_fixture()
70+
assert {:ok, %OpenGraphObject{}} = OpenGraphObjects.delete_open_graph_object(open_graph_object)
71+
assert_raise Ecto.NoResultsError, fn -> OpenGraphObjects.get_open_graph_object!(open_graph_object.id) end
72+
end
73+
74+
test "change_open_graph_object/1 returns an open_graph_object changeset" do
75+
open_graph_object = open_graph_object_fixture()
76+
assert %Ecto.Changeset{} = OpenGraphObjects.change_open_graph_object(open_graph_object)
77+
end
78+
end
79+
end

0 commit comments

Comments
 (0)