Skip to content
Open
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
57 changes: 57 additions & 0 deletions app/models/github/bounty_intent.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
require 'bigdecimal'

class Github::BountyIntent
AMOUNT_PATTERN = /(?:^|\s)(?:\$\+|\+\$|\$)\s*(\d+(?:\.\d{1,2})?)(?=\s|$|[[:punct:]])/

attr_reader :amount

def initialize(amount)
@amount = BigDecimal(amount.to_s)
end

def self.extract(text)
text.to_s.scan(AMOUNT_PATTERN).flatten.map do |amount|
new(amount)
end.select(&:valid?)
end

def self.queue_from_comment(comment_data, issue:, actor:)
intents = extract(comment_data.try(:[], 'body'))
return [] if intents.empty? || issue.blank? || actor.try(:person).blank?

intents.map { |intent| intent.queue_for(actor.person, issue) }.compact
end

def valid?
amount > 0
end

def to_cart_item(issue)
{
item_type: 'Bounty',
amount: amount.to_s('F'),
currency: 'USD',
issue_id: issue.id,
tweet: false
}
end

def queue_for(person, issue)
cart = person.shopping_cart
attrs = to_cart_item(issue)
return nil if duplicate?(cart, attrs)

cart.add_item(attrs)
end

private

def duplicate?(cart, attrs)
cart.items.any? do |cart_item|
item = cart_item.with_indifferent_access
item[:item_type] == attrs[:item_type] &&
item[:issue_id].to_i == attrs[:issue_id].to_i &&
BigDecimal(item[:amount].to_s) == amount
end
end
end
1 change: 1 addition & 0 deletions app/models/github/event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def self.process_event(event)
when 'IssueCommentEvent'
issue = issue(event['payload']['issue'], tracker: repo)
comment(event['payload']['comment'], issue: issue)
Github::BountyIntent.queue_from_comment(event['payload']['comment'], issue: issue, actor: actor)

when 'PullRequestEvent'
# NOTE: we can NOT pass in the pull_request here because it doesn't have the right remote_id for issue
Expand Down
35 changes: 35 additions & 0 deletions spec/models/github/bounty_intent_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
require 'rails_helper'

RSpec.describe Github::BountyIntent do
describe '.extract' do
it 'extracts GitHub comment bounty amounts' do
amounts = described_class.extract('Please add $+20 to this issue and +$5 more.').map(&:amount)

expect(amounts).to eq([BigDecimal('20'), BigDecimal('5')])
end

it 'ignores comments without bounty amounts' do
expect(described_class.extract('Looks good to me')).to eq([])
end
end

describe '.queue_from_comment' do
let(:person) { create(:person) }
let(:actor) { create(:linked_account_github, person: person) }
let(:issue) { create(:issue) }

it 'queues a bounty in the commenter shopping cart' do
described_class.queue_from_comment({ 'body' => '$+10' }, issue: issue, actor: actor)

expect(person.shopping_cart.items).to include(
hash_including('item_type' => 'Bounty', 'amount' => '10.0', 'currency' => 'USD', 'issue_id' => issue.id)
)
end

it 'does not queue duplicate pending bounty intents' do
2.times { described_class.queue_from_comment({ 'body' => '$+10' }, issue: issue, actor: actor) }

expect(person.shopping_cart.items.count).to eq(1)
end
end
end