Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .iex.exs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ alias Algora.Admin
alias Algora.Admin.Migration
alias Algora.Analytics
alias Algora.Bounties
alias Algora.Bounties.Bounty
alias Algora.Bounties.Claim
alias Algora.Bounties.Tip
alias Algora.Contracts
alias Algora.Contracts.Contract
alias Algora.Contracts.Timesheet
Expand Down
37 changes: 37 additions & 0 deletions lib/algora/activities/discord_views.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,42 @@ defmodule Algora.Activities.DiscordViews do
}
end

def render(%{type: :transaction_succeeded, assoc: tx}) do
tx = Repo.preload(tx, [:user, linked_transaction: [:user]])

%{
embeds: [
%{
color: 0x6366F1,
title: "#{tx.net_amount} paid!",
author: %{
name: tx.linked_transaction.user.name,
icon_url: tx.linked_transaction.user.avatar_url,
url: "#{AlgoraWeb.Endpoint.url()}/org/#{tx.linked_transaction.user.handle}"
},
footer: %{
text: tx.user.name,
icon_url: tx.user.avatar_url
},
thumbnail: %{url: tx.user.avatar_url},
fields: [
%{
name: "Sender",
value: tx.linked_transaction.user.name,
inline: false
},
%{
name: "Recipient",
value: tx.user.name,
inline: false
}
],
url: "#{AlgoraWeb.Endpoint.url()}/org/#{tx.linked_transaction.user.handle}",
timestamp: tx.succeeded_at
}
]
}
end

def render(_activity), do: nil
end
4 changes: 1 addition & 3 deletions lib/algora/activities/schemas/activity.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@ defmodule Algora.Activities.Activity do
require Protocol

@activity_types ~w{
contract_paid
contract_prepaid
contract_created
contract_renewed
identity_created
bounty_awarded
bounty_posted
bounty_repriced
claim_submitted
claim_approved
tip_awarded
transaction_succeeded
}a

typed_schema "activities" do
Expand Down
93 changes: 53 additions & 40 deletions lib/algora/activities/views.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ defmodule Algora.Activities.Views do
@moduledoc false
alias Algora.Repo

require Logger

def render(%{type: type} = activity, template) when is_binary(type) do
render(%{activity | type: String.to_existing_atom(type)}, template)
end
Expand All @@ -18,31 +20,16 @@ defmodule Algora.Activities.Views do
"""
end

def render(%{type: :bounty_awarded, assoc: bounty}, :title) do
bounty = Repo.preload(bounty, :creator)
"🎉 #{bounty.amount} bounty awarded by #{bounty.creator.display_name}"
end

def render(%{type: :bounty_awarded, assoc: bounty} = activity, :txt) do
bounty = Repo.preload(bounty, :creator)

"""
Congratulations, you've been awarded a bounty by #{bounty.creator.display_name}!

#{Algora.Activities.external_url(activity)}
"""
end

def render(%{type: :bounty_posted, assoc: bounty}, :title) do
bounty = Repo.preload(bounty, :creator)
"#{bounty.amount} bounty posted by #{bounty.creator.display_name}"
"#{bounty.amount} bounty posted by #{bounty.creator.name}"
end

def render(%{type: :bounty_posted, assoc: bounty} = activity, :txt) do
bounty = Repo.preload(bounty, :creator)

"""
A new bounty has been posted by #{bounty.creator.display_name}
A new bounty has been posted by #{bounty.creator.name}

#{Algora.Activities.external_url(activity)}
"""
Expand Down Expand Up @@ -102,21 +89,7 @@ defmodule Algora.Activities.Views do
contract = Repo.preload(contract, [:client, :contractor])

"""
A contract between #{contract.client.display_name} and #{contract.contractor.display_name} has been created.

#{Algora.Activities.external_url(activity)}
"""
end

def render(%{type: :contract_paid, assoc: _contract}, :title) do
"A contract has been paid on Algora"
end

def render(%{type: :contract_paid, assoc: contract} = activity, :txt) do
contract = Repo.preload(contract, [:client, :contractor])

"""
A contract between "#{contract.client.display_name}" and "#{contract.contractor.display_name}" has been paid.
A contract between #{contract.client.name} and #{contract.contractor.name} has been created.

#{Algora.Activities.external_url(activity)}
"""
Expand All @@ -130,7 +103,7 @@ defmodule Algora.Activities.Views do
contract = Repo.preload(contract, :client)

"""
A contract for "#{contract.client.display_name}" has been prepaid.
A contract for "#{contract.client.name}" has been prepaid.

#{Algora.Activities.external_url(activity)}
"""
Expand All @@ -144,23 +117,63 @@ defmodule Algora.Activities.Views do
contract = Repo.preload(contract, [:client, :contractor])

"""
A contract between "#{contract.client.display_name}" and "#{contract.contractor.display_name}" has been renewed.
A contract between "#{contract.client.name}" and "#{contract.contractor.name}" has been renewed.

#{Algora.Activities.external_url(activity)}
"""
end

def render(%{type: :tip_awarded, assoc: _tip}, :title) do
"You were awarded a tip on Algora"
def render(%{type: :transaction_succeeded, assoc: tx} = activity, template) do
tx = Repo.preload(tx, [:user, linked_transaction: [:user]])
activity = %{activity | assoc: tx}

case tx do
%{linked_transaction: nil} ->
Logger.error("Unknown transaction type: #{inspect(tx)}")
raise "Unknown transaction type: #{inspect(tx)}"

%{bounty_id: bounty_id} when not is_nil(bounty_id) ->
render_transaction_succeeded(activity, template, :bounty)

%{tip_id: tip_id} when not is_nil(tip_id) ->
render_transaction_succeeded(activity, template, :tip)

%{contract_id: contract_id} when not is_nil(contract_id) ->
render_transaction_succeeded(activity, template, :contract)

_ ->
Logger.error("Unknown transaction type: #{inspect(tx)}")
raise "Unknown transaction type: #{inspect(tx)}"
end
end

defp render_transaction_succeeded(%{assoc: tx}, :title, :bounty) do
"🎉 #{tx.net_amount} bounty awarded by #{tx.linked_transaction.user.name}"
end

defp render_transaction_succeeded(%{assoc: tx}, :title, :tip) do
"💸 #{tx.net_amount} tip received from #{tx.linked_transaction.user.name}"
end

def render(%{type: :tip_awarded, assoc: tip} = activity, :txt) do
tip = Repo.preload(tip, :creator)
defp render_transaction_succeeded(%{assoc: tx}, :title, :contract) do
"💰 #{tx.net_amount} contract paid by #{tx.linked_transaction.user.name}"
end

defp render_transaction_succeeded(%{assoc: tx}, :txt, :bounty) do
"""
Congratulations, you've been awarded a #{tx.net_amount} bounty by #{tx.linked_transaction.user.name}!
"""
#{tip.creator.display_name} sent you a #{tip.amount} tip on Algora!
end

#{Algora.Activities.external_url(activity)}
defp render_transaction_succeeded(%{assoc: tx}, :txt, :tip) do
"""
Congratulations, you've been awarded a #{tx.net_amount} tip by #{tx.linked_transaction.user.name}!
"""
end

defp render_transaction_succeeded(%{assoc: tx}, :txt, :contract) do
"""
Congratulations, you've been awarded a #{tx.net_amount} contract by #{tx.linked_transaction.user.name}!
"""
end
end
23 changes: 7 additions & 16 deletions lib/algora/bounties/bounties.ex
Original file line number Diff line number Diff line change
Expand Up @@ -678,10 +678,7 @@ defmodule Algora.Bounties do
recipient_id: recipient.id,
ticket_id: if(ticket, do: ticket.id)
})
|> Repo.insert_with_activity(%{
type: :tip_awarded,
notify_users: []
})
|> Repo.insert()
end
end

Expand All @@ -696,18 +693,12 @@ defmodule Algora.Bounties do
) ::
{:ok, String.t()} | {:error, atom()}
def reward_bounty(%{owner: owner, amount: amount, bounty_id: bounty_id, claims: claims}, opts \\ []) do
Repo.transact(fn ->
activity_attrs = %{type: :bounty_awarded}

with {:ok, _activity} <- Algora.Activities.insert(%Bounty{id: bounty_id}, activity_attrs) do
create_payment_session(
%{owner: owner, amount: amount, description: "Bounty payment for OSS contributions"},
ticket_ref: opts[:ticket_ref],
bounty_id: bounty_id,
claims: claims
)
end
end)
create_payment_session(
%{owner: owner, amount: amount, description: "Bounty payment for OSS contributions"},
ticket_ref: opts[:ticket_ref],
bounty_id: bounty_id,
claims: claims
)
end

@spec generate_line_items(
Expand Down
7 changes: 1 addition & 6 deletions lib/algora/contracts/contracts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -539,12 +539,7 @@ defmodule Algora.Contracts do
end

defp mark_contract_as_paid(contract) do
contract
|> change(%{status: :paid})
|> Repo.update_with_activity(%{
type: :contract_paid,
notify_users: []
})
change(contract, %{status: :paid})
end

defp renew_contract(contract) do
Expand Down
16 changes: 14 additions & 2 deletions lib/algora/repo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ defmodule Algora.Repo do
otp_app: :algora,
adapter: Ecto.Adapters.Postgres

alias Algora.Activities.Activity
alias Algora.Activities.Notifier

require Ecto.Query

@spec fetch_one(Ecto.Queryable.t(), Keyword.t()) ::
Expand Down Expand Up @@ -105,13 +108,22 @@ defmodule Algora.Repo do
def with_activity(multi, activity) do
multi
|> Ecto.Multi.insert(:activity, fn %{target: target} ->
Algora.Activities.Activity.build_activity(target, Map.put(activity, :id, target.id))
Activity.build_activity(target, Map.put(activity, :id, target.id))
end)
|> Oban.insert(:notification, fn %{activity: activity, target: target} ->
Algora.Activities.Notifier.changeset(activity, target)
Notifier.changeset(activity, target)
end)
end

def insert_activity(target, activity) do
activity = Map.put(activity, :id, target.id)

with {:ok, activity} <- Algora.Repo.insert(Activity.build_activity(target, activity)),
{:ok, notification} <- Oban.insert(Notifier.changeset(activity, target)) do
{:ok, %{activity: activity, notification: notification}}
end
end

defp extract_target(response) do
case response do
{:ok, %{target: target}} ->
Expand Down
11 changes: 11 additions & 0 deletions lib/algora_web/controllers/webhooks/stripe_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ defmodule AlgoraWeb.Webhooks.StripeController do
Repo.update_all(from(t in Tip, where: t.id in ^tip_ids), set: [status: :paid])
Repo.update_all(from(c in Contract, where: c.id in ^contract_ids), set: [status: :paid])

activities_result =
txs
|> Enum.filter(&(&1.type == :credit))
|> Enum.reduce_while(:ok, fn tx, :ok ->
case Repo.insert_activity(tx, %{type: :transaction_succeeded, notify_users: [tx.user_id]}) do
{:ok, _} -> {:cont, :ok}
error -> {:halt, error}
end
end)

jobs_result =
txs
|> Enum.filter(&(&1.type == :credit))
Expand All @@ -90,6 +100,7 @@ defmodule AlgoraWeb.Webhooks.StripeController do
end)

with txs when txs != [] <- txs,
:ok <- activities_result,
:ok <- jobs_result do
Payments.broadcast()
{:ok, nil}
Expand Down
15 changes: 5 additions & 10 deletions test/algora/bounties_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,11 @@ defmodule Algora.BountiesTest do
claims: claims
)

assert_activity_names([:bounty_posted, :claim_submitted, :bounty_awarded, :tip_awarded])
assert_activity_names_for_user(creator.id, [:bounty_posted, :bounty_awarded, :tip_awarded])
assert_activity_names_for_user(recipient.id, [:claim_submitted, :tip_awarded])

assert [bounty, _claim, _awarded, tip] = Enum.reverse(Algora.Activities.all())
assert "tip_activities" == tip.assoc_name
# assert tip.notify_users == [recipient.id]
assert activity = Algora.Activities.get_with_preloaded_assoc(tip.assoc_name, tip.id)
assert activity.assoc.__meta__.schema == Tip
assert activity.assoc.creator.id == creator.id
assert_activity_names([:bounty_posted, :claim_submitted])
assert_activity_names_for_user(creator.id, [:bounty_posted])
assert_activity_names_for_user(recipient.id, [:claim_submitted])

assert [bounty, _claim] = Enum.reverse(Algora.Activities.all())

assert_enqueued(worker: Notifier, args: %{"activity_id" => bounty.id})
refute_enqueued(worker: SendEmail, args: %{"activity_id" => bounty.id})
Expand Down
10 changes: 2 additions & 8 deletions test/algora/contracts_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -160,26 +160,22 @@ defmodule Algora.ContractsTest do

assert_activity_names(
contract_a_0,
[:contract_prepaid, :contract_paid]
[:contract_prepaid]
)

assert_activity_names(
contract_a_1,
[:contract_renewed, :contract_paid]
[:contract_renewed]
)

assert_activity_names(
"contract_activities",
[
:contract_prepaid,
:contract_paid,
:contract_renewed,
:contract_paid,
:contract_renewed,
:contract_prepaid,
:contract_paid,
:contract_renewed,
:contract_paid,
:contract_renewed
]
)
Expand All @@ -188,9 +184,7 @@ defmodule Algora.ContractsTest do
contract_a_0.contractor_id,
[
:contract_prepaid,
:contract_paid,
:contract_renewed,
:contract_paid,
:contract_renewed
]
)
Expand Down
Loading