-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create people migration #162
Changes from all commits
1e702cf
0a7278b
2398e5b
211708f
6797530
8cfd3a9
c1e015c
cbb765b
bff37c1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
alias App.{Repo, Item, Tag, ItemTag} | ||
alias App.{Repo, Item, Tag, ItemTag, Person} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
defmodule App.Person do | ||
use Ecto.Schema | ||
import Ecto.Changeset | ||
alias App.{Item, Repo, Tag} | ||
alias __MODULE__ | ||
# https://hexdocs.pm/phoenix/Phoenix.Param.html | ||
@derive {Phoenix.Param, key: :person_id} | ||
|
||
@primary_key {:person_id, :id, autogenerate: false} | ||
schema "people" do | ||
field :name, :string | ||
|
||
has_many :items, Item, foreign_key: :person_id | ||
has_many :tags, Tag, foreign_key: :person_id | ||
timestamps() | ||
end | ||
|
||
@doc false | ||
def changeset(person, attrs \\ %{}) do | ||
person | ||
|> cast(attrs, [:person_id, :name]) | ||
|> validate_required([:person_id, :name]) | ||
|> unique_constraint(:name, name: :people_name_index) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't say I understand why the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm thinking of using the name later on in the UI to select people to add to groups. If the name is not unique we won't be able to distinguish between users. Maybe it should be called |
||
end | ||
|
||
def create_person(attrs) do | ||
%Person{} | ||
|> changeset(attrs) | ||
|> Repo.insert() | ||
end | ||
|
||
def get_person!(person_id), do: Repo.get!(Person, person_id) | ||
|
||
def update_person(%Person{} = person, attrs) do | ||
person | ||
|> Person.changeset(attrs) | ||
|> Repo.update() | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
defmodule AppWeb.ProfileController do | ||
use AppWeb, :controller | ||
alias App.Person | ||
plug :permission_profile when action in [:edit, :update] | ||
|
||
def edit(conn, %{"person_id" => person_id}) do | ||
profile = Person.get_person!(person_id) | ||
changeset = Person.changeset(profile) | ||
render(conn, "edit.html", profile: profile, changeset: changeset) | ||
end | ||
|
||
def update( | ||
conn, | ||
%{"person_id" => person_id, "person" => person_params} | ||
) do | ||
profile = Person.get_person!(person_id) | ||
|
||
case Person.update_person(profile, person_params) do | ||
{:ok, _person} -> | ||
conn | ||
|> put_flash(:info, "Person updated successfully.") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guessing this "Person updated successfully." is auto-generated? 🤖 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The flash messages are not displayed at the moment. |
||
|> redirect(to: "/") | ||
|
||
{:error, %Ecto.Changeset{} = changeset} -> | ||
render(conn, "edit.html", profile: profile, changeset: changeset) | ||
end | ||
end | ||
|
||
defp permission_profile(conn, _opts) do | ||
person_id = conn.assigns[:person][:id] || 0 | ||
|
||
if String.to_integer(conn.params["person_id"]) == person_id do | ||
conn | ||
else | ||
conn | ||
|> put_flash(:info, "You can't access that page") | ||
|> redirect(to: "/") | ||
|> halt() | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,7 +16,9 @@ | |
<div class="container flex flex-wrap justify-between items-center mx-auto"> | ||
|
||
<%= if @loggedin do %> | ||
<a href={Routes.profile_path(@conn, :edit, @person.id)}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add link to the profile picture in the nav bar to the profile edit page |
||
<img src={@person.picture} class="mr-3 h-6 sm:h-9 rounded-full" alt="avatar image"> | ||
</a> | ||
<% else %> | ||
<h1 class="text-white">Hi Friend!</h1> | ||
<% end %> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<.container> | ||
<.h2 class="text-center mt-3">Edit Profile</.h2> | ||
<.p>Please make sure a name is defined as it is used to share items</.p> | ||
<.form :let={f} for={@changeset} action={Routes.profile_path(@conn, :update, @profile)} method="patch" class="py-3"> | ||
<%= if @changeset.action do %> | ||
<div class="alert alert-danger"> | ||
<p>Oops, something went wrong! Please check the errors below.</p> | ||
</div> | ||
<% end %> | ||
<.form_field | ||
type="text_input" | ||
form={f} | ||
field={:name} | ||
/> | ||
|
||
<.button type="submit" label="Save" /> | ||
</.form> | ||
|
||
</.container> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
defmodule AppWeb.ProfileView do | ||
use AppWeb, :view | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
defmodule App.Repo.Migrations.AddPerson do | ||
use Ecto.Migration | ||
|
||
def change do | ||
execute("CREATE EXTENSION IF NOT EXISTS citext") | ||
|
||
create table(:people, primary_key: false) do | ||
add(:person_id, :integer, primary_key: true) | ||
add(:name, :citext) | ||
|
||
timestamps() | ||
end | ||
|
||
# insert new people rows by select unique person_id from items and tags | ||
execute( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Create person from the existing person_id from the items and tags table |
||
"INSERT INTO people(person_id, inserted_at, updated_at) | ||
SELECT DISTINCT i.person_id, now(), now() FROM items as i UNION SELECT DISTINCT t.person_id, now(), now() from tags as t;" | ||
) | ||
|
||
create(unique_index(:people, [:name], name: :people_name_index)) | ||
|
||
alter table(:items) do | ||
modify(:person_id, references(:people, column: :person_id)) | ||
end | ||
|
||
alter table(:tags) do | ||
modify(:person_id, references(:people, column: :person_id)) | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
defmodule App.PersonTest do | ||
use App.DataCase | ||
alias App.Person | ||
|
||
describe "Test constraints and requirements for Person schema" do | ||
test "valid person changeset" do | ||
changeset = Person.changeset(%Person{}, %{person_id: 1, name: "person1"}) | ||
|
||
assert changeset.valid? | ||
end | ||
|
||
test "invalid person changeset when name value missing" do | ||
changeset = Person.changeset(%Person{}, %{person_id: 1, name: ""}) | ||
refute changeset.valid? | ||
end | ||
|
||
test "invalid person changeset when person_id value missing" do | ||
changeset = Person.changeset(%Person{}, %{name: "person1"}) | ||
refute changeset.valid? | ||
end | ||
end | ||
|
||
describe "Save person in Postgres" do | ||
@valid_attrs %{person_id: 1, name: "person 1"} | ||
@invalid_attrs %{name: nil} | ||
|
||
test "get_person!/1 returns the person with given id" do | ||
{:ok, person} = Person.create_person(@valid_attrs) | ||
assert Person.get_person!(person.person_id).name == person.name | ||
end | ||
|
||
test "create_person/1 with invalid data returns error changeset" do | ||
assert {:error, %Ecto.Changeset{}} = Person.create_person(@invalid_attrs) | ||
end | ||
|
||
test "create_person/1 returns invalid changeset when trying to insert a duplicate name" do | ||
assert {:ok, _person} = Person.create_person(@valid_attrs) | ||
|
||
assert {:error, _changeset} = | ||
Person.create_person(%{person_id: 2, name: "person 1"}) | ||
end | ||
end | ||
|
||
describe "Update person in Postgres" do | ||
@valid_attrs %{person_id: 1, name: "person 1"} | ||
@valid_update_attrs %{person_id: 1, name: "person 1 updated"} | ||
|
||
test "create_person/1 returns invalid changeset when trying to insert a duplicate name" do | ||
assert {:ok, person} = Person.create_person(@valid_attrs) | ||
|
||
assert {:ok, person_updated} = | ||
Person.update_person(person, @valid_update_attrs) | ||
|
||
assert person_updated.name == "person 1 updated" | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find the docs for
Param
pretty useless. Wish there was a standalone example. 💭