Skip to content

Commit 350065c

Browse files
authored
Merge pull request #1269 from code-corps/improve-project-creation
Improve project creation
2 parents 959a6a5 + d0ea4b5 commit 350065c

File tree

7 files changed

+116
-18
lines changed

7 files changed

+116
-18
lines changed

lib/code_corps/model/project.ex

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ defmodule CodeCorps.Project do
99
import CodeCorps.Helpers.Slug
1010
import CodeCorps.Helpers.URL, only: [prefix_url: 2]
1111
import CodeCorps.Validators.SlugValidator
12+
import Ecto.Query, only: [where: 3]
1213

14+
alias CodeCorps.{Category, Repo, Skill, TaskList}
1315
alias CodeCorps.Services.MarkdownRendererService
14-
alias CodeCorps.TaskList
1516

1617
alias Ecto.Changeset
1718

@@ -42,10 +43,11 @@ defmodule CodeCorps.Project do
4243
has_many :task_lists, CodeCorps.TaskList
4344
has_many :tasks, CodeCorps.Task
4445

45-
has_many :categories, through: [:project_categories, :category]
46-
has_many :skills, through: [:project_skills, :skill]
4746
has_many :stripe_connect_subscriptions, through: [:stripe_connect_plan, :stripe_connect_subscriptions]
4847

48+
many_to_many :categories, CodeCorps.Category, join_through: CodeCorps.ProjectCategory
49+
many_to_many :skills, CodeCorps.Skill, join_through: CodeCorps.ProjectSkill
50+
4951
timestamps()
5052
end
5153

@@ -54,7 +56,7 @@ defmodule CodeCorps.Project do
5456
|> cast(params, [:title, :description, :long_description_markdown, :cloudinary_public_id, :default_color, :website])
5557
|> prefix_url(:website)
5658
|> validate_format(:website, CodeCorps.Helpers.URL.valid_format())
57-
|> validate_required(:title)
59+
|> validate_required([:title])
5860
|> generate_slug(:title, :slug)
5961
|> validate_slug(:slug)
6062
|> unique_constraint(:slug, name: :index_projects_on_slug)
@@ -71,12 +73,45 @@ defmodule CodeCorps.Project do
7173
struct
7274
|> changeset(params)
7375
|> cast(params, [:organization_id])
76+
|> validate_required([:cloudinary_public_id, :description])
77+
|> associate_categories(params)
78+
|> validate_length(:categories, min: 1)
79+
|> associate_skills(params)
80+
|> validate_length(:skills, min: 1)
7481
|> put_assoc(:task_lists, TaskList.default_task_lists())
7582
|> put_member_assoc()
7683
|> generate_icon_color(:default_color)
7784
|> assoc_constraint(:organization)
7885
end
7986

87+
defp associate_categories(changeset, %{"categories_ids" => ids}) when is_list(ids) when length(ids) > 0 do
88+
categories = ids |> Enum.map(&String.to_integer/1) |> find_categories()
89+
changeset |> put_assoc(:categories, categories)
90+
end
91+
defp associate_categories(changeset, _) do
92+
changeset |> put_assoc(:categories, [])
93+
end
94+
95+
defp find_categories(ids) do
96+
Category
97+
|> where([object], object.id in ^ids)
98+
|> Repo.all()
99+
end
100+
101+
defp associate_skills(changeset, %{"skills_ids" => ids}) when is_list(ids) when length(ids) > 0 do
102+
skills = ids |> Enum.map(&String.to_integer/1) |> find_skills()
103+
changeset |> put_assoc(:skills, skills)
104+
end
105+
defp associate_skills(changeset, _) do
106+
changeset |> put_assoc(:skills, [])
107+
end
108+
109+
defp find_skills(ids) do
110+
Skill
111+
|> where([object], object.id in ^ids)
112+
|> Repo.all()
113+
end
114+
80115
@doc """
81116
Builds a changeset for updating a project.
82117
"""

lib/code_corps_web/controllers/project_controller.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ defmodule CodeCorpsWeb.ProjectController do
4646

4747
@preloads [
4848
:categories, :donation_goals, :github_repos,
49-
[organization: :stripe_connect_account],
50-
:project_categories, :project_skills, :project_users, :stripe_connect_plan,
49+
[organization: :stripe_connect_account], :project_categories,
50+
:project_skills, :project_users, :skills, :stripe_connect_plan,
5151
:task_lists, :tasks
5252
]
5353

lib/code_corps_web/views/project_view.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ defmodule CodeCorpsWeb.ProjectView do
2323
has_many :project_categories, serializer: CodeCorpsWeb.ProjectCategoryView, identifiers: :always
2424
has_many :project_skills, serializer: CodeCorpsWeb.ProjectSkillView, identifiers: :always
2525
has_many :project_users, serializer: CodeCorpsWeb.ProjectUserView, identifiers: :always
26+
has_many :skills, serializer: CodeCorpsWeb.SkillView, identifiers: :always
2627
has_many :tasks, serializer: CodeCorpsWeb.TaskView, identifiers: :always
2728
has_many :task_lists, serializer: CodeCorpsWeb.TaskListView, identifiers: :always
2829

test/lib/code_corps/model/project_test.exs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,17 @@ defmodule CodeCorps.ProjectTest do
8383

8484
describe "create_changeset/3" do
8585
test "with valid attributes" do
86+
category = insert(:category)
87+
skill = insert(:skill)
8688
organization = insert(:organization)
87-
attrs = %{title: "A title", organization_id: organization.id}
89+
attrs = %{
90+
"categories_ids" => [category.id |> Integer.to_string()],
91+
"cloudinary_public_id" => "foo123",
92+
"description" => "Description",
93+
"skills_ids" => [skill.id |> Integer.to_string()],
94+
"title" => "A title",
95+
"organization_id" => organization.id
96+
}
8897
changeset = create_changeset(%Project{}, attrs)
8998
assert changeset.valid?
9099
end
@@ -95,7 +104,16 @@ defmodule CodeCorps.ProjectTest do
95104
end
96105

97106
test "casts :organization_id and ensures organization exists" do
98-
attrs = %{title: "A title", organization_id: -1}
107+
category = insert(:category)
108+
skill = insert(:skill)
109+
attrs = %{
110+
"categories_ids" => [category.id |> Integer.to_string()],
111+
"cloudinary_public_id" => "foo123",
112+
"description" => "Description",
113+
"skills_ids" => [skill.id |> Integer.to_string()],
114+
"title" => "A title",
115+
"organization_id" => -1
116+
}
99117
changeset = create_changeset(%Project{}, attrs)
100118

101119
assert {:error, failed_insert_changeset} = changeset |> Repo.insert()
@@ -105,8 +123,17 @@ defmodule CodeCorps.ProjectTest do
105123
end
106124

107125
test "casts and inserts proper associated records" do
126+
category = insert(:category)
127+
skill = insert(:skill)
108128
organization = insert(:organization)
109-
attrs = %{title: "A title", organization_id: organization.id}
129+
attrs = %{
130+
"categories_ids" => [category.id |> Integer.to_string()],
131+
"cloudinary_public_id" => "foo123",
132+
"description" => "Description",
133+
"skills_ids" => [skill.id |> Integer.to_string()],
134+
"title" => "A title",
135+
"organization_id" => organization.id
136+
}
110137
changeset = Project.create_changeset(%Project{}, attrs)
111138

112139
{_, project} = Repo.insert(changeset)

test/lib/code_corps_web/controllers/project_controller_test.exs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ defmodule CodeCorpsWeb.ProjectControllerTest do
33

44
use CodeCorpsWeb.ApiCase, resource_name: :project
55

6-
@valid_attrs %{title: "Valid project"}
6+
@valid_attrs %{
7+
cloudinary_public_id: "foo123",
8+
description: "Valid description",
9+
title: "Valid project"
10+
}
711
@invalid_attrs %{title: ""}
812

913
describe "index" do
@@ -83,8 +87,17 @@ defmodule CodeCorpsWeb.ProjectControllerTest do
8387
describe "create" do
8488
@tag :authenticated
8589
test "creates and renders resource when attributes are valid", %{conn: conn, current_user: current_user} do
90+
category = insert(:category)
8691
organization = insert(:organization, owner: current_user)
87-
attrs = @valid_attrs |> Map.merge(%{organization: organization})
92+
skill = insert(:skill)
93+
94+
params = %{
95+
categories: [category],
96+
organization: organization,
97+
skills: [skill]
98+
}
99+
100+
attrs = @valid_attrs |> Map.merge(params)
88101
response = conn |> request_create(attrs)
89102
assert %{assigns: %{data: %{task_lists: [_inbox, _backlog, _in_progress, _done]}}} = response
90103
assert response |> json_response(201)

test/lib/code_corps_web/views/project_view_test.exs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ defmodule CodeCorpsWeb.ProjectViewTest do
88
donation_goal = insert(:donation_goal, project: project)
99
project_category = insert(:project_category, project: project)
1010
github_repo = insert(:github_repo, project: project)
11-
project_skill = insert(:project_skill, project: project)
11+
skill = insert(:skill)
12+
project_skill = insert(:project_skill, project: project, skill: skill)
1213
project_user = insert(:project_user, project: project)
1314
stripe_connect_plan = insert(:stripe_connect_plan, project: project)
1415
task_list = insert(:task_list, project: project)
@@ -87,6 +88,14 @@ defmodule CodeCorpsWeb.ProjectViewTest do
8788
%{"id" => project_user.id |> Integer.to_string, "type" => "project-user"}
8889
]
8990
},
91+
"skills" => %{
92+
"data" => [
93+
%{
94+
"id" => skill.id |> Integer.to_string,
95+
"type" => "skill"
96+
}
97+
]
98+
},
9099
"stripe-connect-plan" => %{
91100
"data" => %{
92101
"id" => stripe_connect_plan.id |> Integer.to_string,

test/support/json_api_helpers.ex

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ defmodule CodeCorps.JsonAPIHelpers do
2323

2424
@spec attribute?(tuple) :: boolean
2525
defp attribute?({_key, %DateTime{} = _val}), do: true
26+
defp attribute?({_key, val}) when is_list(val), do: false
2627
defp attribute?({_key, val}) when is_map(val), do: false
2728
defp attribute?({_key, _val}), do: true
2829

@@ -31,23 +32,28 @@ defmodule CodeCorps.JsonAPIHelpers do
3132
attrs |> Map.put(key |> Atom.to_string, value)
3233
end
3334

34-
@spec build_relationships(map) :: map
35+
@spec build_relationships(list | map) :: map
3536
defp build_relationships(%{} = attrs) do
3637
attrs
3738
|> Enum.filter(&relationship?(&1))
3839
|> Enum.reduce(%{}, &add_relationship(&1, &2))
3940
end
4041

4142
@spec relationship?(any) :: boolean
42-
defp relationship?(tupple), do: !attribute?(tupple)
43+
defp relationship?(tuple), do: !attribute?(tuple)
4344

44-
@spec add_attribute(tuple, map) :: map
45+
@spec add_relationship(tuple, map) :: map
46+
defp add_relationship({_, []}, %{} = rels), do: rels
47+
defp add_relationship({atom_key, list}, %{} = rels) when is_list(list) do
48+
string_key = atom_key |> Atom.to_string
49+
items = list |> Enum.map(&resource_identifier_from_record(&1))
50+
rels |> Map.put(string_key, %{"data" => items})
51+
end
4552
defp add_relationship({atom_key, record}, %{} = rels) do
46-
with id <- record.id |> to_correct_type(),
47-
type <- record |> model_name_as_string(),
53+
with resource_identifier <- record |> resource_identifier_from_record(),
4854
string_key = atom_key |> Atom.to_string
4955
do
50-
rels |> Map.put(string_key, %{"data" => %{"id" => id, "type" => type}})
56+
rels |> Map.put(string_key, %{"data" => resource_identifier})
5157
end
5258
end
5359

@@ -62,4 +68,11 @@ defmodule CodeCorps.JsonAPIHelpers do
6268
@spec to_correct_type(any) :: any
6369
defp to_correct_type(value) when is_integer(value), do: value |> Integer.to_string
6470
defp to_correct_type(value), do: value
71+
72+
defp resource_identifier_from_record(record) do
73+
%{
74+
"id" => record.id |> to_correct_type(),
75+
"type" => record |> model_name_as_string()
76+
}
77+
end
6578
end

0 commit comments

Comments
 (0)