diff --git a/lib/code_corps/analytics/segment_tracking_support.ex b/lib/code_corps/analytics/segment_tracking_support.ex index 8131fbf07..301bfb01f 100644 --- a/lib/code_corps/analytics/segment_tracking_support.ex +++ b/lib/code_corps/analytics/segment_tracking_support.ex @@ -19,8 +19,6 @@ defmodule CodeCorps.Analytics.SegmentTrackingSupport do def includes?(:create, %CodeCorps.StripeConnectSubscription{}), do: true def includes?(:create, %CodeCorps.StripePlatformCard{}), do: true def includes?(:create, %CodeCorps.StripePlatformCustomer{}), do: true - def includes?(:create, %CodeCorps.Task{}), do: true - def includes?(:update, %CodeCorps.Task{}), do: true def includes?(:create, %CodeCorps.User{}), do: true def includes?(:update, %CodeCorps.User{}), do: true def includes?(:create, %CodeCorps.UserCategory{}), do: true diff --git a/lib/code_corps/analytics/segment_traits_builder.ex b/lib/code_corps/analytics/segment_traits_builder.ex index 11e03a8a4..95efccecb 100644 --- a/lib/code_corps/analytics/segment_traits_builder.ex +++ b/lib/code_corps/analytics/segment_traits_builder.ex @@ -3,30 +3,14 @@ defmodule CodeCorps.Analytics.SegmentTraitsBuilder do Builds Segment traits from provided data """ - @spec build(struct) :: map - def build(record), do: traits(record) + alias CodeCorps.Repo - defp traits(user = %CodeCorps.User{}) do - %{ - admin: user.admin, - biography: user.biography, - created_at: user.inserted_at, - email: user.email, - first_name: user.first_name, - github_id: user.github_id, - github_username: user.github_username, - last_name: user.last_name, - sign_up_context: user.sign_up_context, - state: user.state, - twitter: user.twitter, - type: user.type, - username: user.username, - website: user.website - } - end + @spec build(struct | map) :: map + def build(record), do: traits(record) - defp traits(comment = %CodeCorps.Comment{}) do - comment = comment |> CodeCorps.Repo.preload(:task) + @spec traits(struct | map) :: map + defp traits(%CodeCorps.Comment{} = comment) do + comment = comment |> Repo.preload(:task) %{ comment_id: comment.id, task: comment.task.title, @@ -34,16 +18,64 @@ defmodule CodeCorps.Analytics.SegmentTraitsBuilder do project_id: comment.task.project_id } end - - defp traits(record = %CodeCorps.ProjectUser{}) do - record = record |> CodeCorps.Repo.preload(:project) + defp traits(%CodeCorps.DonationGoal{} = donation_goal) do %{ + amount: donation_goal.amount, + current: donation_goal.current, + project_id: donation_goal.project_id + } + end + defp traits(%CodeCorps.GithubAppInstallation{} = installation) do + %{ + access_token_expires_at: installation.access_token_expires_at, + github_account_login: installation.github_account_login, + github_account_type: installation.github_account_type, + github_id: installation.github_id, + origin: installation.origin, + state: installation.state, + project_id: installation.project_id, + user_id: installation.user_id + } + end + defp traits(%CodeCorps.ProjectGithubRepo{} = record) do + record = record |> Repo.preload([:project, :github_repo]) + %{ + project: record.project.title, + project_id: record.project_id, + github_repo_id: record.github_repo_id, + github_repo_github_account_login: record.github_repo.github_account_login, + github_repo_github_account_type: record.github_repo.github_account_type, + github_repo_github_id: record.github_repo.github_id, + github_repo_name: record.github_repo.name + } + end + defp traits(%CodeCorps.ProjectSkill{} = record) do + record = record |> Repo.preload([:project, :skill]) + %{ + skill: record.skill.title, + skill_id: record.skill_id, project: record.project.title, project_id: record.project_id } end - - defp traits(charge = %CodeCorps.StripeConnectCharge{}) do + defp traits(%CodeCorps.ProjectUser{} = record) do + record = record |> Repo.preload(:project) + %{ + project: record.project.title, + project_id: record.project_id + } + end + defp traits(%CodeCorps.StripeConnectAccount{} = account) do + %{ + id: account.id, + business_name: account.business_name, + display_name: account.display_name, + email: account.email, + id_from_stripe: account.id_from_stripe, + organization_id: account.organization_id, + } + end + defp traits(%CodeCorps.StripeConnectCharge{} = charge) do # NOTE: this only works for some currencies revenue = charge.amount / 100 currency = String.capitalize(charge.currency) # ISO 4127 format @@ -55,38 +87,121 @@ defmodule CodeCorps.Analytics.SegmentTraitsBuilder do user_id: charge.user_id } end + defp traits(%CodeCorps.StripeConnectPlan{} = plan) do + %{ + id: plan.id, + amount: plan.amount, + created: plan.created, + id_from_stripe: plan.id_from_stripe, + name: plan.name, + project_id: plan.project_id + } + end + defp traits(%CodeCorps.StripeConnectSubscription{} = subscription) do + subscription = subscription |> Repo.preload(:stripe_connect_plan) - defp traits(task = %CodeCorps.Task{}) do %{ + id: subscription.id, + created: subscription.created, + cancelled_at: subscription.cancelled_at, + current_period_start: subscription.current_period_start, + current_period_end: subscription.current_period_end, + ended_at: subscription.ended_at, + id_from_stripe: subscription.id_from_stripe, + quantity: subscription.quantity, + status: subscription.status, + start: subscription.start, + plan_id: subscription.stripe_connect_plan_id, + user_id: subscription.user_id, + project_id: subscription.stripe_connect_plan.project_id + } + end + defp traits(%CodeCorps.StripePlatformCard{} = card) do + %{ + id: card.id, + brand: card.brand, + exp_month: card.exp_month, + exp_year: card.exp_year, + id_from_stripe: card.id_from_stripe, + last4: card.last4, + name: card.name, + user_id: card.user_id + } + end + defp traits(%CodeCorps.StripePlatformCustomer{} = customer) do + %{ + id: customer.id, + created: customer.created, + currency: customer.currency, + delinquent: customer.delinquent, + email: customer.email, + id_from_stripe: customer.id_from_stripe, + user_id: customer.user_id + } + end + defp traits(%CodeCorps.Task{} = task) do + %{ + order: task.order, task: task.title, task_id: task.id, + task_list_id: task.task_list_id, project_id: task.project_id } end - - defp traits(user_category = %CodeCorps.UserCategory{}) do - user_category = user_category |> CodeCorps.Repo.preload(:category) + defp traits(%CodeCorps.TaskSkill{} = task_skill) do + task_skill = task_skill |> Repo.preload([:skill, :task]) + %{ + skill: task_skill.skill.title, + skill_id: task_skill.skill.id, + task: task_skill.task.title + } + end + defp traits(%CodeCorps.User{} = user) do + %{ + admin: user.admin, + biography: user.biography, + created_at: user.inserted_at, + email: user.email, + first_name: user.first_name, + github_id: user.github_id, + github_username: user.github_username, + last_name: user.last_name, + sign_up_context: user.sign_up_context, + state: user.state, + twitter: user.twitter, + type: user.type, + username: user.username, + website: user.website + } + end + defp traits(%CodeCorps.UserCategory{} = user_category) do + user_category = user_category |> Repo.preload(:category) %{ category: user_category.category.name, category_id: user_category.category.id } end - - defp traits(user_role = %CodeCorps.UserRole{}) do - user_role = user_role |> CodeCorps.Repo.preload(:role) + defp traits(%CodeCorps.UserRole{} = user_role) do + user_role = user_role |> Repo.preload(:role) %{ role: user_role.role.name, role_id: user_role.role.id } end - - defp traits(user_skill = %CodeCorps.UserSkill{}) do - user_skill = user_skill |> CodeCorps.Repo.preload(:skill) + defp traits(%CodeCorps.UserSkill{} = user_skill) do + user_skill = user_skill |> Repo.preload(:skill) %{ skill: user_skill.skill.title, skill_id: user_skill.skill.id } end + defp traits(%CodeCorps.UserTask{} = user_task) do + user_task = user_task |> Repo.preload(:task) - defp traits(_), do: %{} + %{ + task: user_task.task.title, + task_id: user_task.task_id + } + end + defp traits(%{token: _, user_id: _}), do: %{} end diff --git a/lib/code_corps/model/task.ex b/lib/code_corps/model/task.ex index d3cd2ed21..8d604874b 100644 --- a/lib/code_corps/model/task.ex +++ b/lib/code_corps/model/task.ex @@ -3,7 +3,7 @@ defmodule CodeCorps.Task do import EctoOrdered - alias CodeCorps.Services.MarkdownRendererService + alias CodeCorps.{Task, Services.MarkdownRendererService} alias Ecto.Changeset @type t :: %__MODULE__{} @@ -67,13 +67,15 @@ defmodule CodeCorps.Task do |> put_change(:status, "open") end - def update_changeset(struct, params) do + @spec update_changeset(struct, map) :: Ecto.Changeset.t + def update_changeset(struct, %{} = params) do struct |> changeset(params) |> cast(params, [:archived, :status]) |> validate_inclusion(:status, statuses()) |> set_closed_at() |> update_modified_at() + |> maybe_assoc_with_repo(params) end def apply_position(changeset) do @@ -109,4 +111,15 @@ defmodule CodeCorps.Task do defp update_modified_at(changeset) do put_change(changeset, :modified_at, DateTime.utc_now) end + + @spec maybe_assoc_with_repo(Changeset.t, map) :: Changeset.t + defp maybe_assoc_with_repo( + %Changeset{data: %Task{github_repo_id: nil}} = changeset, + %{} = params) do + + changeset + |> cast(params, [:github_repo_id]) + |> assoc_constraint(:github_repo) + end + defp maybe_assoc_with_repo(%Changeset{} = changeset, %{}), do: changeset end diff --git a/lib/code_corps/task/service.ex b/lib/code_corps/task/service.ex index a57c2c5db..297033d49 100644 --- a/lib/code_corps/task/service.ex +++ b/lib/code_corps/task/service.ex @@ -22,12 +22,6 @@ defmodule CodeCorps.Task.Service do end @spec update(Task.t, map) :: {:ok, Task.t} | {:error, Changeset.t} | {:error, :github} - def update(%Task{github_issue_id: nil} = task, %{} = attributes) do - Multi.new - |> Multi.update(:task, task |> Task.update_changeset(attributes)) - |> Repo.transaction - |> marshall_result() - end def update(%Task{} = task, %{} = attributes) do Multi.new |> Multi.update(:task, task |> Task.update_changeset(attributes)) @@ -64,7 +58,7 @@ defmodule CodeCorps.Task.Service do defp create_on_github(%Task{github_repo: _} = task) do with %Task{github_repo: github_repo} = task <- task |> Repo.preload(@preloads), {:ok, payload} <- GitHub.API.Issue.create(task), - {:ok, %GithubIssue{} = github_issue } <- IssueGithubIssueSyncer.create_or_update_issue({github_repo, nil}, payload) do + {:ok, %GithubIssue{} = github_issue} <- IssueGithubIssueSyncer.create_or_update_issue({github_repo, nil}, payload) do task |> link_with_github_changeset(github_issue) |> Repo.update else {:error, error} -> {:error, error} @@ -77,12 +71,13 @@ defmodule CodeCorps.Task.Service do end @spec update_on_github(Task.t) :: {:ok, Task.t} :: {:error, GitHub.api_error_struct} - defp update_on_github(%Task{github_repo_id: nil} = task), do: {:ok, task} + defp update_on_github(%Task{github_repo_id: nil, github_issue_id: nil} = task), do: {:ok, task} + defp update_on_github(%Task{github_repo_id: _, github_issue_id: nil} = task), do: task |> create_on_github() defp update_on_github(%Task{github_repo_id: _} = task) do with %Task{github_repo: github_repo} = task <- task |> Repo.preload(@preloads), {:ok, payload} <- GitHub.API.Issue.update(task), - {:ok, %GithubIssue{} } <- IssueGithubIssueSyncer.create_or_update_issue({github_repo, nil}, payload) do - {:ok, task} + {:ok, %GithubIssue{}} <- IssueGithubIssueSyncer.create_or_update_issue({github_repo, nil}, payload) do + {:ok, Task |> Repo.get(task.id)} else {:error, github_error} -> {:error, github_error} end diff --git a/lib/code_corps_web/controllers/github_app_installation_controller.ex b/lib/code_corps_web/controllers/github_app_installation_controller.ex index 0b494bb1c..0388bd0c0 100644 --- a/lib/code_corps_web/controllers/github_app_installation_controller.ex +++ b/lib/code_corps_web/controllers/github_app_installation_controller.ex @@ -4,7 +4,7 @@ defmodule CodeCorpsWeb.GithubAppInstallationController do import CodeCorps.Helpers.Query, only: [id_filter: 2] - alias CodeCorps.{GithubAppInstallation, User} + alias CodeCorps.{Analytics.SegmentTracker, GithubAppInstallation, User} action_fallback CodeCorpsWeb.FallbackController plug CodeCorpsWeb.Plug.DataToAttributes @@ -29,7 +29,14 @@ defmodule CodeCorpsWeb.GithubAppInstallationController do with %User{} = current_user <- conn |> Guardian.Plug.current_resource, {:ok, :authorized} <- current_user |> Policy.authorize(:create, %GithubAppInstallation{}, params), {:ok, %GithubAppInstallation{} = installation} <- %GithubAppInstallation{} |> GithubAppInstallation.create_changeset(params) |> Repo.insert do + + current_user |> track_created(installation) conn |> put_status(:created) |> render("show.json-api", data: installation) end end + + @spec track_created(User.t, GithubAppInstallation.t) :: any + defp track_created(%User{id: user_id}, %GithubAppInstallation{} = installation) do + user_id |> SegmentTracker.track("Created GitHub App Installation", installation) + end end diff --git a/lib/code_corps_web/controllers/project_github_repo_controller.ex b/lib/code_corps_web/controllers/project_github_repo_controller.ex index 7c61a2679..f7cf27ff4 100644 --- a/lib/code_corps_web/controllers/project_github_repo_controller.ex +++ b/lib/code_corps_web/controllers/project_github_repo_controller.ex @@ -2,7 +2,7 @@ defmodule CodeCorpsWeb.ProjectGithubRepoController do @moduledoc false use CodeCorpsWeb, :controller - alias CodeCorps.{ProjectGithubRepo, User, Helpers.Query} + alias CodeCorps.{Analytics.SegmentTracker, ProjectGithubRepo, User, Helpers.Query} action_fallback CodeCorpsWeb.FallbackController plug CodeCorpsWeb.Plug.DataToAttributes @@ -27,6 +27,9 @@ defmodule CodeCorpsWeb.ProjectGithubRepoController do with %User{} = current_user <- conn |> Guardian.Plug.current_resource, {:ok, :authorized} <- current_user |> Policy.authorize(:create, %ProjectGithubRepo{}, params), {:ok, %ProjectGithubRepo{} = project_github_repo} <- create_project_repo_changeset(params) |> Repo.insert do + + current_user |> track_created(project_github_repo) + conn |> put_status(:created) |> render("show.json-api", data: project_github_repo) end end @@ -36,7 +39,10 @@ defmodule CodeCorpsWeb.ProjectGithubRepoController do with %ProjectGithubRepo{} = project_github_repo <- ProjectGithubRepo |> Repo.get(id), %User{} = current_user <- conn |> Guardian.Plug.current_resource, {:ok, :authorized} <- current_user |> Policy.authorize(:delete, project_github_repo, params), - {:ok, _project_github_repo} <- project_github_repo |> Repo.delete do + {:ok, project_github_repo} <- project_github_repo |> Repo.delete do + + current_user |> track_deleted(project_github_repo) + conn |> send_resp(:no_content, "") end end @@ -46,4 +52,14 @@ defmodule CodeCorpsWeb.ProjectGithubRepoController do %ProjectGithubRepo{} |> ProjectGithubRepo.create_changeset(params) end + + @spec track_created(User.t, ProjectGithubRepo.t) :: any + defp track_created(%User{id: user_id}, %ProjectGithubRepo{} = project_github_repo) do + user_id |> SegmentTracker.track("Connected GitHub Repo to Project", project_github_repo) + end + + @spec track_deleted(User.t, ProjectGithubRepo.t) :: any + defp track_deleted(%User{id: user_id}, %ProjectGithubRepo{} = project_github_repo) do + user_id |> SegmentTracker.track("Disconnected GitHub Repo from Project", project_github_repo) + end end diff --git a/lib/code_corps_web/controllers/project_skill_controller.ex b/lib/code_corps_web/controllers/project_skill_controller.ex index ed56553a1..cd0d45bf8 100644 --- a/lib/code_corps_web/controllers/project_skill_controller.ex +++ b/lib/code_corps_web/controllers/project_skill_controller.ex @@ -2,7 +2,7 @@ defmodule CodeCorpsWeb.ProjectSkillController do @moduledoc false use CodeCorpsWeb, :controller - alias CodeCorps.{ProjectSkill, User, Helpers.Query} + alias CodeCorps.{Analytics.SegmentTracker, ProjectSkill, User, Helpers.Query} action_fallback CodeCorpsWeb.FallbackController plug CodeCorpsWeb.Plug.DataToAttributes @@ -27,6 +27,8 @@ defmodule CodeCorpsWeb.ProjectSkillController do with %User{} = current_user <- conn |> Guardian.Plug.current_resource, {:ok, :authorized} <- current_user |> Policy.authorize(:create, %ProjectSkill{}, params), {:ok, %ProjectSkill{} = project_skill} <- %ProjectSkill{} |> ProjectSkill.create_changeset(params) |> Repo.insert do + + current_user.id |> SegmentTracker.track("Added Project Skill", project_skill) conn |> put_status(:created) |> render("show.json-api", data: project_skill) end end @@ -36,8 +38,9 @@ defmodule CodeCorpsWeb.ProjectSkillController do with %ProjectSkill{} = project_skill <- ProjectSkill |> Repo.get(id), %User{} = current_user <- conn |> Guardian.Plug.current_resource, {:ok, :authorized} <- current_user |> Policy.authorize(:delete, project_skill), - {:ok, %ProjectSkill{} = _project_skill} <- project_skill |> Repo.delete + {:ok, %ProjectSkill{} = project_skill} <- project_skill |> Repo.delete do + current_user.id |> SegmentTracker.track("Removed Project Skill", project_skill) conn |> Conn.assign(:project_skill, project_skill) |> send_resp(:no_content, "") end end diff --git a/lib/code_corps_web/controllers/task_controller.ex b/lib/code_corps_web/controllers/task_controller.ex index a3568c342..94d248bdb 100644 --- a/lib/code_corps_web/controllers/task_controller.ex +++ b/lib/code_corps_web/controllers/task_controller.ex @@ -2,7 +2,7 @@ defmodule CodeCorpsWeb.TaskController do @moduledoc false use CodeCorpsWeb, :controller - alias CodeCorps.{Task, Policy, User} + alias CodeCorps.{Analytics.SegmentTracker, Task, Policy, User} action_fallback CodeCorpsWeb.FallbackController plug CodeCorpsWeb.Plug.DataToAttributes @@ -27,6 +27,10 @@ defmodule CodeCorpsWeb.TaskController do with %User{} = current_user <- conn |> Guardian.Plug.current_resource, {:ok, :authorized} <- current_user |> Policy.authorize(:create, %Task{}, params), {:ok, %Task{} = task} <- params |> Task.Service.create do + + current_user |> track_created(task) + current_user |> maybe_track_connected(task) + conn |> put_status(:created) |> render("show.json-api", data: task) end end @@ -36,8 +40,88 @@ defmodule CodeCorpsWeb.TaskController do with %Task{} = task <- Task.Query.find(params), %User{} = current_user <- conn |> Guardian.Plug.current_resource, {:ok, :authorized} <- current_user |> Policy.authorize(:update, task), - {:ok, %Task{} = task} <- task |> Task.Service.update(params) do - conn |> render("show.json-api", data: task) + {:ok, %Task{} = updated_task} <- task |> Task.Service.update(params) do + + current_user |> track_updated(updated_task) + current_user |> maybe_track_connected(updated_task, task) + current_user |> maybe_track_list_move(updated_task, task) + current_user |> maybe_track_title_change(updated_task, task) + current_user |> maybe_track_close(updated_task, task) + current_user |> maybe_track_archive(updated_task, task) + + conn |> render("show.json-api", data: updated_task) end end + + # tracking + + @spec track_created(User.t, Task.t) :: any + defp track_created(%User{id: user_id}, %Task{} = task) do + user_id |> SegmentTracker.track("Created Task", task) + end + + @spec track_updated(User.t, Task.t) :: any + defp track_updated(%User{id: user_id}, %Task{} = task) do + user_id |> SegmentTracker.track("Edited Task", task) + end + + @spec maybe_track_connected(User.t, Task.t) :: any + defp maybe_track_connected( + %User{id: user_id}, + %Task{github_issue_id: issue_id} = task) when not is_nil(issue_id) do + + user_id |> SegmentTracker.track("Connected Task to GitHub", task) + end + defp maybe_track_connected(%User{}, %Task{}), do: :nothing + + @spec maybe_track_connected(User.t, Task.t, Task.t) :: any + defp maybe_track_connected( + %User{id: user_id}, + %Task{github_issue_id: new_issue_id} = task, + %Task{github_issue_id: old_issue_id}) + when is_nil(old_issue_id) and not is_nil(new_issue_id) do + + user_id |> SegmentTracker.track("Connected Task to GitHub", task) + end + defp maybe_track_connected(%User{}, %Task{}, %Task{}), do: :nothing + + @spec maybe_track_list_move(User.t, Task.t, Task.t) :: any + defp maybe_track_list_move( + %User{id: user_id}, + %Task{task_list_id: new_list_id} = task, + %Task{task_list_id: old_list_id}) when new_list_id != old_list_id do + + user_id |> SegmentTracker.track("Moved Task Between Lists", task) + end + defp maybe_track_list_move(%User{}, %Task{}, %Task{}), do: :nothing + + @spec maybe_track_title_change(User.t, Task.t, Task.t) :: any + defp maybe_track_title_change( + %User{id: user_id}, + %Task{title: new_title} = task, + %Task{title: old_title}) when new_title != old_title do + + user_id |> SegmentTracker.track("Edited Task Title", task) + end + defp maybe_track_title_change(%User{}, %Task{}, %Task{}), do: :nothing + + @spec maybe_track_close(User.t, Task.t, Task.t) :: any + defp maybe_track_close( + %User{id: user_id}, + %Task{status: "closed"} = task, + %Task{status: "open"}) do + + user_id |> SegmentTracker.track("Closed Task", task) + end + defp maybe_track_close(%User{}, %Task{}, %Task{}), do: :nothing + + @spec maybe_track_archive(User.t, Task.t, Task.t) :: any + defp maybe_track_archive( + %User{id: user_id}, + %Task{archived: true} = task, + %Task{archived: false}) do + + user_id |> SegmentTracker.track("Archived Task", task) + end + defp maybe_track_archive(%User{}, %Task{}, %Task{}), do: :nothing end diff --git a/lib/code_corps_web/controllers/task_skill_controller.ex b/lib/code_corps_web/controllers/task_skill_controller.ex index f3a57c846..63e470be3 100644 --- a/lib/code_corps_web/controllers/task_skill_controller.ex +++ b/lib/code_corps_web/controllers/task_skill_controller.ex @@ -2,7 +2,12 @@ defmodule CodeCorpsWeb.TaskSkillController do @moduledoc false use CodeCorpsWeb, :controller - alias CodeCorps.{TaskSkill, User, Helpers.Query} + alias CodeCorps.{ + Analytics.SegmentTracker, + Helpers.Query, + TaskSkill, + User + } action_fallback CodeCorpsWeb.FallbackController plug CodeCorpsWeb.Plug.DataToAttributes @@ -26,7 +31,9 @@ defmodule CodeCorpsWeb.TaskSkillController do def create(%Conn{} = conn, %{} = params) do with %User{} = current_user <- conn |> Guardian.Plug.current_resource, {:ok, :authorized} <- current_user |> Policy.authorize(:create, %TaskSkill{}, params), - {:ok, %TaskSkill{} = task_skill} <- %TaskSkill{} |> TaskSkill.create_changeset(params) |> Repo.insert do + {:ok, %TaskSkill{} = task_skill} <- %TaskSkill{} |> TaskSkill.create_changeset(params) |> Repo.insert + do + SegmentTracker.track(current_user.id, "Added Task Skill", task_skill) conn |> put_status(:created) |> render("show.json-api", data: task_skill) end end @@ -38,6 +45,7 @@ defmodule CodeCorpsWeb.TaskSkillController do {:ok, :authorized} <- current_user |> Policy.authorize(:delete, task_skill), {:ok, %TaskSkill{} = _task_skill} <- task_skill |> Repo.delete do + SegmentTracker.track(current_user.id, "Removed Task Skill", task_skill) conn |> Conn.assign(:task_skill, task_skill) |> send_resp(:no_content, "") end end diff --git a/lib/code_corps_web/controllers/user_controller.ex b/lib/code_corps_web/controllers/user_controller.ex index 8f16b9322..6480130b6 100644 --- a/lib/code_corps_web/controllers/user_controller.ex +++ b/lib/code_corps_web/controllers/user_controller.ex @@ -1,76 +1,5 @@ defmodule CodeCorpsWeb.UserController do @moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false -@moduledoc false use CodeCorpsWeb, :controller alias CodeCorps.{ diff --git a/lib/code_corps_web/controllers/user_task_controller.ex b/lib/code_corps_web/controllers/user_task_controller.ex index 28b6c99d7..a0d0a787e 100644 --- a/lib/code_corps_web/controllers/user_task_controller.ex +++ b/lib/code_corps_web/controllers/user_task_controller.ex @@ -2,7 +2,12 @@ defmodule CodeCorpsWeb.UserTaskController do @moduledoc false use CodeCorpsWeb, :controller - alias CodeCorps.{UserTask, User, Helpers.Query} + alias CodeCorps.{ + Analytics.SegmentTracker, + UserTask, + User, + Helpers.Query + } action_fallback CodeCorpsWeb.FallbackController plug CodeCorpsWeb.Plug.DataToAttributes @@ -27,6 +32,8 @@ defmodule CodeCorpsWeb.UserTaskController do {:ok, :authorized} <- current_user |> Policy.authorize(:create, %UserTask{}, params), {:ok, %UserTask{} = user_task} <- %UserTask{} |> UserTask.create_changeset(params) |> Repo.insert do + current_user |> track_assigned(user_task) + conn |> put_status(:created) |> render("show.json-api", data: user_task) end end @@ -38,6 +45,8 @@ defmodule CodeCorpsWeb.UserTaskController do {:ok, :authorized} <- current_user |> Policy.authorize(:update, user_task), {:ok, %UserTask{} = user_task} <- user_task |> UserTask.update_changeset(params) |> Repo.update do + current_user |> track_assigned(user_task) + conn |> render("show.json-api", data: user_task) end end @@ -49,7 +58,21 @@ defmodule CodeCorpsWeb.UserTaskController do {:ok, :authorized} <- current_user |> Policy.authorize(:delete, user_task), {:ok, %UserTask{} = _user_task} <- user_task |> Repo.delete do + current_user |> track_unassigned(user_task) + conn |> send_resp(:no_content, "") end end + + @spec track_assigned(User.t, UserTask.t) :: any + defp track_assigned(%User{id: user_id}, %UserTask{user_id: assigned_user_id} = user_task) + when user_id == assigned_user_id, do: SegmentTracker.track(user_id, "Assigned Task to Self", user_task) + defp track_assigned(%User{id: user_id}, %UserTask{} = user_task), + do: SegmentTracker.track(user_id, "Assigned Task to Someone Else", user_task) + + @spec track_unassigned(User.t, UserTask.t) :: any + defp track_unassigned(%User{id: user_id}, %UserTask{user_id: assigned_user_id} = user_task) + when user_id == assigned_user_id, do: SegmentTracker.track(user_id, "Unassigned Task from Self", user_task) + defp track_unassigned(%User{id: user_id}, %UserTask{} = user_task), + do: SegmentTracker.track(user_id, "Unassigned Task from Someone Else", user_task) end diff --git a/mix.lock b/mix.lock index 1310e418f..d57435382 100644 --- a/mix.lock +++ b/mix.lock @@ -21,7 +21,7 @@ "ecto_ordered": {:hex, :ecto_ordered, "0.2.0-beta1", "cb066bc608f1c8913cea85af8293261720e6a88e3c99061e6877d7025352f045", [:mix], [{:ecto, "~> 2.0", [hex: :ecto, optional: false]}]}, "elixir_make": {:hex, :elixir_make, "0.4.0", "992f38fabe705bb45821a728f20914c554b276838433349d4f2341f7a687cddf", [:mix], []}, "ex_aws": {:hex, :ex_aws, "1.1.4", "4bdc4fff91f8d35c7fe2355b9da54cc51f980c92f1137715d8b2d70d8e8511cc", [:mix], [{:configparser_ex, "~> 0.2.1", [hex: :configparser_ex, optional: true]}, {:hackney, "1.6.3 or 1.6.5 or 1.7.1 or 1.8.6 or ~> 1.9", [hex: :hackney, optional: true]}, {:jsx, "~> 2.8", [hex: :jsx, optional: true]}, {:poison, ">= 1.2.0", [hex: :poison, optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, optional: true]}, {:xml_builder, "~> 0.1.0", [hex: :xml_builder, optional: true]}]}, - "ex_doc": {:hex, :ex_doc, "0.18.1", "37c69d2ef62f24928c1f4fdc7c724ea04aecfdf500c4329185f8e3649c915baf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}, + "ex_doc": {:hex, :ex_doc, "0.18.1", "37c69d2ef62f24928c1f4fdc7c724ea04aecfdf500c4329185f8e3649c915baf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, optional: false]}]}, "ex_machina": {:hex, :ex_machina, "2.1.0", "4874dc9c78e7cf2d429f24dc3c4005674d4e4da6a08be961ffccc08fb528e28b", [:mix], [{:ecto, "~> 2.1", [hex: :ecto, optional: true]}]}, "excoveralls": {:hex, :excoveralls, "0.7.2", "f69ede8c122ccd3b60afc775348a53fc8c39fe4278aee2f538f0d81cc5e7ff3a", [:mix], [{:exjsx, ">= 3.0.0", [hex: :exjsx, optional: false]}, {:hackney, ">= 0.12.0", [hex: :hackney, optional: false]}]}, "exjsx": {:hex, :exjsx, "3.2.1", "1bc5bf1e4fd249104178f0885030bcd75a4526f4d2a1e976f4b428d347614f0f", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, optional: false]}]}, diff --git a/priv/repo/structure.sql b/priv/repo/structure.sql index 7404bf4ff..aea25f3ea 100644 --- a/priv/repo/structure.sql +++ b/priv/repo/structure.sql @@ -2,7 +2,7 @@ -- PostgreSQL database dump -- --- Dumped from database version 9.5.9 +-- Dumped from database version 10.0 -- Dumped by pg_dump version 10.0 SET statement_timeout = 0; diff --git a/test/lib/code_corps/analytics/segment_traits_builder_test.exs b/test/lib/code_corps/analytics/segment_traits_builder_test.exs new file mode 100644 index 000000000..bcbe58d4e --- /dev/null +++ b/test/lib/code_corps/analytics/segment_traits_builder_test.exs @@ -0,0 +1,44 @@ +defmodule CodeCorps.Analytics.SegmentTraitsBuilderTest do + @moduledoc false + + use CodeCorps.DbAccessCase + + + alias CodeCorps.Analytics.SegmentTraitsBuilder + + describe "build/1" do + # NOTE: These tests only make sure there's a function clause for each + # supported struct and do not assert the traits content. Simply put, the + # only way to assert that would mean we're practically re-implementing the + # builder within tests + + test "works for all supported struct types" do + assert :comment |> insert |> SegmentTraitsBuilder.build + assert :donation_goal |> insert |> SegmentTraitsBuilder.build + + assert :github_app_installation |> insert |> SegmentTraitsBuilder.build + + assert :project_skill |> insert |> SegmentTraitsBuilder.build + assert :project_user |> insert |> SegmentTraitsBuilder.build + assert :project_github_repo |> insert |> SegmentTraitsBuilder.build + + assert :stripe_connect_account |> insert |> SegmentTraitsBuilder.build + assert :stripe_connect_charge |> insert |> SegmentTraitsBuilder.build + assert :stripe_connect_plan |> insert |> SegmentTraitsBuilder.build + assert :stripe_connect_subscription |> insert |> SegmentTraitsBuilder.build + assert :stripe_platform_card |> insert |> SegmentTraitsBuilder.build + assert :stripe_platform_customer |> insert |> SegmentTraitsBuilder.build + + assert :task |> insert |> SegmentTraitsBuilder.build + assert :task_skill |> insert |> SegmentTraitsBuilder.build + + assert :user |> insert |> SegmentTraitsBuilder.build + assert :user_category |> insert |> SegmentTraitsBuilder.build + assert :user_role |> insert |> SegmentTraitsBuilder.build + assert :user_skill |> insert |> SegmentTraitsBuilder.build + assert :user_task |> insert |> SegmentTraitsBuilder.build + + assert %{token: "foo", user_id: 1} |> SegmentTraitsBuilder.build + end + end +end diff --git a/test/lib/code_corps/task/service_test.exs b/test/lib/code_corps/task/service_test.exs index eb6f67c6d..c67bceb2f 100644 --- a/test/lib/code_corps/task/service_test.exs +++ b/test/lib/code_corps/task/service_test.exs @@ -117,6 +117,23 @@ defmodule CodeCorps.Task.ServiceTest do refute_received({:post, _string, {}, "{}", []}) end + test "creates a github issue if task is just now connected to a repo" do + github_repo = + :github_repo + |> insert(github_account_login: "foo", name: "bar") + + task = insert(:task) + + attrs = @update_attrs |> Map.put("github_repo_id", github_repo.id) + + {:ok, updated_task} = task |> Task.Service.update(attrs) + + assert updated_task.github_issue_id + assert updated_task.github_repo_id == github_repo.id + + assert_received({:post, "https://api.github.com/repos/foo/bar/issues", _headers, _body, _options}) + end + test "propagates changes to github if task is synced to github issue" do github_repo = :github_repo diff --git a/test/lib/code_corps_web/controllers/github_app_installation_controller_test.exs b/test/lib/code_corps_web/controllers/github_app_installation_controller_test.exs index 0f89ea18a..4d3a11d75 100644 --- a/test/lib/code_corps_web/controllers/github_app_installation_controller_test.exs +++ b/test/lib/code_corps_web/controllers/github_app_installation_controller_test.exs @@ -3,6 +3,8 @@ defmodule CodeCorpsWeb.GithubAppInstallationControllerTest do use CodeCorpsWeb.ApiCase, resource_name: :github_app_installation + alias CodeCorps.{Analytics.SegmentTraitsBuilder, GithubAppInstallation, Repo} + describe "index" do test "lists all resources", %{conn: conn} do [record_1, record_2] = insert_pair(:github_app_installation) @@ -49,6 +51,19 @@ defmodule CodeCorpsWeb.GithubAppInstallationControllerTest do assert conn |> request_create(attrs) |> json_response(201) end + @tag :authenticated + test "tracks creation", %{conn: conn, current_user: current_user} do + project = insert(:project) + insert(:project_user, project: project, user: current_user, role: "owner") + attrs = %{project: project, user: current_user} + + conn |> request_create(attrs) + + user_id = current_user.id + traits = GithubAppInstallation |> Repo.one |> SegmentTraitsBuilder.build + assert_receive({:track, ^user_id, "Created GitHub App Installation", ^traits}) + end + @tag :authenticated test "does not create resource and renders 422 when data is invalid", %{conn: conn, current_user: user} do project = insert(:project) diff --git a/test/lib/code_corps_web/controllers/project_github_repo_controller_test.exs b/test/lib/code_corps_web/controllers/project_github_repo_controller_test.exs index 6b5f145bc..022703b90 100644 --- a/test/lib/code_corps_web/controllers/project_github_repo_controller_test.exs +++ b/test/lib/code_corps_web/controllers/project_github_repo_controller_test.exs @@ -1,6 +1,8 @@ defmodule CodeCorpsWeb.ProjectGithubRepoControllerTest do use CodeCorpsWeb.ApiCase, resource_name: :project_github_repo + alias CodeCorps.{Analytics.SegmentTraitsBuilder, ProjectGithubRepo, Repo} + describe "index" do test "lists all entries on index", %{conn: conn} do [project_github_repo_1, project_github_repo_2] = insert_pair(:project_github_repo) @@ -51,6 +53,21 @@ defmodule CodeCorpsWeb.ProjectGithubRepoControllerTest do assert conn |> request_create(attrs) |> json_response(201) end + @tag :authenticated + test "is being tracked", %{conn: conn, current_user: current_user} do + project = insert(:project) + insert(:project_user, project: project, user: current_user, role: "owner") + github_repo = insert(:github_repo) + + attrs = %{project: project, github_repo: github_repo} + conn |> request_create(attrs) + + user_id = current_user.id + traits = ProjectGithubRepo |> Repo.one |> SegmentTraitsBuilder.build + + assert_receive({:track, ^user_id, "Connected GitHub Repo to Project", ^traits}) + end + @tag :authenticated test "renders 422 error when data is invalid", %{conn: conn, current_user: current_user} do project = insert(:project) @@ -79,6 +96,20 @@ defmodule CodeCorpsWeb.ProjectGithubRepoControllerTest do assert conn |> request_delete(project_github_repo) |> response(204) end + @tag :authenticated + test "is being tracked", %{conn: conn, current_user: current_user} do + project = insert(:project) + insert(:project_user, project: project, user: current_user, role: "owner") + project_github_repo = insert(:project_github_repo, project: project) + + conn |> request_delete(project_github_repo) + + user_id = current_user.id + traits = project_github_repo |> SegmentTraitsBuilder.build + + assert_receive({:track, ^user_id, "Disconnected GitHub Repo from Project", ^traits}) + end + test "renders 401 when unauthenticated", %{conn: conn} do assert conn |> request_delete |> json_response(401) end diff --git a/test/lib/code_corps_web/controllers/project_skill_controller_test.exs b/test/lib/code_corps_web/controllers/project_skill_controller_test.exs index 93397542e..725c4ffe9 100644 --- a/test/lib/code_corps_web/controllers/project_skill_controller_test.exs +++ b/test/lib/code_corps_web/controllers/project_skill_controller_test.exs @@ -1,6 +1,8 @@ defmodule CodeCorpsWeb.ProjectSkillControllerTest do use CodeCorpsWeb.ApiCase, resource_name: :project_skill + alias CodeCorps.{Analytics.SegmentTraitsBuilder, ProjectSkill, Repo} + describe "index" do test "lists all entries on index", %{conn: conn} do [project_skill_1, project_skill_2] = insert_pair(:project_skill) @@ -51,6 +53,21 @@ defmodule CodeCorpsWeb.ProjectSkillControllerTest do assert conn |> request_create(attrs) |> json_response(201) end + @tag :authenticated + test "tracks on segment", %{conn: conn, current_user: current_user} do + project = insert(:project) + insert(:project_user, project: project, user: current_user, role: "owner") + skill = insert(:skill) + + attrs = %{project: project, skill: skill} + conn |> request_create(attrs) + + user_id = current_user.id + traits = ProjectSkill |> Repo.one |> SegmentTraitsBuilder.build + + assert_received({:track, ^user_id, "Added Project Skill", ^traits}) + end + @tag :authenticated test "renders 422 error when data is invalid", %{conn: conn, current_user: current_user} do project = insert(:project) @@ -79,6 +96,20 @@ defmodule CodeCorpsWeb.ProjectSkillControllerTest do assert conn |> request_delete(project_skill) |> response(204) end + @tag :authenticated + test "tracks on segment", %{conn: conn, current_user: current_user} do + project = insert(:project) + insert(:project_user, project: project, user: current_user, role: "owner") + project_skill = insert(:project_skill, project: project) + + conn |> request_delete(project_skill) + + user_id = current_user.id + traits = project_skill |> SegmentTraitsBuilder.build + + assert_received({:track, ^user_id, "Removed Project Skill", ^traits}) + end + test "renders 401 when unauthenticated", %{conn: conn} do assert conn |> request_delete |> json_response(401) end diff --git a/test/lib/code_corps_web/controllers/task_controller_test.exs b/test/lib/code_corps_web/controllers/task_controller_test.exs index e871db5f4..14bd24d0c 100644 --- a/test/lib/code_corps_web/controllers/task_controller_test.exs +++ b/test/lib/code_corps_web/controllers/task_controller_test.exs @@ -1,6 +1,8 @@ defmodule CodeCorpsWeb.TaskControllerTest do use CodeCorpsWeb.ApiCase, resource_name: :task + alias CodeCorps.{Analytics.SegmentTraitsBuilder, Task} + @valid_attrs %{ title: "Test task", markdown: "A test task", @@ -120,13 +122,29 @@ defmodule CodeCorpsWeb.TaskControllerTest do assert json["data"]["attributes"]["number"] == 1 user_id = current_user.id - tracking_properties = %{ - task: @valid_attrs.title, - task_id: String.to_integer(json["data"]["id"]), - project_id: project.id + traits = Task |> Repo.one |> SegmentTraitsBuilder.build + assert_received {:track, ^user_id, "Created Task", ^traits} + end + + @tag :authenticated + test "tracks connecting to github", %{conn: conn, current_user: current_user} do + %{project: project, github_repo: github_repo} = + insert(:project_github_repo) + task_list = insert(:task_list, project: project) + assocs = %{ + project: project, + user: current_user, + task_list: task_list, + github_repo: github_repo } - assert_received {:track, ^user_id, "Created Task", ^tracking_properties} + attrs = @valid_attrs |> Map.merge(assocs) + + conn |> request_create(attrs) + + traits = Task |> Repo.one |> SegmentTraitsBuilder.build + user_id = current_user.id + assert_received {:track, ^user_id, "Connected Task to GitHub", ^traits} end @tag :authenticated @@ -148,13 +166,123 @@ defmodule CodeCorpsWeb.TaskControllerTest do assert conn |> request_update(task, @valid_attrs) |> json_response(200) user_id = current_user.id - tracking_properties = %{ - task: task.title, - task_id: task.id, - project_id: task.project.id - } + traits = Task |> Repo.get(task.id) |> SegmentTraitsBuilder.build + assert_received {:track, ^user_id, "Edited Task", ^traits} + end + + @tag :authenticated + test "tracks connecting to github", %{conn: conn, current_user: current_user} do + %{project: project, github_repo: github_repo} = insert(:project_github_repo) + task_list = insert(:task_list, project: project) + task = insert(:task, task_list: task_list, project: project, user: current_user) + + attrs = @valid_attrs |> Map.merge(%{github_repo_id: github_repo.id}) + conn |> request_update(task, attrs) + + traits = Task |> Repo.get(task.id) |> SegmentTraitsBuilder.build + user_id = current_user.id + assert_received {:track, ^user_id, "Connected Task to GitHub", ^traits} + end + + @tag :authenticated + test "does not track connecting to github if already connected", %{conn: conn, current_user: current_user} do + %{project: project, github_repo: github_repo} = insert(:project_github_repo) + task_list = insert(:task_list, project: project) + github_issue = insert(:github_issue, github_repo: github_repo) + task = insert(:task, task_list: task_list, project: project, user: current_user, github_repo: github_repo, github_issue: github_issue) + + attrs = @valid_attrs |> Map.merge(%{github_repo_id: github_repo.id}) + conn |> request_update(task, attrs) + + user_id = current_user.id + refute_received {:track, ^user_id, "Connected Task to GitHub", _} + end + + @tag :authenticated + test "tracks move between task lists", %{conn: conn, current_user: current_user} do + %{project: project} = task = insert(:task, user: current_user) + task_list = insert(:task_list, project: project) + + attrs = @valid_attrs |> Map.put(:task_list_id, task_list.id) + + conn |> request_update(task, attrs) + + traits = Task |> Repo.get(task.id) |> SegmentTraitsBuilder.build + user_id = current_user.id + assert_received {:track, ^user_id, "Moved Task Between Lists", ^traits} + end + + @tag :authenticated + test "does not track move between task lists if no move took place", %{conn: conn, current_user: current_user} do + task = insert(:task, user: current_user) - assert_received {:track, ^user_id, "Edited Task", ^tracking_properties} + conn |> request_update(task, @valid_attrs) + + user_id = current_user.id + refute_received {:track, ^user_id, "Moved Task Between Lists", _} + end + + @tag :authenticated + test "tracks title change", %{conn: conn, current_user: current_user} do + task = insert(:task, user: current_user) + attrs = @valid_attrs |> Map.put(:title, "New title") + conn |> request_update(task, attrs) + + traits = Task |> Repo.get(task.id) |> SegmentTraitsBuilder.build + user_id = current_user.id + assert_received {:track, ^user_id, "Edited Task Title", ^traits} + end + + @tag :authenticated + test "does not track title change if none took place", %{conn: conn, current_user: current_user} do + task = insert(:task, user: current_user) + attrs = @valid_attrs |> Map.delete(:title) + conn |> request_update(task, attrs) + + user_id = current_user.id + refute_received {:track, ^user_id, "Edited Task Title", _} + end + + @tag :authenticated + test "tracks closing task", %{conn: conn, current_user: current_user} do + task = insert(:task, user: current_user, status: "open") + attrs = @valid_attrs |> Map.put(:status, "closed") + conn |> request_update(task, attrs) + + traits = Task |> Repo.get(task.id) |> SegmentTraitsBuilder.build + user_id = current_user.id + assert_received {:track, ^user_id, "Closed Task", ^traits} + end + + @tag :authenticated + test "does not track closing task if no close took place", %{conn: conn, current_user: current_user} do + task = insert(:task, user: current_user, status: "open") + attrs = @valid_attrs |> Map.delete(:status) + conn |> request_update(task, attrs) + + user_id = current_user.id + refute_received {:track, ^user_id, "Closed Task", _} + end + + @tag :authenticated + test "tracks archiving task", %{conn: conn, current_user: current_user} do + task = insert(:task, user: current_user, archived: false) + attrs = @valid_attrs |> Map.put(:archived, true) + conn |> request_update(task, attrs) + + traits = Task |> Repo.get(task.id) |> SegmentTraitsBuilder.build + user_id = current_user.id + assert_received {:track, ^user_id, "Archived Task", ^traits} + end + + @tag :authenticated + test "does not track archiving task if no archive took place", %{conn: conn, current_user: current_user} do + task = insert(:task, user: current_user, archived: false) + attrs = @valid_attrs |> Map.delete(:archived) + conn |> request_update(task, attrs) + + user_id = current_user.id + refute_received {:track, ^user_id, "Archived Task", _} end @tag :authenticated diff --git a/test/lib/code_corps_web/controllers/task_skill_controller_test.exs b/test/lib/code_corps_web/controllers/task_skill_controller_test.exs index 99a2eb81a..8d4b0da5f 100644 --- a/test/lib/code_corps_web/controllers/task_skill_controller_test.exs +++ b/test/lib/code_corps_web/controllers/task_skill_controller_test.exs @@ -3,6 +3,8 @@ defmodule CodeCorpsWeb.TaskSkillControllerTest do use CodeCorpsWeb.ApiCase, resource_name: :task_skill + alias CodeCorps.{Repo, TaskSkill} + describe "index" do test "lists all entries on index", %{conn: conn} do [task_skill_1, task_skill_2] = insert_pair(:task_skill) @@ -52,6 +54,24 @@ defmodule CodeCorpsWeb.TaskSkillControllerTest do assert conn |> request_create(attrs) |> json_response(201) end + @tag :authenticated + test "tracks event when data is valid", %{conn: conn, current_user: current_user} do + task = insert(:task, user: current_user) + skill = insert(:skill) + user_id = current_user.id + + attrs = %{task: task, skill: skill} + json = conn |> request_create(attrs) |> json_response(201) + assert json + + expected_data = + TaskSkill + |> Repo.get(json["data"]["id"]) + |> CodeCorps.Analytics.SegmentTraitsBuilder.build + + assert_received {:track, ^user_id, "Added Task Skill", ^expected_data} + end + @tag :authenticated test "renders 422 error when data is invalid", %{conn: conn, current_user: current_user} do task = insert(:task, user: current_user) @@ -83,6 +103,21 @@ defmodule CodeCorpsWeb.TaskSkillControllerTest do assert conn |> request_delete(task_skill) |> response(204) end + @tag :authenticated + test "tracks event", %{conn: conn, current_user: current_user} do + task = insert(:task, user: current_user) + task_skill = insert(:task_skill, task: task) + user_id = current_user.id + expected_data = + TaskSkill + |> Repo.get(task_skill.id) + |> CodeCorps.Analytics.SegmentTraitsBuilder.build + + assert conn |> request_delete(task_skill) |> response(204) + + assert_received {:track, ^user_id, "Removed Task Skill", ^expected_data} + end + test "renders 401 when unauthenticated", %{conn: conn} do assert conn |> request_delete |> json_response(401) end diff --git a/test/lib/code_corps_web/controllers/user_controller_test.exs b/test/lib/code_corps_web/controllers/user_controller_test.exs index cc55547df..4cb0d8a6f 100644 --- a/test/lib/code_corps_web/controllers/user_controller_test.exs +++ b/test/lib/code_corps_web/controllers/user_controller_test.exs @@ -27,7 +27,6 @@ defmodule CodeCorpsWeb.UserControllerTest do @relationships %{} describe "index" do - test "lists all entries on index", %{conn: conn} do [user_1, user_2] = insert_pair(:user) diff --git a/test/lib/code_corps_web/controllers/user_task_controller_test.exs b/test/lib/code_corps_web/controllers/user_task_controller_test.exs index 7c81a98a8..e099b7070 100644 --- a/test/lib/code_corps_web/controllers/user_task_controller_test.exs +++ b/test/lib/code_corps_web/controllers/user_task_controller_test.exs @@ -3,7 +3,7 @@ defmodule CodeCorpsWeb.UserTaskControllerTest do use CodeCorpsWeb.ApiCase, resource_name: :user_task - alias CodeCorps.{Repo, UserTask} + alias CodeCorps.{Analytics.SegmentTraitsBuilder, Repo, UserTask} describe "index" do test "lists all entries on index", %{conn: conn} do @@ -54,6 +54,33 @@ defmodule CodeCorpsWeb.UserTaskControllerTest do assert conn |> request_create(attrs) |> json_response(201) end + @tag :authenticated + test "tracks when current user assigns task to self", %{conn: conn, current_user: current_user} do + task = insert(:task, user: current_user) + + attrs = %{task: task, user: current_user} + conn |> request_create(attrs) + + traits = UserTask |> Repo.one |> SegmentTraitsBuilder.build + user_id = current_user.id + + assert_received {:track, ^user_id, "Assigned Task to Self", ^traits} + end + + @tag :authenticated + test "tracks when current user assings task to someone else", %{conn: conn, current_user: current_user} do + task = insert(:task, user: current_user) + user = insert(:user) + + attrs = %{task: task, user: user} + conn |> request_create(attrs) + + traits = UserTask |> Repo.one |> SegmentTraitsBuilder.build + user_id = current_user.id + + assert_received {:track, ^user_id, "Assigned Task to Someone Else", ^traits} + end + @tag :authenticated test "renders 422 error when data is invalid", %{conn: conn, current_user: current_user} do task = insert(:task, user: current_user) @@ -79,6 +106,7 @@ defmodule CodeCorpsWeb.UserTaskControllerTest do describe "update" do @tag :authenticated test "updates chosen resource", %{conn: conn, current_user: current_user} do + # task owner or project contributor are the only ones who can re-assign task = insert(:task, user: current_user) user_task = insert(:user_task, task: task) new_user = insert(:user) @@ -89,6 +117,35 @@ defmodule CodeCorpsWeb.UserTaskControllerTest do assert updated_task.user_id == new_user.id end + @tag :authenticated + test "tracks when current user assigns task to self", %{conn: conn, current_user: current_user} do + # task owner or project contributor are the only ones who can re-assign + task = insert(:task, user: current_user) + user_task = insert(:user_task, task: task) + + conn |> request_update(user_task, %{user_id: current_user.id}) + + traits = UserTask |> Repo.one |> SegmentTraitsBuilder.build + user_id = current_user.id + + assert_received {:track, ^user_id, "Assigned Task to Self", ^traits} + end + + @tag :authenticated + test "tracks when current user assings task to someone else", %{conn: conn, current_user: current_user} do + # task owner or project contributor are the only ones who can re-assign + task = insert(:task, user: current_user) + user_task = insert(:user_task, task: task) + new_user = insert(:user) + + conn |> request_update(user_task, %{user_id: new_user.id}) + + traits = UserTask |> Repo.one |> SegmentTraitsBuilder.build + user_id = current_user.id + + assert_received {:track, ^user_id, "Assigned Task to Someone Else", ^traits} + end + test "renders 401 when unauthenticated", %{conn: conn} do user_task = insert(:user_task) new_user = insert(:user) @@ -111,6 +168,33 @@ defmodule CodeCorpsWeb.UserTaskControllerTest do assert conn |> request_delete(user_task) |> response(204) end + @tag :authenticated + test "tracks when current user unassigns task from self", %{conn: conn, current_user: current_user} do + # task owner or project contributor are the only ones who can unassign + task = insert(:task, user: current_user) + user_task = insert(:user_task, task: task, user: current_user) + + conn |> request_delete(user_task) + + traits = user_task |> SegmentTraitsBuilder.build + user_id = current_user.id + + assert_received {:track, ^user_id, "Unassigned Task from Self", ^traits} + end + + @tag :authenticated + test "tracks when current user unassings task from someone else", %{conn: conn, current_user: current_user} do + # task owner or project contributor are the only ones who can unassign + task = insert(:task, user: current_user) + user_task = insert(:user_task, task: task) + + conn |> request_delete(user_task) + + traits = user_task |> SegmentTraitsBuilder.build + user_id = current_user.id + + assert_received {:track, ^user_id, "Unassigned Task from Someone Else", ^traits} + end test "renders 401 when unauthenticated", %{conn: conn} do assert conn |> request_delete |> json_response(401) end diff --git a/test/support/factories.ex b/test/support/factories.ex index 919696f8a..ad7a0dd2f 100644 --- a/test/support/factories.ex +++ b/test/support/factories.ex @@ -84,7 +84,9 @@ defmodule CodeCorps.Factories do def github_repo_factory do %CodeCorps.GithubRepo{ - github_app_installation: build(:github_app_installation) + github_account_login: sequence(:github_account_login, &"owner_#{&1}"), + github_app_installation: build(:github_app_installation), + name: sequence(:name, &"repo_#{&1}") } end @@ -228,6 +230,8 @@ defmodule CodeCorps.Factories do def stripe_connect_charge_factory do %CodeCorps.StripeConnectCharge{ + amount: 1000, + currency: "usd", id_from_stripe: sequence(:id_from_stripe, &"stripe_id_#{&1}"), stripe_connect_account: build(:stripe_connect_account), stripe_connect_customer: build(:stripe_connect_customer),